cargo-rail
A deterministic, cargo-native control plane for Rust monorepos: build/check/test/bench only what changes locally and in CI, unify the graph (deps/features), split/sync crates into new repos/monorepos, and automate releases with 14 core dependencies.
Why
Documented impact on real repos (tokio, helix, meilisearch):
| Metric | Impact |
|---|---|
| CI Surface Execution | 55% fewer surfaces run per merge |
Weighted test/build Units |
64% reduction in compute units |
| Dependencies Unified | 96 across 53 crates (avg 1.8 per crate) |
Undeclared features Fixed |
258 silent bugs prevented |
| Tooling Consolidation | 6-8 cargo plugins → 1 command |
| MSRV Computation | Automatic from dependency graph |
Compounding effects = massive time/cost savings:
- Change Detection (
plan/run) reduces what runs — 55% fewer CI surfaces, 64% fewer compute units w/ shown determinism - Dependency Unification (
unify) reduces build graph complexity — cleaner deps, smaller build units, fewer rebuilds - Smaller Build Graphs cargo-rail uses 14 core-deps for automatically removing unused dependencies, pruning truly dead features, unifying undeclared features, computing MSRV, splitting and synching crate/s to new, clean repos, and the entire release workflow w/ changelog generation. This results in fewer tools and less cargo-metadata fetches.
Before cargo-rail: Run 6 tools separately (hakari and/or workspace-hack, udeps, machete, shear, features-manager, msrv, sort, and more), each with different data/timing
After cargo-rail: cargo rail unify --check in one metadata call
Quick Start
# install
# generate config
# dependency hygiene
# deterministic planning + execution
Pre-built binaries: GitHub Releases
Workflows
Change Planning (detection) + Execution (plan / run)
Problem: CICD wastes resources testing unchanged code. We either (1) test everything on every commit, or (2) build custom scripts that drift from local behavior.
Solution: One planner contract, used everywhere:
# Local: what would CI run if I pushed this branch?
# CI: deterministic plan → selective execution
How to use this effectively:
- Configure change detection rules in
.config/rail.toml(infrastructure files, doc-only changes, custom, etc.) - Run
planto see impact classification (which surfaces: build, test, bench, docs, infra, custom) - Run
runto execute only selected surfaces — locally or in CI - or wire tojustfile,makefile,xtask, or shell scripts. - Use
--explainto understand any decision: "why did this run?" / "why was this skipped?"
Result: 55% fewer CI surface executions, 64% reduction in weighted compute units (validated on tokio/helix/meilisearch) See: Examples.
Dependency Unification (unify)
Problem: We all juggle 6+ cargo plugins for dependency hygiene (hakari, udeps, machete, shear, features-manager, msrv, sort, etc.). Each runs separately, on different data, with different CLI patterns... pulling the same cargo-metadata over and over. Undeclared features (borrowed from Cargo's resolver) break isolated builds silently.
Solution: cargo rail unify — one command, one metadata call, comprehensive analysis:
What it does:
- Unifies versions — writes to
[workspace.dependencies], converts members to{ workspace = true } - Fixes undeclared features — detects features borrowed via Cargo's unified resolution
- Prunes dead features — removes features never enabled in resolved graph
- Detects unused deps — flags dependencies not used anywhere
- Computes MSRV — derives minimum Rust version from dependency graph
- Replaces workspace-hack — enable
pin_transitivesfor cargo-hakari equivalent - Configurable - we all have different ideas about what clean means, such as whether to remove unused dependencies or prune features or sort manifests - that's what your
rail.tomlfile is for.
Validated impact on real repos:
| Repository | Crates | Deps Unified | Undeclared Features | MSRV Computed |
|---|---|---|---|---|
| tokio-rs/tokio | 10 | 9 | 7 | 1.85.0 |
| helix-editor/helix | 14 | 15 | 19 | 1.87.0 |
| meilisearch/meilisearch | 23 | 54 | 215 | 1.88.0 |
| helixdb/helix-db | 6 | 18 | 17 | 1.88.0 |
| Aggregate | 53 | 96 | 258 | — |
Config files and validation artifacts: Examples | Validation Forks
Split + Sync (Google Copybara Replacement)
Problem: We need/want to publish crates from monorepos but want clean standalone repos with full git history. I wanted to build in a canonical dev monorepo, but release crates independently. Google'sCopybara requires so much and it's built w/ Java. Existing tools (git subtree, git-filter-repo) are one-way and manual. This offers us a bidirectional sync engine w/ 3-way merge conflict resolution in the event we need it; it never merges to main w/o a review PR for the canonical repo.
Solution: cargo rail split + cargo rail sync — bidirectional sync with 3-way conflict resolution:
# Extract crate to standalone repo with full git history
# Bidirectional sync
Three modes:
single: one crate → one repo (most common)combined: multiple crates → one repo (shared utilities)workspace: multiple crates → workspace structure (mirrors monorepo)
Built on system git (not libgit2) for deterministic SHAs and full git fidelity with less attack surface/weight in the graph.
Release Automation (release)
Release checks, versioning, changelogs, tags, dependency-order publish. Release_plz is great, but it's pulling in something like 500 deps to release our work. That's too much weight to carry around in the graph and too much attack surface in my world. I release cargo-rail with 1cargo-rail.
# Cut a new release
This gives me a clean changelog, tags, crates.io / Github release.
GitHub Actions Integration
For CICD integration, use cargo-rail-action — a thin transport over cargo rail plan -f github that handles installation, checksum verification, and output publishing for job gating. It will make output cleaner and more readable.
The action keeps CI behavior aligned with local plan + run workflows.
Config
cargo rail initgenerates.config/rail.toml.cargo rail config syncupdates.config/rail.tomlwith latest defaults from new releases installed.cargo rail config validatevalidates.config/rail.tomlfor when you're unsure.
By default, on cargo rail init, the rail.toml file is written to the .config/ directory. It's created if it doesn't exist. However, you can move/change it to the workspace root if you prefer it there. You can also 'unhide' the file if you'd like. Reference
Full documentation:
- Configuration Reference
- Command Reference
- Architecture
- Change Detection Recipe
- Change Detection Operations Guide
- How to Use Change Detection Effectively
- Troubleshooting
Migration Guides
Tested & Proven On Large Repos
All core workflows (plan/run, unify) validated on production repos with full git history:
| Repository | Crates | Validation | Fork |
|---|---|---|---|
| tokio-rs/tokio | 10 | Unify (9 deps, 7 features), Plan/run | Fork |
| helix-editor/helix | 14 | Unify (15 deps, 19 features), Plan/run | Fork |
| meilisearch/meilisearch | 23 | Unify (54 deps, 215 features), Plan/run | Fork |
| helixdb/helix-db | 6 | Unify (18 deps, 17 features), Plan/run | Fork |
Validation forks: cargo-rail-testing — full configs, integration guides, and reproducible artifacts.
How to Validate:
- Reproducibility: Every command in docs/large-repo-validation.md runs on forked repos with real merge history
- Metrics collection: Automated scripts measure execution reduction, surface accuracy, plan duration, unify impact
- Quality audit: Heuristics flag potential false positives/negatives for human review
- Real-world scenarios: Tests run on actual merge commits and real dependency graphs, not synthetic fixtures
Unify results (4 repos, 53 crates):
- 96 dependencies unified to
[workspace.dependencies] - 258 undeclared features fixed (silent bugs prevented)
- 2 dead features pruned
- MSRV computed for all repos (1.85.0 - 1.88.0)
Full protocol and raw artifacts: examples/README.md
Examples
Each workflow includes working config files and reproducible command sequences:
| Workflow | Examples | Real-World Configs |
|---|---|---|
| Change detection | examples/change_detection/ | tokio, helix, meilisearch |
| Unify | examples/unify/ | Validation results |
| Split/sync | examples/split-sync/ | — |
| Release | examples/release/ | — |
Full validation configs: Each fork in cargo-rail-testing includes:
.config/rail.toml— production-ready configdocs/cargo-rail-integration-guide.md— step-by-step integrationdocs/CHANGE_DETECTION_METRICS.md— measured impact analysis
Getting Help
- Issues: GitHub Issues
- Crate: crates.io/cargo-rail
License
Licensed under MIT.