Repository navigation
Script the release bump and tag - #1942
Merged
Merged
Conversation
Collaborator
Coverage Report for CI Build 37031682383Coverage remained the same at 87.494%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
DanGould
force-pushed
the
release-chain-head
branch
from
October 2, 2026 15:35
defc5c9 to
6cd76a8
Compare
DanGould
force-pushed
the
release-chain-head
branch
2 times, most recently
from
October 2, 2026 16:03
c85cc32 to
b82cb1d
Compare
Release tags were created by hand from the issue checklist, one per crate and one per language package, with the changelog pasted into the tag message. The script reads each version from the manifest that owns it, skips anything already tagged, runs check-invariants on the crates it is about to tag, and creates signed annotated tags named <crate>-<version>. The message is the one line "Release <tag>"; the GitHub release body already comes from CHANGELOG.md. It refuses a tree with uncommitted changes or a HEAD that is not on master, which is what verify-tag-hygiene would reject after the push. Master is fetched from github.com/payjoin/rust-payjoin by URL at tag time, because a maintainer's origin is often a fork and a local remote-tracking ref can be stale; verify-tag-hygiene takes that commit through RELEASE_MASTER and still defaults to origin/master in CI. It prints the push command, naming whichever remote points at payjoin/rust-payjoin, and never pushes. With no names it sweeps the release crates and reports the ones that are not bumped. With --bindings it also covers each payjoin-ffi package that ships a contrib/release-version.sh. That script prints the version the package's next tag names, so adding a language means adding one script rather than editing tag.sh, and a package without one is never tagged. The python tag carries +payjoin-<version> like the others; pyproject.toml cannot hold it, so its script appends the payjoin version tag.sh passes in.
Changelog sections were written at release time, in the bump PR. Any merge to master under an open bump PR then meant editing that PR and restacking everything above it, and the 1.1.0 bindings release was lost to that churn. Each release crate's CHANGELOG.md now keeps an Unreleased section at the top. A pull request that changes what a user of the crate sees adds its line there in the same commit. The bump PR renames the heading and nothing else, so it no longer has to change when something lands beneath it. A workflow fails a pull request that touches a release crate's src/ without touching its CHANGELOG.md, unless the PR carries the "changelog: none" label for changes with no user-visible effect. The rule is documented in CONTRIBUTING.md. Only triage access can apply a label, so an outside contributor says in the PR description that the change has no user-visible effect and a maintainer labels it.
The checklist still asked for a changelog compiled in the issue body and pasted into the tag message, and it stopped at the crate without saying what happens to the bindings or the dependent crates. The changelog is now the crate's Unreleased section, the tag is made by contrib/release/tag.sh, fixes merge to master with their own lines instead of being cherry-picked into the bump branch, and a payjoin release is followed by the bindings bump (Dart pins the release tag commit, never a branch commit) and by the dependent crates on their own schedule.
The bump commit was assembled by hand: edit the crate's version, find every workspace member that requires it and edit those too, retitle the changelog section, run update-lock-files.sh, and hope check-invariants agrees. The script does those steps in that order and stops at the first one that fails, leaving the tree uncommitted so the changelog section can be read before the commit. --no-lock skips the lock file regeneration for a quick look at the edits. The minor release checklist now calls it instead of listing the steps.
DanGould
force-pushed
the
release-chain-head
branch
from
October 2, 2026 16:06
b82cb1d to
c6d23fa
Compare
benalleng
approved these changes
Oct 2, 2026
3 of 5 tasks
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.
Four changes to the part of the release process that happens before the tag. Nothing from the tag onward changes.
bump.sh.
contrib/release/bump.sh CRATE X.Y.Zdoes the bump commit's mechanical work: sets the version, rewrites every workspace member's requirement on the crate, renames## Unreleasedto## X.Y.Zand opens a fresh one, regenerates both lock files, runs check-invariants. Leaves the tree uncommitted for review. Tested on mailroom 0.1.2 to 0.1.3 with the lock step skipped: the manifest, test-utils' requirement and the changelog heading all moved, and check-invariants flagged the stale lock files as it should.tag.sh.
contrib/release/tag.shcreates the signed release tags for HEAD from the manifest versions: one per release crate, and with--bindingsone per language package. It runs check-invariants first, refuses a dirty tree or a HEAD that is not on origin/master, writes the one-line messageRelease <tag>, and prints the push command.--dry-runshows what it would do. Tested against the 1.2.0 bump branch: it reports the crates that are not bumped, offerspayjoin-1.2.0, and refuses a crate whose current version is already tagged elsewhere.Changelog lines land with the PR. Each release crate's CHANGELOG.md gets an
## Unreleasedsection. A PR that changes what a user sees adds its line there; the bump PR renames the heading. A new workflow fails a PR that touches a release crate'ssrc/without touching its CHANGELOG.md unless it carries thechangelog: nonelabel. Documented in CONTRIBUTING.md and the PR template. The label needs creating in the repo settings before the check can be skipped.Minor release checklist rewritten to match: changelog from Unreleased, tag from tag.sh, no changelog copy in the tag message, and the bindings and dependent crates listed after the payjoin release instead of implied.
Follow-ups tracked in #1766: payjoin-test-utils out of the publish chain, shared major.minor for payjoin-ffi and its packages, semver-checks on every PR.
Reviewer: @benalleng
Written with Claude Code; I reviewed and ran the gates (
nix fmt -- --ci, codespell, a dry run of the script).