Tag what a release built instead of what it was meant to build - #135
Open
andiwand wants to merge 1 commit into
Open
Tag what a release built instead of what it was meant to build#135andiwand wants to merge 1 commit into
andiwand wants to merge 1 commit into
Conversation
A version tag had to be pushed before the build it named, which is a promise the release cannot keep: a version often takes more than one build to clear review, and the second one is built from a different commit. Tag 1.37 is the case in point - it points at 6e97420, three commits behind what was actually submitted, and the build that shipped has no name in git at all. So the tag stops being the trigger and becomes the record. The workflow is dispatched by hand with the version it should build, and writes build/<flavor>/<version>/<build> at the commit it built once the upload goes through. Per flavor, because Pro and Lite count their builds separately: one commit uploaded to both apps is two builds with two different numbers. The plain version tag is left to a human, once the release is actually live. The build number was only ever known inside the lane, so the Fastfile hands it back through GITHUB_OUTPUT. Dropping the tag trigger also matters on its own: the receipt tag would have matched '[0-9]*' and started another release. resolve-version.py loses its tag arm, and the version input is now the only source. Nobody bumps a version in the tree - a commit on main is not a release, and the 0.0.0 in project.pbxproj says so. CHANGELOG.md gets the matching rule: the heading is cut at submission rather than at a tag, on main, in the same pull request that writes the store copy, and a fix that goes up as a second build of the same version belongs under the heading already cut rather than back under Unreleased. That is the case #134 fell into. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HcHeDGMHcFChp6oHiaiEDV
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A version tag had to be pushed before the build it named, which is a promise the release cannot keep: a version often takes more than one build to clear review, and the second one gets built from a different commit. Tag
1.37is the case in point — it points at6e97420, three commits behind what was actually submitted, and the build that shipped has no name in git at all.So the tag stops being the trigger and becomes the record.
What changes
release.ymlis dispatch-only. Thepush: tags:trigger is gone; theversioninput is now the only source. Same one-liner as before:The workflow tags what it built. After each successful upload it writes
build/<flavor>/<version>/<build>at the built commit, annotated with the run it came from. Per flavor, because Pro and Lite count their builds separately — one commit uploaded to both apps is two builds with two different numbers. Needscontents: write, and thesuccess()in the step'sifis spelled out because naming anifdrops the implicit one.The plain
<version>tag stays manual, created once the release is live, pointing at the accepted build rather than the tip ofmain:git tag 1.38 build/pro/1.38/4^{} && git push origin 1.38Dropping the tag trigger isn't optional here, incidentally:
1.38matches the old'[0-9]*'filter, so the receipt tag would have kicked off another release.Fastfilehands the build number back throughGITHUB_OUTPUT— it's computed fromlatest_testflight_build_numberinside the lane, and the workflow can't name the tag without it. A local run has noGITHUB_OUTPUTand is unaffected.resolve-version.pyloses its--tagarm and the tag/input agreement check. The "only a dry run may go without a version" guard is untouched.CHANGELOG.mdgets the matching rule: cut the heading at submission rather than at a tag, onmain, in the same PR that writes the store copy — and if a second build of the same version is needed, the fix goes under the heading already cut rather than back underUnreleased. That is exactly the case #134 fell into.Also
__pycache__/in.gitignore, since running the version script by hand leaves one.Notes
Nobody bumps a version in the tree. A commit on
mainis not a release and the0.0.0inproject.pbxprojsays so — a stale-but-plausible1.37sitting there would be worse.Two things deliberately left alone:
1.37tag. It's wrong but it's published; moving it is worse than leaving it. The README says what it points at.changelogs/1.37.txtdescribes the workCHANGELOG.mdfiles under 1.36, while 1.36's own entry reads as a shipped release. If 1.36 never reached users, that entry should say so — but that's a factual question about what shipped, not something to guess at in this PR.Untested end to end, since a real run uploads to App Store Connect. The YAML parses, the tag message dedents correctly out of the block scalar,
resolve-version.pywas exercised across all its cases, andruby -cpasses on theFastfile. Adry_rundispatch exercises everything except the tag step itself, which is skipped on a dry run by design.🤖 Generated with Claude Code