#!/usr/bin/env bash # Delete the binary assets of every release except the newest KEEP ones. # # Gitea has no asset retention, so 9 assets x ~57 MB per tag pile up forever on # the host disk (sow-tools #52). Nothing consumes an old asset: CI takes the # Crucible binary from the Nix flake, and every deleted file is reproducible # from its tag. Tags, source archives, release notes and the releases # themselves are never touched — only attachments. # # Nothing is excluded, SHA256SUMS and the wrappers included: the checksums are # only meaningful next to the binaries they cover, and the drift-check reads # the wrappers from the latest release, which always stays complete. # # Best-effort by design: a stale asset is cheaper than a blocked release, so # every API failure is a warning, never a non-zero exit. # # Env: # API repo API base, e.g. https://git.example/api/v1/repos/owner/repo [required] # TOKEN Gitea token with releases:write [required] # KEEP how many newest releases stay complete (default 2) set -uo pipefail : "${API:?set API}" : "${TOKEN:?set TOKEN}" keep="${KEEP:-2}" auth="Authorization: token ${TOKEN}" per_page=50 warn() { echo "prune-release-assets: $*" >&2; } # Walk every page so an old backlog is cleared, not only the tag that fell out # of the window on this run. Each page already embeds its releases' assets, so # no follow-up request per release is needed. releases='[]' page=1 while :; do body="$(curl -fsS -H "$auth" "${API}/releases?limit=${per_page}&page=${page}&draft=false")" || { warn "listing releases failed on page ${page}; pruning what was listed so far" break } count="$(jq 'length' <<<"$body")" || { warn "unreadable release page ${page}"; break; } releases="$(jq -s 'add' <(printf '%s' "$releases") <(printf '%s' "$body"))" # A short page is the last one; this also stops a server that ignores `page`. (( count < per_page )) && break page=$(( page + 1 )) done # Sort here rather than trusting the response order, and skip drafts in case # the server ignored `draft=false` — a draft must not consume a keep slot and # strip the binaries off the newest real release. mapfile -t stale < <(jq -r --argjson keep "$keep" ' [.[] | select(.draft != true)] | sort_by(.created_at) | reverse | .[$keep:] | .[] | "\(.id) \(.assets // [] | map(.id) | join(","))" ' <<<"$releases") (( ${#stale[@]} )) || { echo "prune-release-assets: nothing older than the newest ${keep} releases"; exit 0; } for line in "${stale[@]}"; do id="${line%% *}" assets="${line#* }" for asset in ${assets//,/ }; do echo "deleting asset ${asset} of release ${id}" curl -fsS -X DELETE -H "$auth" "${API}/releases/${id}/assets/${asset}" >/dev/null \ || warn "deleting asset ${asset} of release ${id} failed" done done