tools/release.sh + how-to-release.md.in: guard against tag-before-bump ordering bug (#2137)

This commit is contained in:
John Kerl 2026-07-05 10:13:16 -04:00 committed by GitHub
parent 34da987dc2
commit d008b70a1b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 33 additions and 0 deletions

View file

@ -14,8 +14,12 @@ Much of the mechanical work below is automated by `tools/release.sh`, which has
`rpmbuild` is mandatory for `pre-release` (the SRPM is a required release artifact); `rpmlint` is optional and is run only if installed. The ReadTheDocs admin steps and the flip from pre-release to public remain manual. Each subcommand is idempotent, so a partial run can be re-invoked safely. `rpmbuild` is mandatory for `pre-release` (the SRPM is a required release artifact); `rpmlint` is optional and is run only if installed. The ReadTheDocs admin steps and the flip from pre-release to public remain manual. Each subcommand is idempotent, so a partial run can be re-invoked safely.
**If you are running `pre-release`, do not also do the version-bump / tarball / SRPM / GitHub-release-and-tag steps below by hand first.** `pre-release` does all of that itself, in the order that makes the tag land correctly (version bump commit pushed *before* the tag is cut). Manually creating the GitHub release/tag before the version-bump commit is pushed reproduces the exact bug called out under "Create the GitHub release tag" below -- the tag gets pinned to whatever commit `main` was at that moment, forever, even though it looks like `--target main`. Worse, `pre-release`'s idempotency check only confirms *a* release exists for the tag; it does not verify the tagged commit actually contains the version bump, so a subsequent `pre-release` run will not notice or fix a bad tag -- it will just keep uploading assets to it. If this happens, the only fix is to delete the GitHub release and the tag (`git push --delete origin vX.Y.Z`, plus the local tag) and recreate it pointing at the correct commit.
## Manual steps ## Manual steps
These are the individual steps `pre-release`/`docs`/`afterwork` automate, kept here as a reference for what each phase actually does (and as a fallback if you need to run a step by hand). Do not run these separately from the script for a release the script is also handling.
* Update version found in `mlr --version` and `man mlr`: * Update version found in `mlr --version` and `man mlr`:
* Edit `pkg/version/version.go` from `6.2.0-dev` to `6.3.0`. * Edit `pkg/version/version.go` from `6.2.0-dev` to `6.3.0`.
@ -38,6 +42,7 @@ Much of the mechanical work below is automated by `tools/release.sh`, which has
* Create the GitHub release tag: * Create the GitHub release tag:
* If `pre-release` is handling this release, skip this step -- do not create the release/tag by hand first. See the warning above.
* Don't forget the `v` in `v6.3.0` * Don't forget the `v` in `v6.3.0`
* Write the release notes -- save as a pre-release until below * Write the release notes -- save as a pre-release until below
* Be sure the commit being used is the (non-`main`) PR commit containing the new version, or, `main` after that PR is merged back to `main`. (Otherwise, the release will be tagging the commit _before_ the changes, and `mlr version` will not show the new release number.) * Be sure the commit being used is the (non-`main`) PR commit containing the new version, or, `main` after that PR is merged back to `main`. (Otherwise, the release will be tagging the commit _before_ the changes, and `mlr version` will not show the new release number.)

View file

@ -127,6 +127,32 @@ push_if_needed() {
run_cmd git push "$@" origin "$branch" run_cmd git push "$@" origin "$branch"
} }
# verify_release_tag_commit guards against the classic ordering bug: a
# release/tag for $TAG created (by hand, or by anything other than this
# script's own Phase 4) BEFORE the version-bump commit was pushed. Git tags
# don't retroactively follow branch movement, so once cut against the wrong
# commit they stay wrong -- and a same-tag `gh release view` success on a
# resumed run looks identical whether the tag is right or wrong. This checks
# the actual tagged commit's pkg/version/version.go, not just tag existence.
verify_release_tag_commit() {
local tag="$1"
# Read-only (fetch/rev-parse/show), so this runs even under --dry-run --
# skipping it there would hide exactly the bug it exists to catch.
git fetch --tags origin >/dev/null 2>&1 || true
if ! git rev-parse --verify --quiet "refs/tags/$tag" >/dev/null; then
# Not fetchable yet (e.g. propagation lag right after creation); nothing
# to check against.
return 0
fi
local tagged_version
tagged_version="$(git show "${tag}:pkg/version/version.go" 2>/dev/null \
| awk -F'"' '/^var[[:space:]]+STRING/ { print $2; exit }')"
if [ "$tagged_version" != "$VERSION" ]; then
die "tag '$tag' points at a commit where pkg/version/version.go reports '$tagged_version', not '$VERSION' -- it was likely created before the version-bump commit landed (see docs/src/how-to-release.md.in's warning on tag ordering). Delete the release and the tag (locally and on origin) and recreate it pointing at the correct commit before continuing."
fi
note "verified tag '$tag' points at a commit with version.go == '$VERSION'"
}
# ============================================================================ # ============================================================================
# Argument parsing # Argument parsing
# ============================================================================ # ============================================================================
@ -566,6 +592,8 @@ phase_4_github_release() {
--notes-file "$NOTES_FILE" --notes-file "$NOTES_FILE"
fi fi
verify_release_tag_commit "$TAG"
banner "PHASE 4: upload assets" banner "PHASE 4: upload assets"
# --clobber so that a resumed run can re-upload a replaced artifact. # --clobber so that a resumed run can re-upload a replaced artifact.
run_cmd gh release upload "$TAG" "$tgz" --clobber run_cmd gh release upload "$TAG" "$tgz" --clobber