# Releasing A checklist, not an explanation. The reasoning is `context/code/tooling.md` §4; read it if a step surprises you, not before. Its own file rather than a `README.md` section because `README.md` §Installation addresses someone *installing* improvise and this addresses the one person *publishing* it — folding them together lengthens the user-facing document for no user benefit. ## The one rule **Bump the version and commit it BEFORE you tag.** `dist` matches the tag against the root package's version and refuses when they disagree — and by the time CI tells you, the tag is already published and a published tag is not moved. Every step below exists to put that check before the irreversible one. ## Cutting a release 1. **Bump the version.** `Cargo.toml`'s `[package]` version is the one `dist` announces (`dist-workspace.toml` says `members = ["cargo:."]`). Keep the crates under `crates/` in step with it; `gp-tree` sits at its own version deliberately, and the preflight lists such crates as a note rather than an error. 2. **Commit the bump**, and let `Cargo.lock` be part of that commit. 3. **Run the preflight**, with the tag you intend to push: ```sh scripts/release-preflight.sh vX.Y.Z ``` It refuses a dirty tree, refuses a tag that does not match the version, runs `dist plan` as a second opinion when `dist` is on `PATH`, and prints the tag commands only once it has passed. It cuts nothing itself. 4. **Tag and push**, using the commands it printed: ```sh git tag -a vX.Y.Z -m "vX.Y.Z" git push gh vX.Y.Z ``` Push the tag to **`gh`** — `.github/workflows/release.yml` triggers on a tag push there, matching `**[0-9]+.[0-9]+.[0-9]+*`. `origin` and `gitea` are mirrors and build nothing. 5. **Watch the run.** A green release takes about three minutes and publishes tarballs for all four targets with their `.sha256` files, `sha256.sum`, `dist-manifest.json`, `source.tar.gz` and `improvise-installer.sh`. The failure signature is unmistakable once you know it: **`plan` fails in about twenty seconds and the four jobs behind it skip.** That is the tag not matching the version — see below. A run that gets past `plan` is building. ## When a tag has already been pushed against the wrong version **Leave it dead and cut the next one.** Do not move or re-push the tag: it is already public, and a published tag that changes meaning is worse than one that never built. Bump to the next version, commit, run the preflight, and tag that. The worked example is `v0.1.0-rc3`: it was pushed while every crate still said `0.1.0-rc2`, `dist` refused, and the four jobs behind `plan` skipped. No release was ever built from it. **Ask which tags exist; do not trust a document that tells you.** `git tag -l`, and `git ls-remote --tags ` for each remote you push to. Which tags are where is a fact with a shelf life — it changed twice while this file was being written — so this file deliberately records no inventory of it. Cleaning up a dead tag is a separate decision and is not a step in cutting a release.