| name | cargo-rail |
| description | Rust workspace monorepo orchestration. Use when working with cargo-rail for dependency unification, unused dep detection, dead feature pruning, MSRV computation, affected crate testing, crate extraction, or release orchestration. |
cargo-rail
Graph-aware monorepo orchestration for Rust workspaces.
What It Replaces
| Problem | Before | After |
|---|
| Build graph drift | cargo-hakari, workspace-hack crates | cargo rail unify |
| Unused deps | cargo-udeps, cargo-machete, cargo-shear | cargo rail unify |
| Dead features | cargo-features-manager, manual audit | cargo rail unify |
| MSRV computation | cargo-msrv, compile-and-fail loops | cargo rail unify |
| CI waste | paths-filter + shell scripts | cargo rail affected |
| CI costs | Test everything, bill for everything | Test what changed |
| Crate extraction | git subtree, git-filter-repo, Google's Copybara | cargo rail split |
| Release orchestration | release-plz, cargo-release, git-cliff | cargo rail release |
11 dependencies. One config file.
MSRV policy
- MSRV source of truth:
Cargo.toml (rust-version, written as major.minor.patch)
- Cargo requirement: Cargo shipped with that Rust release (newer Cargo is fine)
- CI: builds on MSRV to prevent accidental bumps
- Workspaces:
cargo rail unify writes [workspace.package].rust-version; enable [unify].enforce_msrv_inheritance = true to set [package].rust-version = { workspace = true } in member crates
Quick Start
cargo rail init
cargo rail unify --check
cargo rail unify
Commands
affected / test
Graph-aware change detection. Only test what's affected:
cargo rail affected
cargo rail affected --merge-base
cargo rail affected -f cargo-args
cargo rail affected -f github-matrix
cargo rail test
cargo rail test --explain
CI Integration:
- uses: loadingalias/cargo-rail-action@v1
id: rail
- run: cargo nextest run ${{ steps.rail.outputs.cargo-args }}
if: steps.rail.outputs.should-test == 'true'
unify
Dependency unification based on Cargo's resolved output:
cargo rail unify --check
cargo rail unify
cargo rail unify --explain
cargo rail unify undo
What it does:
- Unifies versions — writes to
[workspace.dependencies], converts members to workspace = true
- Prunes dead features — removes features never enabled in the resolved graph
- Fixes undeclared features — adds missing feature declarations to member manifests
- Detects unused deps — flags dependencies not used anywhere (auto-removes on apply)
- Computes MSRV — derives minimum Rust version from dependency graph
- Pins transitives — replaces
cargo-hakari without a workspace-hack crate
Multi-target aware: runs cargo metadata per target triple in parallel, computes feature intersections not unions.
split / sync
Extract crates with full git history. Bidirectional sync with 3-way conflict resolution:
cargo rail split init crate/s
cargo rail split run crate/s
cargo rail split run crate/s --check
cargo rail sync crate/s
cargo rail sync crate/s --to-remote
cargo rail sync crate/s --from-remote
Three modes:
single — one crate → one repo (most common)
combined — multiple crates → one repo (shared utilities)
workspace — multiple crates → workspace structure (mirrors monorepo)
Safety: refuses dirty worktree by default. --allow-dirty to override, --yes for CI.
release
Dependency-order publishing with changelog generation:
cargo rail release check crate/s
cargo rail release run crate/s --bump minor
cargo rail release run crate/s --check
Safety: detects default branch, refuses detached HEAD, warns on non-default branch.
config
Manage configuration:
cargo rail init
cargo rail config locate
cargo rail config print
cargo rail config validate
cargo rail config sync
Configuration
Generated by cargo rail init at .config/rail.toml:
targets = ["x86_64-unknown-linux-gnu", "aarch64-apple-darwin"]
[unify]
pin_transitives = false
detect_unused = true
prune_dead_features = true
msrv = true
msrv_source = "max"
enforce_msrv_inheritance = false
[release]
tag_format = "{crate}-{prefix}{version}"
publish_delay = 5
[change-detection]
infrastructure = [".github/**", "scripts/**", "*.sh"]
See full reference.
Notes
- vs cargo-hakari: No workspace-hack crate. Writes directly to
[workspace.dependencies]. Use pin_transitives = true for equivalent behavior.
- Workspace inheritance: Yes. Converts members to
{ workspace = true }.
- Virtual workspaces: Supported. Auto-selects transitive host or configure explicitly.
- Private registries: Works via
cargo metadata, respects .cargo/config.toml.
- Undeclared features: Detects when crates "borrow" features from siblings (works in workspace, breaks on publish). Auto-fixes by adding missing declarations.
References