Release process
How to cut a grex release. Covers the GitHub Release (binaries via
cargo-dist) and the crates.io publish steps. Rollback procedure at the end.
Audience: maintainers. Users should install per README.md §Install.
Prerequisites
- Push access to
mainand tag-push rights onegoisth777/grex. - A crates.io API token with publish rights on
grex-cli,grex-core,grex-mcp,grex-plugins-builtin(cargo loginon your workstation). cargo-distinstalled locally at the pinned version matching[workspace.metadata.dist].cargo-dist-version(currently0.31.0) — only required if you want to re-rundist planbefore tagging.- Clean
git status; working tree must match the exact commit you are releasing. No un-committed changes.
1. Prepare the CHANGELOG
In CHANGELOG.md:
- Rename the
[Unreleased - 1.0.0]heading to[1.0.0] - YYYY-MM-DDusing today's UTC date. - Open a new empty
[Unreleased]section above it with emptyAdded/Changed/Fixed/Removedsubsections. - Ensure every
Addedbullet references the PR that introduced it. - Commit:
git commit -am "chore(release): prepare v1.0.0". - Push to
mainvia the normal PR flow. Do NOT tag yet.
2. Tag and push
Once the chore(release): prepare v1.0.0 commit is on main:
git switch main
git pull --ff-only
git tag -a v1.0.0 -m "grex v1.0.0"
git push origin v1.0.0
The tag push triggers .github/workflows/release.yml:
plan— validatesdist-manifest.jsonagainst the 5 targets.build-local-artifacts× 5 — buildsgrexfor each target, signs artefacts via GitHub's native attestations (actions/attest-build-provenance).build-global-artifacts— produces theinstaller.sh+installer.ps1scripts and SHA-256 sums.host+announce— creates the GitHub Release and uploads all artefacts (.tar.xz/.zip/*.sha256/ installers /source.tar.gz).
The GitHub Release body is auto-extracted from the [1.0.0] section of
CHANGELOG.md (cargo-dist convention).
3. Publish to crates.io (manual)
cargo-dist does NOT publish to crates.io. Do this manually from a
checkout of the tagged commit, in strict topological order. Prefer
--wait-for-publish (cargo 1.66+) over a hand-timed sleep — it polls
the index and only exits once the crate is actually resolvable:
git switch --detach v1.0.0
cargo publish --wait-for-publish --timeout 300 -p grex-core
cargo publish --wait-for-publish --timeout 300 -p grex-plugins-builtin
cargo publish --wait-for-publish --timeout 300 -p grex-mcp
cargo publish --wait-for-publish --timeout 300 -p grex-cli
Order rationale: grex-plugins-builtin and grex-mcp both depend on
grex-core; grex-cli depends on all three. See
openspec/changes/feat-m8-release/crates-io-names.md
§2 for the dep graph.
Smoke test post-publish:
cargo install grex-cli --locked
grex --version # must print 1.0.0
4. Installer smoke tests
From a fresh shell session:
# Linux / macOS
curl -LsSf https://github.com/egoisth777/grex/releases/latest/download/grex-cli-installer.sh | sh
grex --version
# Windows
powershell -c "irm https://github.com/egoisth777/grex/releases/latest/download/grex-cli-installer.ps1 | iex"
grex --version
Verified install (recommended for security-sensitive environments)
Every artefact is signed via GitHub's native build provenance
(actions/attest-build-provenance). Users can verify the binary matches
the commit + workflow that produced it before trusting it:
# Download + verify attestation (requires gh CLI >= 2.49)
gh release download v1.0.0 --repo egoisth777/grex --pattern '*.tar.xz'
gh attestation verify grex-cli-x86_64-unknown-linux-gnu.tar.xz --repo egoisth777/grex
tar xf grex-cli-x86_64-unknown-linux-gnu.tar.xz
sudo mv grex-cli*/grex /usr/local/bin/
grex --version
The curl | sh / irm | iex one-liners above are a convenience path
and do NOT verify attestations.
Supported platforms
Pre-built binaries ship for these five triples (see
[workspace.metadata.dist].targets in root Cargo.toml):
| Triple | Runner |
|---|---|
x86_64-unknown-linux-gnu | ubuntu-22.04 |
aarch64-unknown-linux-gnu | ubuntu-22.04-arm |
x86_64-apple-darwin | macos-13 |
aarch64-apple-darwin | macos-14 |
x86_64-pc-windows-msvc | windows-2022 |
Everything else (32-bit, musl, FreeBSD, aarch64-windows, etc.) falls back to building from source:
cargo install grex-cli --locked
Rollback
Yank a bad crates.io release
cargo yank hides the version from the resolver without deleting it.
Yank in reverse-dependency order (bin first, so dependents cannot keep
pulling it in):
cargo yank --version 1.0.0 grex-cli
cargo yank --version 1.0.0 grex-mcp
cargo yank --version 1.0.0 grex-plugins-builtin
cargo yank --version 1.0.0 grex-core
Yanking is reversible: cargo yank --version 1.0.0 --undo <crate> if
you decide to keep the release after all.
Mark the GitHub Release as pre-release
gh release edit v1.0.0 --prerelease
This hides it from the "latest" installer URL without deleting the artefacts. Users on the installer one-liner will stop picking up the bad release automatically.
Ship a fix
Cut a fresh patch release (v1.0.1) with the fix — do not re-tag
v1.0.0. Re-tagging breaks provenance and every cached copy of the
installer script.
On the limits of rollback
cargo yankis notcargo delete. The crate file stays on crates.io forever; yanking only excludes it from new resolves. Code that pinned= 1.0.0in a lockfile keeps compiling. There is no delete API.- Sigstore attestations are immutable. A released artefact whose
build provenance is on the Sigstore transparency log cannot be
revoked —
gh attestation verifywill keep returningOKeven after you mark the release pre-release. - Compromised-binary rollback MUST use a patch bump (
v1.0.1) that supersedes the bad version. Yankv1.0.0, mark its GitHub Release pre-release, and pushv1.0.1through the same release pipeline. Do not attempt to re-tag or delete artefacts.
Pinning updates
To update the pinned cargo-dist version:
- Bump
[workspace.metadata.dist].cargo-dist-versionin rootCargo.toml. - Run
cargo install cargo-dist --locked --version <new>locally. - Run
dist generateto regenerate.github/workflows/release.yml. - Commit both files together. CI's
release-planjob verifies the manifest still parses.