fix(release): recover a tag whose release never published #27
@@ -80,6 +80,8 @@ jobs:
|
||||
# ── RELEASE ENGINE: decide the next version + changelog ──────────────
|
||||
- name: Plan the release (version + changelog)
|
||||
id: plan
|
||||
env:
|
||||
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p dist
|
||||
@@ -117,21 +119,59 @@ jobs:
|
||||
VERSION="$(bump "${LAST_TAG#v}" "$BUMP")"
|
||||
fi
|
||||
|
||||
# An existing tag is NOT automatically "nothing to do". A tag with no
|
||||
# release behind it means a previous 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 already been pushed. Standing down on the tag
|
||||
# alone makes that state permanent: every later run sees the tag, sets
|
||||
# RELEASE=false, and the release never appears. Note this deliberately
|
||||
# OVERRIDES the RELEASE=false decided just above — with the tag in
|
||||
# place there are no releasable commits after it, so the normal path
|
||||
# would stand down, which is exactly why it could never self-heal.
|
||||
REUSE_TAG=false
|
||||
if git rev-parse -q --verify "refs/tags/v${VERSION}" >/dev/null; then
|
||||
echo "Tag v${VERSION} already exists — nothing to release."
|
||||
RELEASE=false
|
||||
CI_TOKEN="$(printf '%s' "${REGISTRY_TOKEN:-}" | tr -d '\r\n')"
|
||||
REL_HTTP="$(curl -s -o /dev/null -w '%{http_code}' -H "Authorization: token ${CI_TOKEN}" "https://${GITEA_HOST}/api/v1/repos/${REPO}/releases/tags/v${VERSION}" || echo 000)"
|
||||
if [ "$REL_HTTP" = "200" ]; then
|
||||
echo "Tag v${VERSION} already has a release — nothing to do."
|
||||
RELEASE=false
|
||||
elif [ "$REL_HTTP" = "404" ]; then
|
||||
echo "::warning::Tag v${VERSION} exists but has no release — a previous run failed after tagging. Reusing the tag and publishing the release it is missing."
|
||||
REUSE_TAG=true
|
||||
RELEASE=true
|
||||
else
|
||||
# Anything else (000 from a network failure, 401/403 from a bad
|
||||
# token) is not evidence of absence. Guessing "no release" would
|
||||
# re-publish over a good one, so refuse instead.
|
||||
echo "::error::Could not determine whether a release exists for v${VERSION} (HTTP ${REL_HTTP}). Refusing to guess."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Changelog range. A recovery run has nothing after the tag, so
|
||||
# summarize what the tag itself contains rather than emitting an empty
|
||||
# list: the range that produced it, i.e. previous-tag..this-tag.
|
||||
if [ "$REUSE_TAG" = true ]; then
|
||||
PREV_TAG="$(git describe --tags --match 'v*' --abbrev=0 "v${VERSION}^" 2>/dev/null || true)"
|
||||
if [ -n "$PREV_TAG" ]; then CL_RANGE="${PREV_TAG}..v${VERSION}"; else CL_RANGE="v${VERSION}"; fi
|
||||
SINCE="$PREV_TAG"
|
||||
else
|
||||
CL_RANGE="$RANGE"
|
||||
SINCE="$LAST_TAG"
|
||||
fi
|
||||
CL_SUBJECTS="$(git log --no-merges --format='%s' $CL_RANGE || true)"
|
||||
|
||||
{
|
||||
echo "## ${BIN} v${VERSION}"
|
||||
echo
|
||||
FEATS="$(echo "$SUBJECTS" | grep -E '^feat' || true)"
|
||||
FIXES="$(echo "$SUBJECTS" | grep -E '^(fix|perf)' || true)"
|
||||
FEATS="$(echo "$CL_SUBJECTS" | grep -E '^feat' || true)"
|
||||
FIXES="$(echo "$CL_SUBJECTS" | grep -E '^(fix|perf)' || true)"
|
||||
[ -n "$FEATS" ] && { echo "### Features"; echo "$FEATS" | sed 's/^/- /'; echo; }
|
||||
[ -n "$FIXES" ] && { echo "### Fixes"; echo "$FIXES" | sed 's/^/- /'; echo; }
|
||||
echo "### All changes"
|
||||
if [ -n "$LAST_TAG" ]; then echo "Since ${LAST_TAG}:"; fi
|
||||
echo "$SUBJECTS" | sed 's/^/- /'
|
||||
if [ -n "$SINCE" ]; then echo "Since ${SINCE}:"; fi
|
||||
echo "$CL_SUBJECTS" | sed 's/^/- /'
|
||||
} > dist/CHANGELOG.md
|
||||
|
||||
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
||||
@@ -266,7 +306,16 @@ jobs:
|
||||
else
|
||||
echo "Version unchanged (first release) — no bump commit needed."
|
||||
fi
|
||||
git tag "${TAG}"
|
||||
# The tag may already exist when finishing a run that died after tagging
|
||||
# (see the plan step). `git tag` on an existing name fails under
|
||||
# `set -e`; pushing an identical existing tag is a harmless no-op. A
|
||||
# push that fails here means the remote tag points somewhere else,
|
||||
# which SHOULD stop the run.
|
||||
if git rev-parse -q --verify "refs/tags/${TAG}" >/dev/null; then
|
||||
echo "Tag ${TAG} already exists — reusing it."
|
||||
else
|
||||
git tag "${TAG}"
|
||||
fi
|
||||
git push origin "${TAG}"
|
||||
|
||||
# ── RELEASE ENGINE: create the Gitea release + upload assets ─────────
|
||||
|
||||
Reference in New Issue
Block a user