Releasing the bench CLI
A release publishes prebuilt bench binaries for macOS and Linux (x86-64 and
arm64) to a
GitLab Release.
GoReleaser drives it from
.goreleaser.yml; the jobs live
in
.gitlab-ci.yml under the release stage.
One-time setup: the publish token
release:goreleaser needs a token with api scope to create the Release and
upload assets. It reads one from the project’s
GitLab Secrets Manager
as goreleaser_token.
To provision the token, a project owner:
- Creates a
project access token
named
goreleaser, with the Developer role and theapiscope. Developer is enough to create a Release and publish to the package registry. The existingdangertoken is configured exactly this way; copy it. - Adds it under Secure > Secrets manager with the name
goreleaser_tokenand Branch set tov*. The job only runs on tag pipelines, whereCI_COMMIT_BRANCHis unset, so a secret scoped tomainnever resolves and the job fails with a 403.v*matches the release tags and nothing else.
Project access tokens expire, and a lapsed one fails the release job at tag time rather than before. GitLab emails project Maintainers and Owners ahead of the token’s expiry, so when the warning arrives, rotate the token and overwrite the secret’s value.
Cutting a release
The binary reports the version from the git tag. The crate manifests don’t affect it, but keep them in step so the source matches the release.
In a merge request, bump
versionincli/Cargo.tomlandconfig/Cargo.toml, then runcargo update -wto refreshCargo.lock. Merge it; the merge commit is what you tag in step 3.Check that the mock release is green on
main. It runs on merge requests touching.goreleaser.yml,.gitlab-ci.yml, or.mise.toml, and builds every archive without publishing. To run it locally on a macOS host:goreleaser release --snapshot --clean --skip=publishCreate and push the tag on the bump MR’s merge commit:
git tag vX.Y.Z <merge-commit-sha> git push origin vX.Y.ZWatch
release:goreleaserin the tag pipeline. It builds all four targets and uploads the archives pluschecksums.txt.Verify the published artifacts:
mise x gitlab:gitlab-org/lab-bench/bench@X.Y.Z -- bench --versionIt should print the tagged version, since the release build stamps
BENCH_VERSIONfrom the tag. Any other build reports0.0.0-dev.Write the release notes (next section). GoReleaser only fills in the commit list, and a release with nothing but a commit list has already cost a downstream team a day of debugging.
Release notes
Edit the Release description on gitlab.com after the tag pipeline publishes it.
Keep GoReleaser’s ## Changelog at the bottom and put an ## Upgrading from X.Y.Z section above it, in this shape (v0.2.1 is the model):
- Toolchain. What changed between
bench --versionof the previous release and this one: thecommon-template-copierrefbench scaffoldpins, the labkit-rs rev, and the labkit-go and fairway minimumsbench buildenforces. Neitherbench scaffoldnorbench buildrenders the repo again once.copier-answers.ymlexists, so a raised minimum is what an existing repo has to act on; name the file to bump. That is how a repo scaffolded on 0.2.0 ended up feeding aspec.listenersmanifest to fairway 1.x. - Action required. Anything a user must change in
bench.yaml, their repo, or their toolchain before the new version works. One bullet per change, with the error they’d otherwise see and the commit that caused it. - Also changed. User-visible behavior that needs no action.
- Known issues. Workarounds for anything shipped broken.
Diff the tag range to find what belongs in each list:
git log --oneline vPREV..vX.Y.Z
git diff vPREV..vX.Y.Z -- config/gitlab/bench/v1/ cli/src/cmd/scaffold.rsA schema change under config/ is almost always an action-required entry. A
changed line in bench --version is always a toolchain entry.
Why the release runs on macOS
The darwin targets link against the macOS SDK, which only Xcode provides; cargo-zigbuild supplies the Linux linker from that same host. So one macOS runner builds all four targets, while a Linux runner could only build two. Splitting the build across two runners and merging the artifacts into a single Release requires GoReleaser Pro.
The tradeoff is a dependency on saas-macos-medium-m1, which is in beta with
documented capacity limits.
If that pool is exhausted at tag time, retry the job; the tag already exists, so
nothing needs recreating.
Notarization
The macOS binaries are not notarized, which needs a paid Apple Developer ID.
Downloading them through a browser therefore trips Gatekeeper. mise and curl
do not set the quarantine attribute, so the documented install path avoids it
entirely.