Each tag rotation re-runs release.yml. The create-release POST
returns 4xx for the existing release, the script falls back to GET
on the existing one — and then re-POSTs the same asset names to the
upload endpoint. Gitea appends each upload as a new asset rather
than replacing, so the release page accumulates Todo-x86_64.AppImage
once per rotation, same for .zsync / latest.json / SHA256SUMS.
Fix: between getting the release id and the upload loop, list all
existing assets and DELETE them first. Single rotation = single set
of assets, regardless of how many times release.yml has run for this
tag.
Release body stays as set on first creation (the GET returns the
original). If a future rotation needs to refresh the body too, that
would be a separate PATCH on the release.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>