ci: survive a revoked GITEA_BUILD_TOKEN on public commit reads
Follow-up to a2f0a4a, which documented the hazard; this removes it.
resolve-versions read three PUBLIC Gitea repos with `curl -sf -H "$AUTH_HEADER"`.
Gitea rejects an invalid token rather than ignoring it, so the token turned a
read that works anonymously into a hard failure:
no Authorization header 200
empty token (secret unset) 200 <- absent secret was always safe
garbage/revoked token 401 <- stale secret broke the release
A revoked GITEA_BUILD_TOKEN therefore failed resolve-versions via require_sha,
presenting as connectivity or an API fault, on data any anonymous client could
fetch. Hit exactly that failure mode today with an expired PAT.
New gitea_sha() helper tries authed, and on 401/403 retries anonymously with a
loud stderr warning naming the token as the cause. Deliberate choices:
- non-200 after the retry emits nothing and returns 0, so require_sha still
raises the explicit abort — the helper never invents a fallback ref, which is
the property the surrounding code exists to guarantee
- warnings go to stderr, NOT as ::warning:: annotations: the function's stdout
IS the SHA, so an annotation there would be captured into the ref
- the header is still sent first, so a private repo keeps working
Verified by extracting the function from the YAML step body (so the test ran the
committed text, not a copy) and calling it against live Gitea:
valid token -> 0e1369e6b496 (pi-toolkit)
REVOKED token -> warns, retries anon, f60cf9c73205 (mempalace-toolkit)
unset secret -> 98eb07bce60a (pi-extensions)
nonexistent repo -> empty + HTTP 404 warning, so require_sha aborts
Behaviour unchanged on the happy path: all three SHAs are byte-identical to the
ones the v1.8.1 release run resolved with the old curl code. `bash -n` clean on
the extracted step body; YAML re-parsed.
This commit is contained in:
@@ -177,6 +177,40 @@ jobs:
|
|||||||
fi
|
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` ───────────
|
# ── pi version: from the PIN, not from npm `latest` ───────────
|
||||||
# Until v1.7.0 this followed npm `latest`, which meant every release
|
# Until v1.7.0 this followed npm `latest`, which meant every release
|
||||||
# silently adopted whatever pi had shipped that morning — unaudited —
|
# 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
|
# 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
|
# step ever fails on a repo you can browse anonymously, suspect the
|
||||||
# token before you suspect Gitea.
|
# token before you suspect Gitea.
|
||||||
TOOLKIT_REF=$(curl -sf -H "$AUTH_HEADER" \
|
TOOLKIT_REF=$(gitea_sha pi-toolkit)
|
||||||
"https://gitea.jordbo.se/api/v1/repos/joakimp/pi-toolkit/commits?limit=1&sha=main" \
|
|
||||||
| jq -r '.[0].sha // empty' 2>/dev/null || true)
|
|
||||||
require_sha PI_TOOLKIT_REF "$TOOLKIT_REF"
|
require_sha PI_TOOLKIT_REF "$TOOLKIT_REF"
|
||||||
EXTENSIONS_REF=$(curl -sf -H "$AUTH_HEADER" \
|
EXTENSIONS_REF=$(gitea_sha pi-extensions)
|
||||||
"https://gitea.jordbo.se/api/v1/repos/joakimp/pi-extensions/commits?limit=1&sha=main" \
|
|
||||||
| jq -r '.[0].sha // empty' 2>/dev/null || true)
|
|
||||||
require_sha PI_EXTENSIONS_REF "$EXTENSIONS_REF"
|
require_sha PI_EXTENSIONS_REF "$EXTENSIONS_REF"
|
||||||
echo "toolkit_ref=${TOOLKIT_REF}" >> "$GITHUB_OUTPUT"
|
echo "toolkit_ref=${TOOLKIT_REF}" >> "$GITHUB_OUTPUT"
|
||||||
echo "extensions_ref=${EXTENSIONS_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
|
# into the base-decide hash (see that job) to force a base rebuild
|
||||||
# when the toolkit moves — otherwise a toolkit-only fix silently
|
# when the toolkit moves — otherwise a toolkit-only fix silently
|
||||||
# fails to land unless Dockerfile.base itself changes.
|
# fails to land unless Dockerfile.base itself changes.
|
||||||
MEMPALACE_TOOLKIT_REF=$(curl -sf -H "$AUTH_HEADER" \
|
MEMPALACE_TOOLKIT_REF=$(gitea_sha mempalace-toolkit)
|
||||||
"https://gitea.jordbo.se/api/v1/repos/joakimp/mempalace-toolkit/commits?limit=1&sha=main" \
|
|
||||||
| jq -r '.[0].sha // empty' 2>/dev/null || true)
|
|
||||||
require_sha MEMPALACE_TOOLKIT_REF "$MEMPALACE_TOOLKIT_REF"
|
require_sha MEMPALACE_TOOLKIT_REF "$MEMPALACE_TOOLKIT_REF"
|
||||||
echo "mempalace_toolkit_ref=${MEMPALACE_TOOLKIT_REF}" >> "$GITHUB_OUTPUT"
|
echo "mempalace_toolkit_ref=${MEMPALACE_TOOLKIT_REF}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user