ci: registry-gc — read branches with the Actions token, not PACKAGE_TOKEN
The dry-run failed with HTTP 403 on the branches API: PACKAGE_TOKEN is a package-scoped token (registry push/delete) and can't read repo branches. Split by capability: branch reads now use the automatic Actions token (github.token, repo read via 'permissions: contents: read'); package list + delete keep PACKAGE_TOKEN. Also replaced the bare 'curl -f' failures with a get() helper that reports the HTTP code + a token-scope hint, so an auth problem can't look like 'no results' (which would otherwise skew the reconcile).
This commit is contained in:
@@ -23,7 +23,9 @@
|
|||||||
# repo variable REGISTRY_GC_ENABLE=true — so you can eyeball a scheduled dry-run
|
# repo variable REGISTRY_GC_ENABLE=true — so you can eyeball a scheduled dry-run
|
||||||
# first, then flip the variable to let it prune automatically.
|
# first, then flip the variable to let it prune automatically.
|
||||||
#
|
#
|
||||||
# Needs the existing PACKAGE_TOKEN secret (write:package scope, which the
|
# TOKENS (two, by capability — a package token can't read branches, hence the
|
||||||
|
# original 403): branch reads use the automatic Actions token (github.token,
|
||||||
|
# repo read); package list + delete use PACKAGE_TOKEN (write:package, which the
|
||||||
# registry-push token already has). Only touches images THIS repo builds; the
|
# registry-push token already has). Only touches images THIS repo builds; the
|
||||||
# fleet-kiosk image is owned by the kiosk-app submodule's own CI.
|
# fleet-kiosk image is owned by the kiosk-app submodule's own CI.
|
||||||
|
|
||||||
@@ -40,6 +42,9 @@ on:
|
|||||||
type: boolean
|
type: boolean
|
||||||
default: true
|
default: true
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read # branch listing via the auto Actions token
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: registry-gc
|
group: registry-gc
|
||||||
cancel-in-progress: false
|
cancel-in-progress: false
|
||||||
@@ -53,7 +58,8 @@ jobs:
|
|||||||
SERVER: "https://git.bitasylum.org"
|
SERVER: "https://git.bitasylum.org"
|
||||||
OWNER: ${{ github.repository_owner }}
|
OWNER: ${{ github.repository_owner }}
|
||||||
REPO: ${{ github.repository }}
|
REPO: ${{ github.repository }}
|
||||||
TOKEN: ${{ secrets.PACKAGE_TOKEN }}
|
REPO_TOKEN: ${{ github.token }} # auto Actions token: repo/branch reads
|
||||||
|
PKG_TOKEN: ${{ secrets.PACKAGE_TOKEN }} # package list + delete (write:package)
|
||||||
EVENT: ${{ github.event_name }}
|
EVENT: ${{ github.event_name }}
|
||||||
DISPATCH_DRY: ${{ inputs.dry_run }}
|
DISPATCH_DRY: ${{ inputs.dry_run }}
|
||||||
GC_ENABLE: ${{ vars.REGISTRY_GC_ENABLE }}
|
GC_ENABLE: ${{ vars.REGISTRY_GC_ENABLE }}
|
||||||
@@ -69,7 +75,27 @@ jobs:
|
|||||||
command -v jq >/dev/null 2>&1 || { sudo apt-get update -qq && sudo apt-get install -y -qq jq; }
|
command -v jq >/dev/null 2>&1 || { sudo apt-get update -qq && sudo apt-get install -y -qq jq; }
|
||||||
|
|
||||||
API="${SERVER}/api/v1"
|
API="${SERVER}/api/v1"
|
||||||
auth=(-H "Authorization: token ${TOKEN}" -H "Accept: application/json")
|
|
||||||
|
# Authenticated GET. $1 = which token (repo|pkg), $2 = url. Echoes the
|
||||||
|
# body on 2xx; on anything else prints an actionable error and exits
|
||||||
|
# (so a scope/auth problem can never be mistaken for "no results").
|
||||||
|
get() {
|
||||||
|
local who="$1" url="$2" tok tmp code body
|
||||||
|
case "$who" in repo) tok="$REPO_TOKEN";; pkg) tok="$PKG_TOKEN";; esac
|
||||||
|
tmp="$(mktemp)"
|
||||||
|
code="$(curl -sS --retry 3 -o "$tmp" -w '%{http_code}' \
|
||||||
|
-H "Authorization: token ${tok}" -H "Accept: application/json" "$url" || echo 000)"
|
||||||
|
body="$(cat "$tmp")"; rm -f "$tmp"
|
||||||
|
case "$code" in
|
||||||
|
2*) printf '%s' "$body"; return 0 ;;
|
||||||
|
401|403)
|
||||||
|
echo "::error::GET ${url} -> HTTP ${code} (token '${who}' lacks scope). branches need repo read (github.token); packages need read/write:package (PACKAGE_TOKEN)." >&2
|
||||||
|
exit 1 ;;
|
||||||
|
*)
|
||||||
|
echo "::error::GET ${url} -> HTTP ${code}" >&2
|
||||||
|
exit 1 ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
# ---- dry-run resolution ------------------------------------------
|
# ---- dry-run resolution ------------------------------------------
|
||||||
if [ "$EVENT" = "workflow_dispatch" ]; then
|
if [ "$EVENT" = "workflow_dispatch" ]; then
|
||||||
@@ -91,7 +117,7 @@ jobs:
|
|||||||
live=" main " # main is always protected, even if listing is odd
|
live=" main " # main is always protected, even if listing is odd
|
||||||
total=0 page=1
|
total=0 page=1
|
||||||
while :; do
|
while :; do
|
||||||
resp="$(curl -fsSL --retry 3 "${auth[@]}" "${API}/repos/${REPO}/branches?limit=50&page=${page}")"
|
resp="$(get repo "${API}/repos/${REPO}/branches?limit=50&page=${page}")"
|
||||||
cnt="$(printf '%s' "$resp" | jq 'length')"
|
cnt="$(printf '%s' "$resp" | jq 'length')"
|
||||||
[ "$cnt" -eq 0 ] && break
|
[ "$cnt" -eq 0 ] && break
|
||||||
while IFS= read -r b; do
|
while IFS= read -r b; do
|
||||||
@@ -112,7 +138,7 @@ jobs:
|
|||||||
for img in $IMAGES; do
|
for img in $IMAGES; do
|
||||||
page=1
|
page=1
|
||||||
while :; do
|
while :; do
|
||||||
resp="$(curl -fsSL --retry 3 "${auth[@]}" "${API}/packages/${OWNER}?type=container&q=${img}&limit=50&page=${page}")"
|
resp="$(get pkg "${API}/packages/${OWNER}?type=container&q=${img}&limit=50&page=${page}")"
|
||||||
cnt="$(printf '%s' "$resp" | jq 'length')"
|
cnt="$(printf '%s' "$resp" | jq 'length')"
|
||||||
[ "$cnt" -eq 0 ] && break
|
[ "$cnt" -eq 0 ] && break
|
||||||
while IFS= read -r ver; do
|
while IFS= read -r ver; do
|
||||||
@@ -143,7 +169,9 @@ jobs:
|
|||||||
orphans=$((orphans + 1))
|
orphans=$((orphans + 1))
|
||||||
echo "ORPHAN ${img}:${ver}"
|
echo "ORPHAN ${img}:${ver}"
|
||||||
if [ "$DRY" != "true" ]; then
|
if [ "$DRY" != "true" ]; then
|
||||||
code="$(curl -sS -o /dev/null -w '%{http_code}' -X DELETE "${auth[@]}" "${API}/packages/${OWNER}/container/${img}/${ver}" || echo 000)"
|
code="$(curl -sS -o /dev/null -w '%{http_code}' -X DELETE \
|
||||||
|
-H "Authorization: token ${PKG_TOKEN}" \
|
||||||
|
"${API}/packages/${OWNER}/container/${img}/${ver}" || echo 000)"
|
||||||
case "$code" in
|
case "$code" in
|
||||||
2*) deleted=$((deleted + 1)); echo " deleted ${img}:${ver}" ;;
|
2*) deleted=$((deleted + 1)); echo " deleted ${img}:${ver}" ;;
|
||||||
*) failed=$((failed + 1)); echo "::warning::delete ${img}:${ver} returned HTTP ${code}" ;;
|
*) failed=$((failed + 1)); echo "::warning::delete ${img}:${ver} returned HTTP ${code}" ;;
|
||||||
|
|||||||
Reference in New Issue
Block a user