Skip to content
Releasing the bench CLI

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:

  1. Creates a project access token named goreleaser, with the Developer role and the api scope. Developer is enough to create a Release and publish to the package registry. The existing danger token is configured exactly this way; copy it.
  2. Adds it under Secure > Secrets manager with the name goreleaser_token and Branch set to v*. The job only runs on tag pipelines, where CI_COMMIT_BRANCH is unset, so a secret scoped to main never 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.

  1. In a merge request, bump version in cli/Cargo.toml and config/Cargo.toml, then run cargo update -w to refresh Cargo.lock. Merge it; the merge commit is what you tag in step 3.

  2. 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=publish
  3. Create and push the tag on the bump MR’s merge commit:

    git tag vX.Y.Z <merge-commit-sha>
    git push origin vX.Y.Z
  4. Watch release:goreleaser in the tag pipeline. It builds all four targets and uploads the archives plus checksums.txt.

  5. Verify the published artifacts:

    mise x gitlab:gitlab-org/lab-bench/bench@X.Y.Z -- bench --version

    It should print the tagged version, since the release build stamps BENCH_VERSION from the tag. Any other build reports 0.0.0-dev.

  6. 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 --version of the previous release and this one: the common-template-copier ref bench scaffold pins, the labkit-rs rev, and the labkit-go and fairway minimums bench build enforces. Neither bench scaffold nor bench build renders the repo again once .copier-answers.yml exists, 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 a spec.listeners manifest 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.rs

A 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.

Last updated on