diff --git a/.gitea/workflows/docker-publish.yml b/.gitea/workflows/docker-publish.yml index d064ec7..281b383 100644 --- a/.gitea/workflows/docker-publish.yml +++ b/.gitea/workflows/docker-publish.yml @@ -177,6 +177,40 @@ jobs: fi } + # Read a commit SHA from Gitea, surviving a bad build token. + # + # These repos are public (see the note at the call sites), so auth is + # a convenience, not a requirement — but Gitea REJECTS an invalid + # token (401) rather than ignoring it, so a revoked or malformed + # GITEA_BUILD_TOKEN could fail an entire release on reads that work + # fine anonymously. An ABSENT secret was always safe (Gitea ignores an + # empty `token ` value and serves the request, 200); a STALE one was + # not. So: try authed, and on 401/403 retry anonymously. + # + # A non-200 after that emits nothing and returns 0 deliberately, so + # require_sha raises the loud explicit abort rather than this helper + # inventing a fallback ref. + # + # Messages go to STDERR, not as ::warning:: annotations: this + # function's stdout IS the SHA, so anything written there would be + # captured into the ref by the command substitution. + gitea_sha() { # $1=repo + local repo="$1" url resp code + url="https://gitea.jordbo.se/api/v1/repos/joakimp/${repo}/commits?limit=1&sha=main" + resp=$(curl -s -w '\n%{http_code}' -H "$AUTH_HEADER" "$url" || printf '\n000') + code=${resp##*$'\n'} + if [ "$code" = "401" ] || [ "$code" = "403" ]; then + printf 'WARNING: Gitea rejected the build token for %s (HTTP %s); retrying anonymously. The read should succeed (public repo), but GITEA_BUILD_TOKEN is stale or malformed and should be rotated.\n' "$repo" "$code" >&2 + resp=$(curl -s -w '\n%{http_code}' "$url" || printf '\n000') + code=${resp##*$'\n'} + fi + if [ "$code" != "200" ]; then + printf 'WARNING: Gitea commit lookup for %s returned HTTP %s\n' "$repo" "$code" >&2 + return 0 + fi + printf '%s' "${resp%$'\n'*}" | jq -r '.[0].sha // empty' 2>/dev/null || true + } + # ── pi version: from the PIN, not from npm `latest` ─────────── # Until v1.7.0 this followed npm `latest`, which meant every release # silently adopted whatever pi had shipped that morning — unaudited — @@ -256,13 +290,9 @@ jobs: # require_sha failure that reads like an API or network fault. If this # step ever fails on a repo you can browse anonymously, suspect the # token before you suspect Gitea. - TOOLKIT_REF=$(curl -sf -H "$AUTH_HEADER" \ - "https://gitea.jordbo.se/api/v1/repos/joakimp/pi-toolkit/commits?limit=1&sha=main" \ - | jq -r '.[0].sha // empty' 2>/dev/null || true) + TOOLKIT_REF=$(gitea_sha pi-toolkit) require_sha PI_TOOLKIT_REF "$TOOLKIT_REF" - EXTENSIONS_REF=$(curl -sf -H "$AUTH_HEADER" \ - "https://gitea.jordbo.se/api/v1/repos/joakimp/pi-extensions/commits?limit=1&sha=main" \ - | jq -r '.[0].sha // empty' 2>/dev/null || true) + EXTENSIONS_REF=$(gitea_sha pi-extensions) require_sha PI_EXTENSIONS_REF "$EXTENSIONS_REF" echo "toolkit_ref=${TOOLKIT_REF}" >> "$GITHUB_OUTPUT" echo "extensions_ref=${EXTENSIONS_REF}" >> "$GITHUB_OUTPUT" @@ -272,9 +302,7 @@ jobs: # into the base-decide hash (see that job) to force a base rebuild # when the toolkit moves — otherwise a toolkit-only fix silently # fails to land unless Dockerfile.base itself changes. - MEMPALACE_TOOLKIT_REF=$(curl -sf -H "$AUTH_HEADER" \ - "https://gitea.jordbo.se/api/v1/repos/joakimp/mempalace-toolkit/commits?limit=1&sha=main" \ - | jq -r '.[0].sha // empty' 2>/dev/null || true) + MEMPALACE_TOOLKIT_REF=$(gitea_sha mempalace-toolkit) require_sha MEMPALACE_TOOLKIT_REF "$MEMPALACE_TOOLKIT_REF" echo "mempalace_toolkit_ref=${MEMPALACE_TOOLKIT_REF}" >> "$GITHUB_OUTPUT"