fix(ci/arch): the release-rebuild prune called a helper that cannot exist there
apple / swift (pull_request) Successful in 1m32s
apple / screenshots (pull_request) Skipped
ci / rust-arm64 (pull_request) Failing after 1m51s
ci / web (pull_request) Successful in 1m25s
ci / bun-nix (pull_request) Successful in 52s
ci / docs-site (pull_request) Successful in 1m53s
android / android (pull_request) Successful in 6m47s
ci / rust (pull_request) Successful in 28m33s

The v0.25.0 rebuild published perfectly — registry has punktfunk-host 0.25.0-2 with
libavcodec.so=63-64, and it resolves on a real ffmpeg-9 box — then failed its last
step with

    prune_release_assets: command not found

`. scripts/ci/gitea-release.sh` sources from the CHECKED-OUT TREE, and a release
rebuild checks out the OLD TAG. So the step could only ever see the helpers that
existed when that tag was cut, and the prune is gated on exactly that path: the
helper was guaranteed absent in the only case that calls it. Adding it to a shared
script made it look available at review time while being unreachable at run time.

Only the workflow file is read from the dispatched ref, so the logic moves there,
inline. Same reasoning documented at both ends, including the corollary worth knowing
before the next rebuild: a PKGBUILD fix made after a tag does NOT reach a rebuild of
that tag either — the packaging comes from the tag too.

Verified by executing the one-liner's exact bytes out of arch.yml under /bin/sh (the
shell Gitea actually uses): keeps the new -2 set and gamescope, drops the superseded
-1 packages and their .sha256 sidecars, leaves other legs' .dmg/.deb untouched. The
`'\n'` survives the shell quoting, which was the part worth proving.

Also drops the now-dead helper from gitea-release.sh rather than leaving a function
no caller can reach, and leaves a warning there against the next one.
This commit is contained in:
2026-08-08 10:57:49 +02:00
parent 1ef212a78d
commit 8f1c34c6bf
2 changed files with 26 additions and 40 deletions
+5 -38
View File
@@ -38,18 +38,11 @@ for a in json.load(sys.stdin):
print(a.get("id",""));break' "$1" 2>/dev/null
}
_urlencode() { python3 -c 'import urllib.parse,sys;print(urllib.parse.quote(sys.argv[1],safe=""))' "$1"; }
# _json_stale_asset_ids SUFFIX KEEP_NAMES (assets JSON on stdin) -> "<id> <name>" lines
# The assets matching SUFFIX (or its .sha256 sidecar) that are NOT in the whitespace-separated
# KEEP_NAMES. See prune_release_assets.
_json_stale_asset_ids() {
python3 -c 'import json,sys
suffix, keep = sys.argv[1], set(sys.argv[2].split())
keep |= {n + ".sha256" for n in keep}
for a in json.load(sys.stdin):
n = a.get("name", "")
if n.endswith((suffix, suffix + ".sha256")) and n not in keep:
print(a.get("id", ""), n)' "$1" "$2" 2>/dev/null
}
# ⚠ Do NOT add helpers here for a workflow step that runs against a CHECKED-OUT RELEASE TAG
# (arch.yml's release-rebuild dispatch). Callers source this file from the working tree, so such a
# step gets the version of this file that shipped in that tag — never the one you just wrote. That
# logic belongs in the workflow, which is always read from the dispatched ref. Cost this once
# already: `prune_release_assets: command not found`, after the packages published fine.
# _release_notes_path TAG
# Print the path of the in-repo release notes for TAG (docs/releases/<TAG>.md) IFF it exists,
@@ -177,32 +170,6 @@ upsert_asset() {
if _put_asset "$rid" "$sums" "$name.sha256"; then rm -f "$sums"; else rm -f "$sums"; return 1; fi
}
# prune_release_assets RELEASE_ID SUFFIX KEEP_NAMES
# Delete every asset of the release whose name ends in SUFFIX (or SUFFIX.sha256) and is not one
# of KEEP_NAMES (whitespace-separated).
#
# WHY: upsert_asset replaces an asset BY NAME, which is idempotent only while the filename is
# stable. A REBUILD of an already-published release is exactly the case where it is not — a
# distro moved under the release, the artifact is rebuilt at a higher pkgrel, and
# `punktfunk-host-0.25.0-2-x86_64.pkg.tar.zst` collides with nothing, so the -1 build stays
# attached. A superseded package on a release page is not clutter; it is a live download of the
# very build the rebuild exists to replace. Scoped by SUFFIX because a release object is shared
# by ~8 packaging workflows running concurrently — each leg may only ever prune names it owns.
prune_release_assets() {
local rid="${1:?release id}" suffix="${2:?suffix}" keep="${3:-}"
local api
# An empty keep list means "delete every asset matching SUFFIX", which is never what a caller
# wants and is exactly what a mis-expanded glob looks like. Refuse rather than clear a release.
[ -n "$keep" ] || { echo "gitea-release: prune_release_assets got an empty keep list — refusing" >&2; return 0; }
api="$(_gitea_api)"
curl -fsS "$api/releases/$rid/assets" -H "Authorization: token ${GITEA_TOKEN:?}" \
| _json_stale_asset_ids "$suffix" "$keep" \
| while read -r id name; do
echo "gitea-release: dropping superseded asset '$name'"
curl -fsS -o /dev/null -X DELETE "$api/releases/$rid/assets/$id" \
-H "Authorization: token ${GITEA_TOKEN:?}" || true
done
}
# apply_release_notes RELEASE_ID TAG
# Force the release body to match docs/releases/<TAG>.md (the source of truth), if that file