Releasing GPUI Box
This is the runbook for publishing and verifying a GPUI Box crates.io release.
Every cohort, including the initial 0.1.0 release, follows this process.
Cohort and authority
package-authority.toml defines every package name, version, cohort, license,
and publish flag. Framework packages are Apache-2.0; kit packages and
gpui-box-mcp are MIT. A release keeps mutually dependent publishable packages
in one compatible version cohort (currently 0.1.x) and records the contract
in compatibility.toml and provenance.toml.
Never hand-maintain publication order. Derive it from Cargo metadata:
cargo run -p xtask -- dependencies check
cargo run -p xtask -- package plan
Save the plan in the release log. Independent packages may appear early, and platform packages may appear after consumers; the generated order, not a prose list, is authoritative.
Preflight and dry run
-
Select the release commit; ensure versions and internal requirements form the intended cohort, changelog has the release date, machine-readable records are current, and the worktree is clean. For an initial release, confirm every package name is available; for later releases, confirm the expected crates.io owner set.
scripts/sync-zed/sync-zed verify --releasemust prove the complete filtered-history receipt and its ancestry through this commit; an unbootstrapped receipt cannot be released. -
Run the platform validation required by
compatibility.toml. Record actual CI job URLs/results; do not infer a platform result from another platform. -
Run the complete local package gate:
cargo run -p xtask -- gate full cargo run -p xtask -- package plan cargo run -p xtask -- package checkpackage checkrequirescargo-local-registry0.2.12. It packages every publishable crate, constructs a registry undertarget/package-check, then builds framework-only and framework-plus-kit consumers offline with source replacement and without path patches. It also runs a packagedgpui::property_test, installs the MCP binary and checks its help/version, rejects retained internal dev-dependencies that would deadlock a first publication, and requires exactly onelib gpuiowned bygpui-box. This proves registry-only resolution; it does not publish. -
Create and push the annotated immutable
v<version>tag on that exact verified commit. The workflow and publisher both refuse an untagged commit; do not move the tag after this point. -
Run the manual
release.ymlworkflow withexecute=false. It performs the full preflight, runs the applicable native-platform semantic-version checks, and uploads the exact archives and reproducible CycloneDX 1.5 SBOMs without obtaining a crates.io credential. Offline registry consumers are the publication proof; a per-crate dry-run cannot resolve not-yet-published cohort dependencies. The workflow validates the exact tag on all three native targets, checks all three renderer-specific headless baselines, runs the full macOS gate, and runs the WASM build, Chromium smoke, and browser visual baseline. Neither publisher job can start unless every one of those jobs and the SemVer matrix succeeds. Preflight peels the annotated tag once; every downstream checkout and the archive artifact name are bound to that exact commit SHA, so moving the tag later can only fail publication, never switch the commit being validated.
Publish
Only the protected release workflow may execute publication:
GPUI_BOX_PUBLISH=1 cargo run -p xtask -- package publish --execute
It refuses other arguments, a missing opt-in, a dirty worktree, a HEAD not
pointed to by an annotated v<authority version> tag, or missing package check archives. Before each upload it independently regenerates that package
and requires its SHA-256 to equal the downloaded preflight archive. It checks
crates.io download bytes and the unyanked sparse-index entry before and after
each publish, and resumes only when the bytes equal that same archive and the
exact version is visible for dependency resolution. Cargo's --no-verify is
used only because the workflow already ran the complete offline package check.
Publish each package in the exact package plan order. After each publish,
wait until the new crate/version is visible in the crates.io
index before publishing a dependent. Index propagation is asynchronous:
poll with bounded retries and backoff, and retry only the dependent publish or
index lookup. A response saying that the exact version already exists is
success after verifying its checksum/metadata; never attempt to overwrite it.
Pause on any other error. Determine whether crates.io accepted the upload before retrying. Publication is immutable and a partially published cohort is better documented and resumed than guessed at.
Post-publication acceptance
From a clean environment with no workspace patches, Git sources, or path dependencies, create consumers using only crates.io:
[dependencies]
gpui = { package = "gpui-box", version = "=0.1.0" }
gpui_kit = { package = "gpui-box-kit", version = "=0.1.0" }
Build the framework-only and framework-plus-kit smoke workspaces from the
registry on each claimed target. Install and start
gpui-box-mcp --version against a checkout and require its output to report
gpui-box-mcp 0.1.0. Archive commands and results.
Only after these pass:
- create the GitHub release at https://github.com/fran0220/gpui-box, linking crates.io packages, compatibility/provenance records, and platform evidence.
Never move, delete, or reuse a release tag, and never overwrite a version.
External crates.io setup
Before the first release, verify ownership for every publishable package.
The first publication of each crate requires a long-lived, least-privilege
token in the protected crates-io environment: crates.io cannot configure
trusted publishing until the crate exists. Later releases may use the official
OIDC trusted-publisher action. Require protected branch/tag rules, environment
approval, and auditable owners. These are crates.io/GitHub settings outside
this repository; their presence must be checked, not assumed from this
document.
There is no crates.io baseline for the first version, so the release workflow
explicitly skips semantic-version comparison only for 0.1.0. Every later
release runs pinned cargo-semver-checks against the latest applicable
published crates.io baseline on Linux, macOS, and Windows before either
publisher job can start. Workspace selection checks every publishable
library-like package while excluding examples, galleries, and other
publish = false packages; the MCP binary remains covered by its packaged
install, --help, and --version acceptance tests rather than a Rust library
API comparison.
Failure, yank, and recovery
Cargo releases cannot be rolled back. If a published package is unusable:
- stop the cohort and document exactly which versions were published;
- yank only affected versions when continued selection would harm users;
- do not delete or move a tag and do not unyank merely to reuse a version;
- fix forward with a new patch version for the whole affected cohort, rerun all gates and registry-only smoke tests, then publish by a newly generated plan;
- explain the yank and replacement in the changelog and GitHub release notes.
A yank prevents new resolution but does not remove source or break existing lockfiles. Security incidents additionally follow the repository's disclosure process; do not publish secrets in release logs.