Root cause of run #122's GC failure (turns out NOT a permission
issue, despite the 403):
1. The template's URL was wrong: /api/v1/packages/{owner}/container/
{name}/versions — Gitea interprets this as "look up a SINGLE
version named 'versions'" and returns "package does not exist".
The correct list endpoint is:
GET /api/v1/packages/{owner}?type=container&q={name}
which returns one entry per tag with {id, version, created_at}.
2. Cloudflare in front of git.jpaul.io returns 403 to the default
Python-urllib User-Agent — any non-Python UA passes (curl,
"requests", anything). That explains the 403 in CI (Python made
the call) vs 404 from my curl test (curl passed CF, hit Gitea's
wrong-URL 404). So both the URL AND the UA were broken.
Fixes:
- Set User-Agent to "crop-chem-docs-registry-gc/0.1" in api().
- Correct URL for list (above) + DELETE
/api/v1/packages/{owner}/container/{name}/{tag} for delete.
- Cleaner keep policy with explicit reasons:
always: :latest
always: corpus-* (production pins; Drawbar may have locked)
keep: --keep-latest most recent OTHER tags
keep: anything younger than --keep-days
delete: everything else
- --dry-run for safe testing.
Local dry-run against current 4 tags categorizes correctly and
deletes nothing (4 < keep-latest=6).
Leaving continue-on-error: true in the workflows for one more
cycle. If tonight's run passes the GC step cleanly, follow-up
commit removes the safety net.
(Workflow paths: filter excludes scripts/**, so this commit
doesn't trigger image-only.yml.)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>