ci(release): sweep every tag for a missing release, not just this run's
All checks were successful
PR Checks / rust-gates (pull_request) Successful in 1m28s
All checks were successful
PR Checks / rust-gates (pull_request) Successful in 1m28s
The first commit on this branch said the plan step "CAN recover an orphan, but
only on a run that reaches it". Checking link and servuo-plugins for the same
gaps showed that understated it.
The recovery is VERSION-SCOPED. It computes VERSION from the newest tag plus the
conventional-commit bump, then only checks refs/tags/v${VERSION}. So it recovers
an orphan on the very next run and is useless afterwards: once any releasable
commit lands, the next run computes a NEW version and never looks at the old tag
again. The orphan becomes permanent and silent.
servuo-plugins proved it, and the proof is pointed. Its v0.1.0 had been orphaned
since 2026-08-04 -- tag present, no release, no assets -- while v0.1.1, v0.2.0
and v1.0.0 all published normally. The commit that ADDED the recovery to that
repo was itself typed "fix(release): preflight credentials and recover the
orphaned v0.1.0 tag", so it bumped to v0.1.1, and the run that introduced the
recovery stepped straight past the tag it was written to rescue.
The retry added in the previous commit makes an orphan much less likely, but it
does not make one impossible -- a cancelled job or a dying runner produces the
same state with no 500 anywhere -- and until now nothing would ever have
mentioned it again.
So the plan step now sweeps every v* tag and warns about any without a release.
It WARNS rather than recovers, on the org lead's decision. Publishing an old
version would mean building today's tree and shipping it under a tag whose tree
it is not, which is worse than the inconsistency it fixes; and a routine push
silently republishing ancient history is not a thing this pipeline should be
able to do. Recovery stays limited to the version the run computed.
It also never fails the run. A sweep that can break a good release is a sweep
someone will delete.
Verified by running the loop against the real repositories rather than a stub,
since the only thing worth proving is that it tells a clean repo from a dirty
one:
link (9 tags): clean
servuo-plugins (4 tags): :⚠️:Tags with no release: v0.1.0
installer (2 tags): clean
and again after servuo-plugins#15 deleted that tag, where all three report
clean. Every run block bash -n clean, the YAML parses, and no empty template
token.
Companion PRs: link#33 and servuo-plugins#15.
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -165,6 +165,39 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# ── Orphan sweep ────────────────────────────────────────────────
|
||||||
|
#
|
||||||
|
# The check above is VERSION-SCOPED: it only ever asks about the one
|
||||||
|
# version this run computed. That is enough to recover an orphan on
|
||||||
|
# the very next run, and useless afterwards — once any releasable
|
||||||
|
# commit lands, the next run computes a NEW version, never looks at
|
||||||
|
# the old tag again, and the orphan becomes permanent and silent.
|
||||||
|
#
|
||||||
|
# servuo-plugins v0.1.0 is the proof, and the proof is pointed: the
|
||||||
|
# commit that ADDED the recovery above was itself typed
|
||||||
|
# `fix(release): ... recover the orphaned v0.1.0 tag`, so it bumped to
|
||||||
|
# v0.1.1 — and the run that introduced the recovery stepped straight
|
||||||
|
# past the tag it was written to rescue. That tag is still orphaned.
|
||||||
|
#
|
||||||
|
# So every v* tag is checked, and anything missing a release is
|
||||||
|
# WARNED about. Deliberately not recovered: publishing an old version
|
||||||
|
# would mean building today's tree and shipping it under a tag whose
|
||||||
|
# tree it is not, which is worse than the inconsistency it fixes.
|
||||||
|
# A human decides whether to recover or drop it.
|
||||||
|
#
|
||||||
|
# Never fails the run. A sweep that can break a good release is a
|
||||||
|
# sweep someone will delete.
|
||||||
|
ORPHANS=""
|
||||||
|
for T in $(git tag -l 'v*' --sort=-v:refname); do
|
||||||
|
T_HTTP="$(curl -s -o /dev/null -w '%{http_code}' \
|
||||||
|
-H "Authorization: token $(printf '%s' "${REGISTRY_TOKEN:-}" | tr -d '\r\n')" \
|
||||||
|
"https://${GITEA_HOST}/api/v1/repos/${REPO}/releases/tags/${T}" || echo 000)"
|
||||||
|
[ "$T_HTTP" = "404" ] && ORPHANS="${ORPHANS} ${T}"
|
||||||
|
done
|
||||||
|
if [ -n "${ORPHANS}" ]; then
|
||||||
|
echo "::warning::Tags with no release:${ORPHANS} — a run failed after tagging. Publish or delete them; this job will not do either."
|
||||||
|
fi
|
||||||
|
|
||||||
# Changelog range. A recovery run has nothing after the tag, so
|
# Changelog range. A recovery run has nothing after the tag, so
|
||||||
# summarize what the tag itself contains rather than emitting an empty
|
# summarize what the tag itself contains rather than emitting an empty
|
||||||
# list: the range that produced it, i.e. previous-tag..this-tag.
|
# list: the range that produced it, i.e. previous-tag..this-tag.
|
||||||
|
|||||||
Reference in New Issue
Block a user