Skip to content

Latest commit

 

History

History
124 lines (100 loc) · 5.26 KB

File metadata and controls

124 lines (100 loc) · 5.26 KB

Releasing launchbound

One page, copy-pasteable. Maintainers only. The same shape as the sibling projects' release docs — termlens, mossaic, reconverge — so a maintainer moving between them is not relearning the process.

Prerequisites

  • crates.io Trusted Publishing, linked to this repository and release.yml. No token is stored anywhere; the publish job mints a short-lived one over OIDC.
  • v*.*.* tags protected by a ruleset, so only a maintainer can push one.
  • Eight crates publish in dependency order, and cargo publish waits for each to appear on the index before the next. A rate-limited run can be resumed with workflow_dispatch, which skips crates already on the registry.

Cutting vX.Y.Z

# 0. Green main, and no flakes. The gate runs per PR; the hunt does not.
gh workflow run stress.yml -f iterations=100
gh run watch                    # ten shards, both OSes

# 1. Bump the version. `workspace.package.version` AND the internal
#    `version =` pins in [workspace.dependencies] -- both, or `just
#    versions` fails. They are not cosmetic: pins left behind a major
#    bump make `cargo metadata` refuse to resolve the workspace at all.
$EDITOR Cargo.toml              # version = "X.Y.Z", and the eight pins
cargo check --workspace         # refreshes Cargo.lock
just versions                   # the two agree

# 2. Move the CHANGELOG section: [Unreleased] -> [X.Y.Z] - YYYY-MM-DD,
#    leaving an empty [Unreleased] above it.

# 3. Bump every version the docs name. Two kinds go stale: the
#    `action@vN` refs people copy, and "pin a number" examples a reader
#    reasonably reads as current. This finds both:
grep -rEn "launchbound/action@v|[0-9]+\.[0-9]+\.[0-9]+" docs action README.md \
  | grep -v CHANGELOG

# 4. Land it.
git switch -c release/vX.Y.Z
git commit -sam "release: vX.Y.Z"
gh pr create --fill

# 5. Tag the squash-merged commit on main.
git switch main && git pull
git tag vX.Y.Z && git push origin vX.Y.Z

Pushing the tag runs release.yml, which gates, then publishes each crate in order via Trusted Publishing.

The floating major tag

release.yml moves vN to each release, so @v2 follows 2.x without a manual step. It is covered by the protect-release-tags ruleset only up to refs/tags/v*.*.* — release tags are immutable, floating pointers are not, because being moved is the whole of what a floating pointer is for.

That distinction was learned the hard way: the ruleset originally covered refs/tags/v*, so the job could not create v2 at all and 2.0.0's floating tag had to be pushed by hand.

After the tag

  • The GitHub Release is created by hand, from the CHANGELOG section: gh release create vX.Y.Z --title "launchbound X.Y.Z" --notes-file …. Every released version has one; do not skip it.

  • Verify what was published, not what was built. install.yml installs from crates.io into a clean directory and runs the binaries; dispatch it once the version is live:

    gh workflow run install.yml
  • Move the semver baseline, in a PR of its own after the publish. It is the --baseline-version literal in ci.yml's semver job, not a YAML input — grep baseline-version .github/workflows/ci.yml finds it. baseline-version in ci.yml's semver job is a literal. Left at the old release it compares every PR against a version nobody can install any more, and it would also carry this release's own breaks forward as if they were new. Moved before the publish it names a version that does not exist yet and the job cannot fetch it. So: publish, confirm the index has it, then bump the literal.

    $EDITOR .github/workflows/ci.yml   # --baseline-version X.Y.Z
  • A break needs the breaking label on its PR, which switches the semver job from patch to major. Without it the job fails, which is the point; with it, the release notes owe the reader a migration note.

What a version number means here

  • Breaking (minor pre-1.0, major after): a removed or renamed public item, a changed CLI flag, or a change to what the gate admits that a user would have to relearn.
  • Not breaking: new flags, new backends, a corpus addition, a report field.
  • MSRV and pinned-toolchain bumps are minor, never patch, and never land in the same change as a behaviour change.

If something fails mid-release

  • Before publish: fix, delete the tag (git push --delete origin vX.Y.Z), re-tag. Nothing was published; the world never saw it.

  • Part-way through the eight crates: re-run release.yml by dispatch, passing the tag:

    gh workflow run release.yml -f tag=vX.Y.Z

    It skips what is already on the registry, then moves vN. The tag is an input rather than inferred because a dispatch has no tag of its own, and a resumed release that skipped the floating tag is a release whose documented @vN still points at the previous version.

  • After publish: crates.io is immutable. Ship X.Y.Z+1. Yank only if the release is actively harmful — a yanked crate still breaks downstream lockfiles.