ci / ci (pull_request) Successful in 3m6s
Gitea has no release-asset retention, so every v* tag kept its full cross-platform binary set forever — 31 tags had accumulated 1.8 GB on the production host disk. Nothing consumes an old asset: CI takes the Crucible binary from the Nix flake, and every deleted file is reproducible from its tag. scripts/prune-release-assets.sh lists every release page, sorts by creation date, skips the newest two published releases, and deletes the remaining releases' attachments. Two rather than one so a release in flight never leaves the previous download broken. Drafts never consume a keep slot. Releases, tags, source archives and release notes are untouched. Nothing is excluded — SHA256SUMS and the wrappers go with the binaries: the checksums are only meaningful next to the files they cover, and the consumer drift-check reads the wrappers from the latest release, which stays complete. The step is best-effort: a stale asset is cheaper than a blocked publish, so API failures warn and the step carries continue-on-error. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
79 lines
3.5 KiB
Bash
Executable File
79 lines
3.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Contract for scripts/prune-release-assets.sh, driven by a stub curl on PATH:
|
|
# only assets of releases outside the keep window go, the window is decided by
|
|
# release date rather than response order, drafts never consume a keep slot,
|
|
# releases and tags stay, and an API failure never fails the run.
|
|
set -euo pipefail
|
|
|
|
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
script="$repo_root/scripts/prune-release-assets.sh"
|
|
tmp="$(mktemp -d)"
|
|
trap 'rm -rf "$tmp"' EXIT
|
|
|
|
# Stub curl: one short page of releases, deliberately out of date order and
|
|
# with a draft that is newer than every published release. Each release embeds
|
|
# two assets. Every request is appended to $CALLS for the assertions below.
|
|
mkdir -p "$tmp/bin"
|
|
cat >"$tmp/bin/curl" <<'STUB'
|
|
#!/usr/bin/env bash
|
|
url="${!#}"
|
|
printf '%s\n' "$*" >>"$CALLS"
|
|
rel() { printf '{"id":%s,"created_at":"%s","draft":%s,"assets":[{"id":%s01},{"id":%s02}]}' "$1" "$2" "$3" "$1" "$1"; }
|
|
case "$url" in
|
|
*releases\?*)
|
|
[[ "${FAIL_LIST:-0}" == 1 ]] && exit 22
|
|
printf '[%s,%s,%s,%s,%s]\n' \
|
|
"$(rel 12 2024-01-02T00:00:00Z false)" \
|
|
"$(rel 10 2024-01-04T00:00:00Z false)" \
|
|
"$(rel 9 2024-06-01T00:00:00Z true)" \
|
|
"$(rel 13 2024-01-01T00:00:00Z false)" \
|
|
"$(rel 11 2024-01-03T00:00:00Z false)"
|
|
;;
|
|
*/assets/*) [[ "${FAIL_DELETE:-0}" == 1 ]] && exit 22 ;;
|
|
esac
|
|
exit 0
|
|
STUB
|
|
chmod +x "$tmp/bin/curl"
|
|
export PATH="$tmp/bin:$PATH" API=https://git.example/api/v1/repos/o/r TOKEN=t
|
|
|
|
run() { CALLS="$tmp/calls" bash "$script" >"$tmp/out" 2>"$tmp/err"; }
|
|
|
|
# Default keep=2: the two newest published releases (10, 11) stay whole, the
|
|
# older two (12, 13) lose their assets, and the newer draft is ignored.
|
|
: >"$tmp/calls"; run
|
|
deletes="$(grep -c -- '-X DELETE' "$tmp/calls" || true)"
|
|
[[ "$deletes" == 4 ]] || { echo "expected 4 asset deletes, got $deletes" >&2; exit 1; }
|
|
grep -q 'assets/1201' "$tmp/calls" && grep -q 'assets/1302' "$tmp/calls" || {
|
|
echo "expected assets of releases 12 and 13 to be deleted" >&2; exit 1; }
|
|
if grep -q 'releases/10/assets/\|releases/11/assets/' "$tmp/calls"; then
|
|
echo "kept releases lost assets" >&2; exit 1
|
|
fi
|
|
if grep -q 'releases/9/assets/' "$tmp/calls"; then
|
|
echo "a draft release was pruned" >&2; exit 1
|
|
fi
|
|
# A release or tag must never be deleted, only attachments under /assets/.
|
|
if grep -- '-X DELETE' "$tmp/calls" | grep -qv '/assets/'; then
|
|
echo "a non-asset DELETE was issued" >&2; exit 1
|
|
fi
|
|
# A short page ends the sweep: no second page, so a server ignoring `page`
|
|
# cannot spin the job until the job timeout.
|
|
[[ "$(grep -c 'releases?' "$tmp/calls")" == 1 ]] || {
|
|
echo "a short release page did not end the sweep" >&2; exit 1; }
|
|
|
|
# KEEP=4 covers every published release: nothing to delete.
|
|
: >"$tmp/calls"; KEEP=4 run
|
|
if grep -q -- '-X DELETE' "$tmp/calls"; then echo "KEEP=4 still deleted" >&2; exit 1; fi
|
|
|
|
# Failures are warnings, never a non-zero exit.
|
|
: >"$tmp/calls"; FAIL_DELETE=1 run || { echo "delete failure failed the run" >&2; exit 1; }
|
|
grep -q 'failed' "$tmp/err" || { echo "delete failure was not warned about" >&2; exit 1; }
|
|
|
|
: >"$tmp/calls"; FAIL_LIST=1 run || { echo "listing failure failed the run" >&2; exit 1; }
|
|
grep -q 'listing releases failed' "$tmp/err" || {
|
|
echo "listing failure was not warned about" >&2; exit 1; }
|
|
if grep -q -- '-X DELETE' "$tmp/calls"; then
|
|
echo "deleted assets despite an unreadable release list" >&2; exit 1
|
|
fi
|
|
|
|
echo "prune-release-assets: prunes by release date outside the keep window, ignores drafts, never deletes a release, never fails the build"
|