Skip to content

Releasing

Pushing a v* tag runs the release workflow: it tests, cross-compiles four platforms, builds the notes, publishes a GitHub release with the tarballs and checksums.txt, renders the Homebrew formula from those checksums, and commits it to the tap.

The work is in what happens before the tag.

1. Move the changelog section. Rename ## [Unreleased] to ## [X.Y.Z] - YYYY-MM-DD, add a fresh empty [Unreleased] above it, and update the link definitions at the bottom. That section becomes the release body, so write it for someone deciding whether to upgrade.

2. Check whether the shell output changed.

Terminal window
git diff v<previous> HEAD -- testdata/golden

Non-empty means every user must run loadout apply after upgrading, or their shell keeps the old aliases and they will not know why. The notes detect this automatically, but the changelog is what explains it.

3. Preview the notes.

Terminal window
make notes VERSION=vX.Y.Z

Confirm the “run loadout apply” section appears if and only if the golden files changed.

4. Green gate.

Terminal window
make check
make release VERSION=vX.Y.Z # all four platforms build
make tap VERSION=vX.Y.Z # formula renders and is valid ruby
make clean
Terminal window
git push origin main # let CI go green first
git tag -a vX.Y.Z -m "loadout vX.Y.Z"
git push origin vX.Y.Z

CI on main runs the macOS job. Tests here have a history of passing on Linux and mattering on macOS, so wait for it before tagging.

vMAJOR.MINOR.PATCH, and the comparison in selfupdate accepts nothing else — a pre-release suffix or a commit SHA reports as not comparable rather than being coerced into a wrong ordering.

BumpFor
PatchFixes, no change to generated shell or config schema
MinorNew aliases, functions, packs, commands, or config fields
MajorA config schema change users must act on, or a removal
Terminal window
loadout version
loadout update --check
brew upgrade loadout

Verify the tap formula’s checksums match the published release. Locally built tarballs never hash the same as the ones CI produced, so compare against the published checksums.txt, not your dist/.

install.sh says it cannot determine the latest version. No published release, or the tag has no GitHub Release attached. install.sh resolves latest through the /releases/latest redirect, so a bare tag is not enough.

brew still installs the old version. The tap was not updated. Without TAP_GITHUB_TOKEN the workflow warns rather than fails, on purpose, so a missing tap does not block the release everyone else uses. Publish dist/loadout.rb by hand in that case.

loadout update says “not a release version”. The running build is a development build, or the server returned something unparseable. The message distinguishes the two.

The formula fails to install. Its checksums must match the published artefacts, which CI built.

Possible, and occasionally right while the project is young: force-push the tag and the release survives with its assets.

Two things to know. The tag must actually move, or the old commit stays reachable and the exercise is pointless. And the published binaries were built from the old commit, so confirm nothing that reaches the binary changed — comments, documentation and tests do not.

The reference pages are generated from the catalog, so a release that changed aliases or functions needs them refreshed:

Terminal window
npm run gen
git commit -am "Refresh reference for vX.Y.Z"

The diff is a useful review in itself: it is exactly what changed in the shipped set.