releasetools/release
Cut a release: prechecks, notes, version bump, pull request, merge, tag, publish
release
Newest release first. Each says what changed, and the choices behind it.
0.3.0 - 2026-11-04
Added
A repository that publishes by dispatch is released the way it publishes.
Under release.trigger: dispatch, step 5 runs the publish workflow with the
bare version and the release commit instead of pushing a tag, then checks that
the tag the workflow created names that commit or carries a Source-Commit
trailer naming it. Where the branch moved on after the merge, it dispatches on
a publish/v<version> branch at the release commit, so later commits never
ship under the version. Under tag, the default, nothing changes.
/release:setup declares which trigger a repository uses and changes its
publish workflow to meet the rules for it, as a pull request. It is a command
rather than a GitHub Action because a workflow's own token cannot write
.github/workflows/.
Choices
One key, release.trigger, rather than a convention per mechanism: conventions
apply unless excepted, so two rival ones would put every repository in breach
of one, and a repository allowing both would still need a key to say which this
plugin runs.
0.2.1 - 2026-09-28
Fixed
Release preparation runs in a linked worktree while the main checkout stays clean on the default branch. Publication tags the verified PR merge commit explicitly, even when the default branch advances during checks. Releases from another branch use a separate publication checkout, and the publish workflow is selected by the release commit and tag.
0.2.0 - 2026-09-21
A release's pull request lands the way .releasetools.yaml says, in the
merge key: squash, rebase or merge. It was --rebase for everybody
before this, and squash where the file leaves it out.
A branch requiring signed commits refuses a rebase merge, because GitHub
replays the author's commits unsigned, and a branch requiring linear history
refuses a merge commit. Squash is the only one both take, and
signed-git
had already said so, so a repository declaring nothing was being cut against
its own convention.
The rt this needs is named: v0.4.0, where version::bump arrived. Step 2
called it from the day this shipped, and an older rt answers
Invalid function name there.
The declaration example names my-package rather than a real distribution.
registry is the name the registry knows, which is not always the
repository's, and one copied from an example is checked against somebody
else's package.
The tag key is gone from the documentation. There was never one to set:
the format does not define it, @releasetools/config does not read it, and
the conventions settle the tag's shape. A repository that declared it got a
key nothing read.
Choices
The default changed rather than staying where it was. A key with a backwards-compatible default would have left every repository that declares nothing on the strategy its own conventions refuse, which is the reading nobody would choose if they were asked.
0.1.0 - 2026-09-20
/release:cut <version> runs a release's steps in order: the prechecks, the
changelog entry, the version bump, the pull request and its checks, the merge,
the wait for the merged commit to go green, and the tag that publishes. It
stops at the first refusal and says what refused.
What each step needs is read from .releasetools.yaml: the branch a release is
cut from, the workflow that has to be green, the workflow the tag starts, the
URL that must not already carry the version, and the command the project
declares for setting its version. Those keys are the
releasetools conventions
rather than this plugin's own, so a second tool reading the file reads the same
thing.
Choices
The steps are commands, not a script. An agent that runs them reads what each one printed and can say what went wrong; a script that wraps them turns a failure into an exit code and a person into a reader of logs. That is the whole reason this is a plugin rather than another subcommand.
Nothing here parses a manifest. Every ecosystem ships the command that sets a version, so the project declares which one it is and releasetools/cli runs it.
The tag's shape is not declared: the conventions settle it, and a key here would let a repository contradict a rule that is already made.
The tag lands only on a commit that has passed on its own, after the merge, rather than on the pull request's last green check. A rebase onto a branch that moved is a tree neither side has tested.