Tag releases at the commit that carries the version - #187
Conversation
release.sh bumped MARKETING_VERSION and CURRENT_PROJECT_VERSION but never committed them, so every tag pointed at source declaring the *previous* version — v1.8.1's tree says 1.8.0, v1.9.0's says 1.8.1. Checking out a tag didn't rebuild that release. Commit the bump and tag at it. The commit lands after the build, notarize and sign steps rather than before, so a run that fails partway leaves no stray commit behind. Guard the whole thing on a clean, in-sync main: without that the bump commit would sweep up unrelated working-tree changes, and a push failing at the end would strand an already-public release.
There was a problem hiding this comment.
Looks good. The fix is correct and the preflight guards are well-designed.
What's solid:
git status --porcelain/ ahead-behind check are robust preflight guards; failure messages are clear and tested.- Placing the commit after build/notarize/staple/sign is the right call — those are the steps that actually fail, so a bad run leaves no orphan commit on
main. - Capturing
RELEASE_SHAafter the push and threading it intogh release create --targetis the right way to pin the tag; using a branch name there would be racy. fetch --quietbefore the sync check means the behind-count reflects the actual remote state, not a stale local tracking ref.
CI note: only the ssot review workflow is showing (in-progress); no build/test CI run visible for this diff, consistent with this being a shell-script-only change with no Swift sources touched.
219af70 to
eb70129
Compare
A re-run was worse than before the tag change: the version bump is unconditional, so retrying an already-released version burned a build number and pushed a public "Release vX.Y.Z" commit before dying at `gh release create`. Check for the tag, and separately for a release — a partly-failed `gh release create` can leave a draft with no tag. Also assert origin is $GITHUB_REPO. The bump pushes to origin while the release is cut against $GITHUB_REPO; if they differ, every other guard passes and --target then references a SHA the release repo doesn't have.
The previous commit was scratch state from exercising the branch guard against this branch instead of main.
There was a problem hiding this comment.
Round 2 follow-up — previous findings addressed.
The two concerns raised after the first approval are now correctly handled:
Re-release guard: ls-remote --exit-code --tags catches a published tag; the separate gh release view catches a draft left by a partly-failed run. Both run before any push — ordering is correct.
Origin/GITHUB_REPO identity check: glob-matching $ORIGIN_URL against $GITHUB_REPO catches forks and stale .env configs up front, before the branch is touched.
Failed-push failure mode: If git push fails (step 5), set -euo pipefail exits immediately with a local-only "Release vX.Y.Z" commit. On re-run the ahead-behind guard (AHEAD != 0) blocks cleanly, requiring a manual reset — correct and safe.
RELEASE_BRANCH correctly restored to "main".
CI: only the ssot review workflow is visible; no build/test CI is expected for a shell-script-only change.
release.shbumpsMARKETING_VERSIONandCURRENT_PROJECT_VERSION, then never commits them. It only commits togh-pages. So every tag points at source declaring the previous version:project.ymlat that tagChecking out a release tag doesn't rebuild that release. The bump has been landing as a manual
Release vX.Y.Zcommit afterwards, one commit too late.Change
Commit the bump inside the script and tag the release at that commit (
gh release create --target).Placement is deliberate: the commit happens after build, notarize, staple and sign — the steps that actually fail — so a run that dies partway leaves no stray commit behind. Everything before that point is reversible.
Preflight
Committing from the script means the working tree has to be trustworthy, so it now refuses to start unless:
main— not a feature branchmainis in sync withorigin— checked up front so the push at the end can't fail after the release is already publicAll three run before the ~4-minute build, alongside the existing changelog and
SUPublicEDKeychecks.Test plan
Each guard exercised against the real script, not a copy:
error: on branch 'wells/release-script-tag-fix', expected 'main'.error: working tree is dirty. Commit or stash first:+git status --shorterror: … is out of sync with origin (1 ahead, 0 behind). Pull/push first.bash -nclean. The commit/tag path itself runs only during a real release — it'll get its first live exercise on v1.9.1.Not fixed
The existing v1.8.0 / v1.8.1 / v1.9.0 tags stay where they are. Moving a published tag rewrites history users may already have fetched.
Review round 2
Adversarial review found that the first commit made re-runs worse than before, plus a repo-identity hole. Both fixed.
A retry could publish a junk commit. The version bump is unconditional, so re-running an already-released version burned a build number and pushed a public
Release vX.Y.Zcommit — then died atgh release createon the tag collision. That's a regression the tagging change introduced, and it lands onmainwhere everyone sees it.Now checked before anything is pushed, as two separate guards:
$GITHUB_REPO— a partly-failedgh release createcan leave a draft behind without ever creating the tag, which a tag check alone missesOrigin might not be the release repo. The bump pushes to
originwhile the release is cut against$GITHUB_REPO. If those differ (a fork, or a stale.env), every other guard passes and--targetthen references a SHA the release repo doesn't have. Now asserted up front.Verified against the real script:
v1.9.0→error: tag v1.9.0 already exists on origin.Review also confirmed as sound: detached-HEAD handling,
set -euo pipefailinteraction, the AHEAD/BEHIND arithmetic,--targetaccepting a just-pushed SHA, and that a rejected push leaves only a local commit with nothing published.