Publishing runbook
This chapter is the maintainer-only reference. It covers the publish-to-crates.io procedure: the pre-flight checklist, the dependency topological order, the dry-run, the actual publish, and the rollback procedure if something goes wrong.
The audience is the maintainer cutting a release. An adopter does not need this chapter; the chapter is here so the maintainer has a written reference and so the procedure can be followed by a different maintainer if needed.
Pre-flight
The pre-flight checklist runs before the first dry-run. Each item is a binary pass-or-fail; one failure blocks the release.
The CI is green on the release branch. The full test matrix (default features, all-features, per-backend isolation, FIPS backend) all pass. A red CI does not publish.
The version bumps are consistent across the workspace. Every
member of the workspace gets the same version bump (this is the
versioning policy: the workspace ships as one unit). The
Cargo.toml in each crate carries the new version; the
version.workspace = true shape inherits from the root.
The Cargo.lock is up to date. Run cargo update --workspace,
review the changes, commit if needed.
The migration guide is complete. The Migration guide chapter in the book carries an entry for every breaking change in the release. The entry covers the symptom, the rationale, and the fix.
The CHANGELOG.md has a current entry for the release. The entry covers the new features, the breaking changes (referencing the migration guide), and the bug fixes. The entry is the short-form version of the migration guide; both exist because they serve different audiences (the CHANGELOG is the per-release overview, the migration guide is the per-change reference).
The docs.rs configuration is present and correct on every crate that publishes to crates.io. The shape:
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
Verify by running cargo doc --all-features locally; the build
must succeed without warnings. A docs build that fails on
docs.rs after publish is a maintenance problem that surfaces
once the release is out.
The description, license, repository, keywords, and
categories fields are populated on every published crate. The
crates.io page renders these; a missing field is a missing
detail in the listing.
The publish = false flag is removed from every crate that
should publish. This is the deliberate gate that keeps
accidental publishes from happening; flipping the flag is what
makes the release possible.
The version branch (a fresh release/0.2.0 branch from main)
exists. The branch is the source of truth for the release; any
fixes during the publish window land on the branch and merge
back to main after.
Topological dependency order
The workspace's library crates publish in dependency order: a crate must be published before any crate that depends on it. The order:
axess-strings(no axess deps)axess-clock(no axess deps)axess-rng(no axess deps)axess-identity(no axess deps)axess-cache(depends onaxess-clock)axess-events(depends onaxess-identity)axess-factors(depends onaxess-identity,axess-clock,axess-rng)axess-macros(no axess deps; procedural macros stand alone)axess-core(depends on everything in the previous tier)axess(the facade, depends onaxess-coreandaxess-factorsandaxess-macros)
The order is generated by cargo publish --dry-run against each
crate in turn, but the maintainer should know it manually so a
publish that fails partway through can be resumed at the right
position.
The dry-run
Before the actual publish, run a dry-run for each crate in topological order. The shape:
cargo publish --dry-run -p axess-strings
cargo publish --dry-run -p axess-clock
cargo publish --dry-run -p axess-rng
cargo publish --dry-run -p axess-identity
cargo publish --dry-run -p axess-cache
cargo publish --dry-run -p axess-events
cargo publish --dry-run -p axess-factors
cargo publish --dry-run -p axess-macros
cargo publish --dry-run -p axess-core
cargo publish --dry-run -p axess
The dry-run does everything the publish does except the upload.
It builds the package, verifies the manifest, runs the publish-time
checks, and prints the path of the .crate file it would have
uploaded. A failure here is an opportunity to fix without having
to yank a half-published release.
A failure on a downstream crate (say axess-core) typically
means an upstream crate (say axess-factors) needs an update
that the dry-run does not yet reflect. The fix is to update the
upstream first; the downstream dry-run picks up the change.
The publish
After the dry-runs pass, run the actual publish in the same topological order. The shape:
cargo publish -p axess-strings
# wait ~30s for crates.io to index, then:
cargo publish -p axess-clock
# wait, then:
cargo publish -p axess-rng
# ... and so on
The wait between publishes is necessary because each subsequent publish needs the previous one to be available on crates.io's index. Without the wait, the downstream publish fails with "crate not found"; with the wait, the index has propagated by the time the next publish queries it.
The wait time is short (30 seconds is generous; sometimes 15 seconds works). For automation, the wait can be scripted with a retry loop that polls the crates.io API until the expected version is listed.
After the final publish (cargo publish -p axess), wait a few
minutes and verify on crates.io that all the crates are listed
at the new version.
The smoke test
After the publish, run a smoke test against a fresh dependency on the published version. The shape:
mkdir /tmp/axess-smoke
cd /tmp/axess-smoke
cargo new --name axess-smoke .
echo 'axess = "0.2"' >> Cargo.toml
cargo build
The build pulls the published crates from crates.io (not from the workspace) and verifies they assemble. A failure here indicates an issue that the dry-run did not catch (typically a crates.io-specific issue like a missing file in the package manifest); the rollback procedure is the response.
For a more thorough smoke test, copy examples/sqlite/ to a
fresh directory, point its Cargo.toml at the published
versions (replacing path = "../../axess" with
version = "0.2"), and verify it builds and runs.
The smoke test is the last gate before announcing the release. A successful smoke test means the publish is real.
The announcement
After the smoke test passes:
Tag the release in git (git tag v0.2.0 && git push --tags).
The tag is the canonical reference point.
Update the status banner near the top of
README.md
from "pre-release" to "0.2.0 released."
Open a 0.3.0-pending section in CHANGELOG.md. Future PRs
land entries under that section until the next release cuts.
Post to the project's announcement channels (the GitHub releases page is the canonical one; the project's Discord, Slack, mailing list, or other channels mirror as appropriate).
Update the docs.rs links anywhere they hardcode a version. The canonical version is now the released one, not the development branch.
Rollback
If the publish goes wrong (a critical bug surfaces, a crate is broken on crates.io, the release was premature), the rollback procedure:
cargo yank --version 0.2.0 axess (and every other crate)
withdraws the version from crates.io. Yanked versions remain
available to existing consumers (so Cargo.lock references
continue to work), but new resolves do not pick them up.
A yanked version cannot be unyanked, and a new publish with the
same version number is not possible. The next release picks a
new version (0.2.1); the fix lands there.
In practice the rollback is needed less often than the dry-run suggests; the topological-publish discipline catches most issues before the upload. A yank is the genuine emergency response, typically for a security issue that warrants withdrawing a specific version.
Post-release maintenance
After the release lands, the maintenance window is the period where the maintainer watches for issues. The shape:
The first 24 hours: monitor crates.io for download numbers (a quick check that the publish reached an audience); monitor the GitHub issue tracker for new bug reports; monitor the dashboards of any deployments that follow main closely.
The first week: triage the issues that come in; assess whether any warrant a patch release (0.2.1). The criteria for a patch release: a critical bug, a security issue, a regression from 0.1.x that the migration guide did not catch.
The first month: roll up the lessons learned. The patches shipped, the issues that surfaced, the documentation gaps the release exposed. The roll-up feeds the next release's planning.
Further reading
Migration guide covers the cross-version compatibility surface
that the release-management decisions depend on. Contributing
covers the development workflow that produces the changes a
release ships. The
CHANGELOG.md
covers the exhaustive list of changes per release.