fix(release): recover a tag whose release never published #27

Merged
whitlocktech merged 1 commits from fix/release-tag-without-release into main 2026-08-05 18:09:40 +00:00

1 Commits

Author SHA1 Message Date
07021d38c9 fix(release): recover a tag whose release never published
All checks were successful
PR Checks / rust-gates (pull_request) Successful in 2m15s
An existing tag was treated as "nothing to release", unconditionally.
That is wrong in the one case it matters: a tag with no release behind
it means an earlier run tagged and then died before publishing -- which
is exactly what happened on servuo-plugins' first release, where absent
REGISTRY_* secrets took the release API call to 401 after the tag had
been pushed. Standing down on the tag alone makes that permanent. Every
later run sees the tag, sets RELEASE=false, and the release never
appears; the version is unpublishable forever.

The tag now decides nothing on its own. The API does:

  200 -> a release exists, stand down
  404 -> tag without release, reuse the tag and publish what is missing
  else -> refuse and exit 1

The last arm matters as much as the others. A 000 from a network failure
or a 401 from a bad token is not evidence of absence, and guessing "no
release" would republish over a good one.

Note the 404 arm deliberately overrides the RELEASE=false decided just
above it: with the tag in place there are no releasable commits after
it, so the normal path always stands down -- which is why this could
never self-heal on its own.

Two consequences handled with it. The changelog range is now
previous-tag..this-tag on a recovery run, since a run finishing an
earlier one has nothing after the tag and would otherwise publish an
empty change list. And the tagging step tolerates the tag already
existing, because `git tag` on an existing name fails under `set -e`
while pushing an identical tag is a harmless no-op -- a push that does
fail there means the remote tag points somewhere else, which should
stop the run.

This is the same handling installer/release.yml already carries; link
was the copy that still had the trap.

Verified by extracting this step and driving it through four scenarios
against a real clone with the live tags, with curl stubbed to return
each status: a normal patch bump (v1.1.1, release=true), a tag with no
release (recovers, release=true), a tag with a release (stands down),
and an unreachable API (exit 1, publishes nothing).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-05 13:05:31 -05:00