The release panic
You've spent weeks on a crate. The tests pass. The docs look good. You're ready to share it with the world. You bump the version in Cargo.toml, commit the change, create a git tag, push to GitHub, and finally run cargo publish. Then you remember: you forgot to update the changelog. Or you tagged 0.1.0 instead of 0.1.1. Or worse, you pushed a breaking change to main before verifying the publish worked. Now you have to yank the crate, fix the repo, and hope no one noticed.
Don't let a forgotten tag ruin your release day.
Automating the checklist
cargo-release is a tool that turns this chaotic sequence into a single, safe command. Think of it like a vending machine that checks your coins, verifies the item is in stock, and only then drops the snack. If you forget a coin, or the item is stuck, it stops. It doesn't just blindly execute steps; it enforces a workflow. You tell it what version you want, and it handles the version bump, the commit, the tag, the push, and the publish, in the right order, with checks along the way.
Trust the workflow. It remembers what you forget.
Getting started
cargo-release is not part of the standard cargo binary. You install it as a separate tool.
# Install the binary. This puts cargo-release in your cargo bin directory.
cargo install cargo-release
Once installed, you can use cargo release just like cargo build. The tool reads your Cargo.toml and git configuration to understand your project structure.
The basic workflow
The safest way to use cargo-release is to start with a dry run.
# Simulate the release. This checks dependencies and shows commands without changes.
cargo release --dry-run
This command simulates the entire release process. It checks your dependencies, verifies the version bump, and shows you the commands it would run. It does not modify your files or push anything. Use this to verify your setup before committing to the release.
Once you're satisfied, run the real thing.
# Execute the release. This bumps version, commits, tags, pushes, and publishes.
cargo release --execute
This executes the workflow. It bumps the version, commits the changes, creates a tag, pushes to the remote, and publishes to Crates.io.
What happens under the hood
When you run cargo release, the tool follows a strict sequence.
- Version bump: It updates the
versionfield inCargo.toml. If you have a workspace, it handles all crates or just the ones you specify. - Commit: It creates a commit with the updated
Cargo.tomlfiles. The message follows a convention, usuallyRelease <version>. - Tag: It creates a git tag for the new version.
- Push: It pushes the commit and tag to the remote repository.
- Publish: It runs
cargo publishto upload the crate to Crates.io.
This order matters. You want to push the tag before publishing so that the published version matches a tag in your repository. If you publish first, you might have a version on Crates.io that doesn't exist in your git history.
Real-world scenarios
Real projects are rarely a single crate. You might have a workspace with multiple crates, or you need to bump a dependency version along with your crate.
# Cargo.toml
[package]
name = "my-lib"
version = "0.1.0"
edition = "2021"
[dependencies]
my-dep = { path = "../my-dep", version = "0.1.0" }
If you run cargo release here, cargo-release will bump my-lib to 0.1.1. It will also check my-dep. If my-dep is also in the workspace, you might want to bump it too.
# Bump both crates. cargo-release resolves the graph and bumps my-dep first.
cargo release --execute my-dep my-lib
This command bumps both crates. The order matters. cargo-release resolves the dependency graph and bumps my-dep first, then my-lib. This ensures that my-lib depends on the new version of my-dep.
You can also specify the version explicitly.
# Force a specific version. Skips automatic semver calculation.
cargo release --execute --version 0.2.0
This forces the version to 0.2.0, skipping the automatic semver calculation. Use this when you need a major version jump or a pre-release version that the tool wouldn't guess.
Configuration and hooks
You can configure cargo-release in Cargo.toml. This is where you define dependencies, pre-release hooks, and version strategy.
# Cargo.toml
[package.metadata.release]
# Customize the commit message. {version} is replaced with the new version.
pre-release-commit-message = "Release {version}"
tag-message = "Release {version}"
# Define dependencies. my-dep must be released before this crate.
dependencies = ["my-dep"]
This allows you to customize the commit messages. You can also define dependencies between crates in a workspace. The dependencies field tells cargo-release that my-dep must be released before this crate.
Some projects use a separate release.toml file for configuration. This keeps Cargo.toml clean. cargo-release supports both. The convention is to use Cargo.toml for simple setups and release.toml for complex workspaces.
You can also hook scripts into the workflow. cargo-release supports pre-release and post-release hooks. This is useful for generating changelogs or running extra checks.
# Cargo.toml
[package.metadata.release]
# Run a script before the release. Useful for changelog generation.
pre-release-hook = ["./scripts/generate-changelog.sh", "{version}"]
This runs your script with the new version as an argument before the release proceeds. If the script fails, the release stops.
Common pitfalls
cargo-release stops if your working tree is dirty. You cannot have uncommitted changes. This is a safety feature. If you have uncommitted changes, the tool refuses to run. You must commit or stash your changes first.
$ cargo release
error: dirty working tree
This error saves you from publishing a version that doesn't match your git history. Fix the dirty tree and try again.
Another common issue is the publish token. cargo-release uses the same token as cargo publish. If you haven't logged in, the publish step will fail.
$ cargo release
...
error: no API token found, run `cargo login`
Run cargo login with your Crates.io API token to fix this.
Workspace pitfalls are also common. If you run cargo release in a workspace root without arguments, it might try to publish all crates. If some crates are private or not ready, this is bad. Use the --exclude flag or specify crates explicitly.
# Exclude a private crate from the release.
cargo release --execute --exclude private-crate
A dirty tree is a warning sign. Clean it up before you ship.
Community conventions
The community convention is to always run --dry-run before --execute. Even if you've done this a hundred times, the dry run catches configuration drift. Your Cargo.toml might have changed, or a dependency might have a new requirement. The dry run verifies the current state.
Another convention is using --execute explicitly. Some users alias cargo release to cargo release --dry-run. This prevents accidental publishes. When you're ready, you type the longer command. It's a friction mechanism that stops mistakes.
# .cargo/config.toml
[alias]
# Make cargo release safe by default. Requires --execute to publish.
release = "release --dry-run"
This alias makes cargo release safe by default. You have to type cargo release --execute to actually publish.
Sometimes you want to bump and tag but not publish. Maybe you're releasing to a private registry, or you want to review the tag first.
# Bump, commit, tag, and push. Stop before publishing.
cargo release --execute --no-publish
This runs the workflow up to the publish step, then stops. You get the version bump, commit, tag, and push. The crate stays unpublished.
When to use cargo-release
Use cargo-release when you need a local, automated workflow that handles versioning, tagging, and publishing in one step. It's ideal for solo developers or small teams who want consistency without setting up complex CI pipelines.
Use cargo publish directly when you only need to upload a crate and don't care about version bumps or git tags. This is useful for quick updates or when your versioning is managed elsewhere.
Use a CI/CD pipeline when you have a large team, strict release policies, or need to publish from a protected environment. CI tools can enforce checks, run tests on multiple platforms, and publish using secrets that never touch a developer's machine.
Use manual version bumps when you have complex release logic that cargo-release can't handle. Some projects have custom versioning schemes or need to update files outside of Cargo.toml. In these cases, a script or manual process gives you full control.
Automate the boring stuff, but keep the keys to the kingdom safe.