Skip to content

Cutting a release

Everything in one place so the release ritual doesn’t grow tribal knowledge.

  • Push access to main on agentstategroup/agentstatecrucible.
  • CI variable GITLAB_RELEASE_TOKEN set (personal access token or project access token with write_repository) — used by the tag push step if you script it, and available to publish-release.sh as a fallback when CI_JOB_TOKEN is scoped out.
  • (Optional) A macOS runner registered with the tag macos so the release-binary-darwin job can build Apple binaries. Until then, darwin binaries are missing from the release and the Homebrew formula sits with MISSING in the SHA fields for macOS targets.
Terminal window
# On a clean main, at the commit you want to ship:
bash scripts/release.sh v0.1.0
git push origin main v0.1.0

That’s the whole ceremony. The tag pipeline takes over from there:

  1. check — fmt, clippy, build, version-guard (all versions == tag)
  2. test — cargo test
  3. build-binary — cross-compile linux musl x86_64 / aarch64 / windows via cargo-zigbuild from a rust:1.83 image. Uploads dist/*.tar.gz, dist/*.zip, dist/SHA256SUMS as job artifacts.
  4. build-imagedocker buildx pushes $CI_REGISTRY_IMAGE:X.Y.Z and :latest.
  5. releasescripts/publish-release.sh uploads each tarball to the project’s Generic Package Registry and creates a GitLab Release object linking to them.
  • Verifies the working tree is clean.
  • Verifies the tag matches semver.
  • Stamps the workspace Cargo.toml [workspace.package].version.
  • Stamps web/package.json and updates web/package-lock.json.
  • Runs cargo update --workspace so Cargo.lock reflects the new workspace version.
  • Commits everything as release: vX.Y.Z.
  • Tags vX.Y.Z on that commit.
  • Does not push. You push. On purpose — gives you one last chance to inspect git show HEAD and git show vX.Y.Z before 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).

Whenever a release completes, render the formula from that release’s SHA256SUMS and commit it to the tap:

Terminal window
# Grab SHA256SUMS from the release (once uploaded):
mkdir -p /tmp/hb
curl -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.rb
git -C ~/tap commit -m "crucible v0.1.0"
git -C ~/tap push

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

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-run release.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.