Repository navigation
chore: harden the release script - #159
Merged
Merged
Conversation
script/release was the stock actions/typescript-action template, and nothing referenced it - not the README, not an npm script, not a workflow. This ports the version currents-dev/cancel-run-gh-action arrived at, after three rounds of review there, and documents it in the README so it stops being an orphan. What changes: - The version comes from package.json instead of being typed at a prompt and confirmed by eye, so the tag cannot disagree with the manifest. - Pre-flight checks the template leaves to the operator: clean tree, on main, level with the remote, tags fetched, version is X.Y.Z, and a version that is not above the last release is refused rather than force-moving the major tag backwards. - The release tag and the major tag go up in one atomic push. Separately, a rejected major tag left vX.Y.Z published with @vn still on old code. - A run that died after tagging can be repeated: an existing tag on the current HEAD is treated as an unfinished run and the remaining steps resumed, including a releases/vN branch the remote is missing. A tag on any other commit still aborts. - A GitHub release is published with generated notes; this repo has tags but no releases today. - Latest-tag selection uses `git tag --sort=-v:refname`, which keeps working once a minor reaches two digits. The template's glob still handles the v1.0.10 and v1.0.11 already in this repo's history; v1.10.0 is where it would report a stale latest. The releases/vN snapshot the template performs is kept - releases/v1 in this repo came from it - guarded against the first-release case where the template expands an empty tag into `git branch releases/`. Verified against a throwaway remote carrying this repo's tag layout, v1.0.10 and v1.0.11 included: the 2.1.0 -> 2.1.1 patch release tags v2.1.1, moves v2, and leaves releases/v1 untouched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AfXo7y92vCvoKLLaVgj5XM
|
| Topic | Details | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| Release documentation | Document how to run releases, update major tags, recover failed runs, and access action versioning guidance.Modified files (1)
Latest Contributors(2)
| |||||||||
| Release automation | Harden script/release with pre-flight validation, semantic version ordering, atomic tag pushes, resumable failures, major-release branch handling, and GitHub release creation.Modified files (1)
Latest Contributors(2)
|
Three findings from review, all verified against a throwaway remote before and after: - A release candidate blocked its own final. sort -V ranks v2.1.1-rc.1 above v2.1.1, and the glob admitted it, so a stray rc left on any branch became latest_tag and the monotonic check refused 2.1.1 as "behind the released v2.1.1-rc.1". Only exact vX.Y.Z counts as a release now. - A release landing while this one sat at the prompt was silently rolled back: the forced major-tag push clobbered it with the older commit fetched before the prompt. The push now carries --force-with-lease against the value read just before the prompt, so it is rejected instead, and --atomic holds the release tag back with it. The major tag's refspec deliberately lost its '+'. A '+' forces unconditionally and silently bypasses the lease - keeping it would have left this guard doing nothing at all. - A local releases/vN branch was pushed as the previous major's maintenance line without checking it was that major. It now has to be at the tag, or the run stops and says so, rather than publishing some unrelated branch that happens to carry the name. The tags are already up by that point, so the fix is to correct the branch and rerun. Not taken: restricting the scan to tags reachable from HEAD. Reachability is what this moved away from, and a release cut from a maintenance branch would then go uncounted - the one case the monotonic check exists to catch. shellcheck clean, as the version before it was. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AfXo7y92vCvoKLLaVgj5XM
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.
script/releasehere is the stockactions/typescript-actiontemplate, and nothing references it — not the README, not an npm script, not a workflow. This ports the versioncurrents-dev/cancel-run-gh-actionarrived at after three review rounds (its PR), and adds a Releases section to the README so it stops being an orphan.What changes
package.jsonagrees. The version now comes out ofpackage.json, so the tag cannot disagree with the manifest.main, level with the remote, tags fetched, version isX.Y.Z— and a version that is not above the last release is refused, rather than force-moving the major tag backwards onto older code.vX.Y.Zpublished with@vNstill pointing at old code.HEADis treated as an unfinished run and the remaining steps resumed — including areleases/vNbranch the remote is missing. A tag on any other commit still aborts.The
releases/vNsnapshot is kept —releases/v1in this repo came from it — guarded against the first-release case where the template expands an empty variable intogit branch releases/.One claim worth not overstating
Latest-tag selection moves to
git tag --list 'v[0-9]*.[0-9]*.[0-9]*' --sort=-v:refname. The template'sgit describe --abbrev=0 --match='v[0-9].[0-9].[0-9]*'is not currently broken here —v1.0.10andv1.0.11still match it, since the trailing*absorbs the extra digit. It breaks when a minor reaches two digits (v1.10.0), which this repo hasn't hit. Included for robustness, not to fix a live bug.Verified
Run against a throwaway remote carrying this repo's tag layout (
v1.0.9,v1.0.10,v1.0.11,v1.1.0,v1.1.5,v2.0.0,v2.1.0, movingv1/v2, and an existingreleases/v1): the2.1.0→2.1.1patch release tagsv2.1.1, movesv2to it, and leavesreleases/v1untouched.The refusal paths and the partial-failure reproductions (pre-receive hooks rejecting
refs/tags/v1andrefs/heads/releases/v1) were exercised in the source PR.npx prettier@3.9.10 --check README.mdpasses, and the new link definition sits inside the existingmarkdownlint-disable MD013block.Relationship to #158
Independent. #158 bumps the version to
2.1.1; this changes the tooling that cuts the release. Either can merge first — they touch different files. If this lands first, #158's release is cut with the hardened script.Generated by Claude Code
Need help on this PR? Tag
@codesmith-botwith what you need.