From 77acac7c75530c14e5e611c6f7d9e5e55080fbac Mon Sep 17 00:00:00 2001 From: Drew Stone Date: Wed, 15 Jul 2026 13:55:10 -0600 Subject: [PATCH] ci(publish): atomic branch+tag push with race retry --follow-tags pushes per-ref: when a merge train moves main mid-run, the HEAD:main push is rejected but the tag ref still lands, orphaning a release tag that fails every later run with 'tag already exists' (v0.43.25 and v0.43.27 today). --atomic makes branch+tag land together or not at all; a lost race resets to fresh main and retries. --- .github/workflows/publish.yml | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 7a03bc3..f51ae9b 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -62,12 +62,29 @@ jobs: # known-good release (>= 11.5.1 for OIDC). Revisit once a fixed 12.x ships. - run: npm install -g npm@11.18.0 - - name: Bump patch, commit, tag, push + # --atomic: branch + tag land together or not at all. --follow-tags is + # per-ref — during a merge train the HEAD:main push gets rejected while + # the tag ref is still accepted, orphaning a vX.Y.Z tag that poisons + # every later release run ("fatal: tag already exists"). On a lost race, + # undo the local bump and retry on fresh main; the newer merge's queued + # run then no-ops via the already-published check. + - name: Bump patch, commit, tag, push (atomic) run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - npm version patch -m "chore(release): %s [skip release]" - git push origin HEAD:main --follow-tags + for attempt in 1 2 3; do + git fetch origin main + git reset --hard origin/main + npm version patch -m "chore(release): %s [skip release]" + V=$(node -p "require('./package.json').version") + if git push --atomic origin HEAD:main "refs/tags/v$V"; then + exit 0 + fi + echo "release push lost the race (attempt $attempt); retrying on fresh main" + git tag -d "v$V" + done + echo "::error::release push lost the race 3 times — check for an orphaned tag" + exit 1 - name: Publish to npm (skip if already published) run: |