Cutting a release
Cutting a release
Section titled “Cutting a release”Everything in one place so the release ritual doesn’t grow tribal knowledge.
Prereqs (one-time)
Section titled “Prereqs (one-time)”- Push access to
mainonagentstategroup/agentstatecrucible. - CI variable
GITLAB_RELEASE_TOKENset (personal access token or project access token withwrite_repository) — used by the tag push step if you script it, and available to publish-release.sh as a fallback whenCI_JOB_TOKENis scoped out. - (Optional) A macOS runner registered with the tag
macosso therelease-binary-darwinjob can build Apple binaries. Until then, darwin binaries are missing from the release and the Homebrew formula sits withMISSINGin the SHA fields for macOS targets.
The 60-second version
Section titled “The 60-second version”# On a clean main, at the commit you want to ship:bash scripts/release.sh v0.1.0git push origin main v0.1.0That’s the whole ceremony. The tag pipeline takes over from there:
- check — fmt, clippy, build, version-guard (all versions == tag)
- test — cargo test
- build-binary — cross-compile linux musl x86_64 / aarch64 /
windows via
cargo-zigbuildfrom a rust:1.83 image. Uploadsdist/*.tar.gz,dist/*.zip,dist/SHA256SUMSas job artifacts. - build-image —
docker buildxpushes$CI_REGISTRY_IMAGE:X.Y.Zand:latest. - release —
scripts/publish-release.shuploads each tarball to the project’s Generic Package Registry and creates a GitLab Release object linking to them.
What scripts/release.sh actually does
Section titled “What scripts/release.sh actually does”- Verifies the working tree is clean.
- Verifies the tag matches semver.
- Stamps the workspace
Cargo.toml[workspace.package].version. - Stamps
web/package.jsonand updatesweb/package-lock.json. - Runs
cargo update --workspacesoCargo.lockreflects the new workspace version. - Commits everything as
release: vX.Y.Z. - Tags
vX.Y.Zon that commit. - Does not push. You push. On purpose — gives you one last
chance to inspect
git show HEADandgit show vX.Y.Zbefore it’s public.
The workflow rules in .gitlab-ci.yml skip the branch pipeline for
release: vX commits, so pushing the branch and tag together only
fires the tag pipeline (which is the authoritative one).
Publishing the Homebrew formula
Section titled “Publishing the Homebrew formula”Whenever a release completes, render the formula from that release’s SHA256SUMS and commit it to the tap:
# Grab SHA256SUMS from the release (once uploaded):mkdir -p /tmp/hbcurl -sSfL \ "https://<GITLAB-HOST>/api/v4/projects/<PROJECT-PATH>/packages/generic/crucible/0.1.0/SHA256SUMS" \ -o /tmp/hb/SHA256SUMS
bash scripts/render-homebrew.sh v0.1.0 /tmp/hb/SHA256SUMS \ "https://<GITLAB-HOST>/api/v4/projects/<PROJECT-PATH>/packages/generic/crucible/0.1.0" \ > ~/tap/Formula/crucible.rb
# In the tap repo:git -C ~/tap add Formula/crucible.rbgit -C ~/tap commit -m "crucible v0.1.0"git -C ~/tap pushMISSING in any sha field means that platform’s tarball wasn’t in
the SHA256SUMS you passed — usually because the darwin manual job
hasn’t run yet. Re-render once it has.
Hotfixing a broken release
Section titled “Hotfixing a broken release”If a tag pipeline fails mid-flight:
- Before publish-release — fix the underlying issue on main,
delete the bad tag (
git push origin :vX.Y.Z), re-runrelease.sh vX.Y.Z. - After publish-release — you have a live release. Bump the
patch version (
release.sh vX.Y.Z+1) and land the fix there. Don’t retag; consumers already fetched the broken artifacts.