You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 3d2ce0a
Browse filesBrowse the repository at this point in the historyBrowse files
fix(release): land the version bump before the build, not after it
The first real automatic release did everything right — chose 0.25.0, bumped, passed the gate, built all six binaries, smoke-tested the linux one — and then threw it away, because main had moved during the eleven minutes it took and the bump would not fast-forward.
Holding the bump until the end was meant to keep a failed release from leaving anything behind. What it actually did was make every release race every other push to main, over a window as long as the whole job, and lose the entire build when it lost.
Push the bump seconds after checkout instead, rebasing onto main and retrying up to three times. Before anything is built, a rebase costs nothing and stays honest: the gate and the builds then run on exactly the tree that gets tagged. Rebasing at the END would have been worse than losing the race — the binaries come from this tree, and the tag would have named a commit whose tree was never built.
The tag still goes out last, after everything has proven itself. It is what publishes, so it is what is worth withholding.
A release that fails now leaves a bump commit with no tag. That is self-healing, not wreckage: the next run picks the same version, finds the files already at it, and prepare-release.sh takes its tag-only path — verified against a clone.
# Push the bump NOW, seconds after checkout, rather than at the end.
146
+
#
147
+
# It used to go out after the builds, so that a failed release left the
148
+
# branch untouched. That made every release lose a race it could not
149
+
# win: the job takes ~40 minutes, and any push in that window — a
150
+
# dependabot merge, an unrelated fix — made the bump un-fast-forwardable
151
+
# and threw the whole build away. It happened on the first real run.
152
+
#
153
+
# Rebasing at the END instead would be worse than losing the race: the
154
+
# binaries are built from this tree, and a rebased tag would name a
155
+
# commit whose tree is not the one that was built. Here, before
156
+
# anything is built, a rebase is free and honest — the gate and the
157
+
# builds then run on exactly what was pushed.
158
+
#
159
+
# What this trades: a release that fails later leaves the bump on the
160
+
# branch with no tag and no release. That state is self-healing, not
161
+
# wreckage — the next run computes the same version, finds the files
162
+
# already at it, and prepare-release.sh takes its tag-only path.
163
+
pushed=0
164
+
for attempt in 1 2 3; do
165
+
if git push origin "HEAD:${GITHUB_REF_NAME}"; then
166
+
pushed=1
167
+
break
168
+
fi
169
+
echo "push rejected — ${GITHUB_REF_NAME} moved (attempt ${attempt}/3); rebasing the bump onto it"
170
+
git fetch origin "${GITHUB_REF_NAME}"
171
+
git rebase "origin/${GITHUB_REF_NAME}"
172
+
done
173
+
if [[ "$pushed" != 1 ]]; then
174
+
echo "::error::could not land the version bump on ${GITHUB_REF_NAME} after 3 attempts — it is being pushed to faster than this job can rebase. Nothing has been published and no tag exists; try again when it is quieter."
175
+
exit 1
176
+
fi
177
+
178
+
# A rebase moves the commit, and prepare-release.sh's tag still points
179
+
# at where it used to be. Re-point it at what was actually pushed, so
180
+
# the tag published later names the tree that gets built below.
181
+
git tag -f "v${version}" HEAD
182
+
echo "bump landed on ${GITHUB_REF_NAME} as $(git rev-parse --short HEAD)"
# The point of no return for an automatic release, and deliberately the
349
-
# last thing before publishing: the gate has passed, the Studio UI is
350
-
# verified embedded, all six binaries are built and the linux/amd64 one
351
-
# has been run and asked what version it thinks it is. Only now do the
352
-
# bump and its tag leave this runner.
388
+
# The point of no return, and deliberately the last thing before
389
+
# publishing: the gate has passed, the Studio UI is verified embedded, all
390
+
# six binaries are built and the linux/amd64 one has been run and asked
391
+
# what version it thinks it is. Only now does the tag exist.
392
+
#
393
+
# The bump commit itself went out at the start (see "Choose the version and
394
+
# bump it"), because holding it until here meant losing a 40-minute race
395
+
# with every other push to the branch. The tag is what actually publishes —
396
+
# it is what the GitHub Release is cut from and what users download — so it
397
+
# is the thing worth withholding until everything has proven itself. A bump
398
+
# without a tag is a no-op that the next run absorbs.
353
399
#
354
-
# A rejected push means the branch moved during the ~40 minutes this took.
355
-
# That is NOT something to force past: the binaries above were built from
356
-
# THIS tree, and forcing would publish them under a branch that no longer
357
-
# contains the commit they came from. Fail and let a person look.
400
+
# Unlike the branch, a tag cannot be rejected for moving: nothing else
401
+
# creates it. The only way this fails is if the tag already exists, which
402
+
# prepare-release.sh refuses up front.
358
403
#
359
-
# Pushing the tag here does not start a second Release run: events raised
360
-
#by GITHUB_TOKEN do not trigger workflows. That restriction is usually a
404
+
# Pushing it here does not start a second Release run: events raised by
405
+
# GITHUB_TOKEN do not trigger workflows. That restriction is usually a
361
406
# nuisance; here it is what keeps one button from producing two releases.
362
-
- name: Publish the version bump (automatic releases)
407
+
- name: Publish the release tag (automatic releases)
363
408
if: steps.auto.outputs.tag != ''
364
409
env:
365
410
TAG: ${{ steps.auto.outputs.tag }}
366
411
run: |
367
412
set -euo pipefail
368
-
if ! git push origin "HEAD:${GITHUB_REF_NAME}"; then
369
-
echo "::error::${GITHUB_REF_NAME} moved while this release was building, so the bump could not be fast-forwarded. Nothing has been published and no tag exists. Re-run the release — the new run reads the newer commits and picks its version again."
370
-
exit 1
371
-
fi
372
413
git push origin "refs/tags/${TAG}"
373
-
echo "pushed the bump and ${TAG} to ${GITHUB_REF_NAME}"
414
+
echo "published ${TAG} at $(git rev-parse --short HEAD)"
0 commit comments