Skip to content

loadingalias/cargo-rail

Repository files navigation

cargo-rail

Cargo-native control plane for Rust monorepos: plan changes, run only needed work locally and in CI, unify the graph (deps/features), split/sync crates into new repos/monorepos, and automate releases with 14 core dependencies.

Crates.io docs.rs CI MSRV

Why cargo-rail

Impact on real repos (validated across 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 78 across 47 crates (avg 1.7 per crate)
Undeclared features fixed 141 silent bugs prevented
Tooling consolidation 6 cargo plugins → 1 command
MSRV computation Automatic from dependency graph

Two compound effects = massive time/cost savings:

  1. Change detection (plan/run) reduces what runs — 55% fewer CI surfaces, 64% fewer compute units
  2. Dependency unification (unify) reduces build graph complexity — cleaner deps, smaller build units, fewer rebuilds

Before cargo-rail: Run 6 tools separately (hakari, udeps, machete, shear, features-manager, msrv), each with different data/timing After cargo-rail: cargo rail unify --check in one metadata call

Quick Start

# install
cargo install cargo-rail

# generate config
cargo rail init

# deterministic planning + execution
cargo rail plan --merge-base
cargo rail run --merge-base --profile ci

# dependency hygiene
cargo rail unify --check

Pre-built binaries: GitHub Releases

Core Workflows

Change Planning + Execution (plan / run)

Problem: CI wastes resources testing unchanged code. Teams 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?
cargo rail plan --merge-base

# CI: deterministic plan → selective execution
cargo rail plan --merge-base -f github  # outputs: build=true, test=false, docs=true, ...
cargo rail run --merge-base --profile ci  # runs ONLY what plan selected

How repos use this:

  1. Configure change detection rules in .config/rail.toml (infrastructure files, doc-only changes, etc.)
  2. Run plan to see impact classification (which surfaces: build, test, bench, docs, infra)
  3. Run run to execute only selected surfaces — locally or in CI
  4. Use --explain to 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).

Dependency Unification (unify)

Problem: Teams juggle 6+ cargo plugins for dependency hygiene (hakari, udeps, machete, shear, features-manager, msrv). Each runs separately, on different data, with different CLI patterns. Undeclared features (borrowed from Cargo's resolver) break isolated builds.

Solution: cargo rail unify — one command, one metadata call, comprehensive analysis:

cargo rail unify --check    # preview all changes
cargo rail unify            # apply workspace-wide
cargo rail unify --explain  # understand each decision

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_transitives for cargo-hakari equivalent

Validated impact on real repos:

Repository Crates Locked Deps Deps Unified Undeclared Features MSRV Computed
tokio-rs/tokio 10 224 9 19 1.85.0
helix-editor/helix 14 351 15 25 1.87.0
meilisearch/meilisearch 23 835 54 97 1.88.0
Aggregate 47 1,410 78 141

Config files and validation artifacts: examples/unify/

Tools replaced: cargo-hakari, cargo-udeps, cargo-machete, cargo-shear, cargo-features-manager, cargo-msrv (6 tools → 1 command)

Split + Sync (Google Copybara Replacement)

Problem: Teams need to publish crates from monorepos but want clean standalone repos with full git history. Existing tools (git subtree, git-filter-repo) are one-way and manual. Google's Copybara requires Bazel and complex config.

Solution: cargo rail split + cargo rail sync — bidirectional sync with 3-way conflict resolution:

# Extract crate to standalone repo with full git history
cargo rail split init my-crate  # configure once
cargo rail split run my-crate   # extract with history preserved

# Bidirectional sync
cargo rail sync my-crate --to-remote    # push monorepo changes to split repo
cargo rail sync my-crate --from-remote  # pull split repo changes (creates PR branch)

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.

Release Automation (release)

Release checks, versioning, changelogs, tags, dependency-order publish.

Why This Model

  • One planner contract for local and CI.
  • One executor (run) for planner-selected surfaces.
  • One config (rail.toml) for policy.
  • Check-mode exit code 1 means "changes detected" (not a crash).

GitHub Actions Integration

For CI 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.

The action keeps CI behavior aligned with local plan + run workflows.

Configuration

cargo rail init generates .config/rail.toml.

Example config:

# Platform targets for multi-target validation
targets = [
  "aarch64-apple-darwin",
  "aarch64-unknown-linux-gnu",
  "x86_64-pc-windows-msvc",
  "x86_64-unknown-linux-gnu",
]

[unify]
pin_transitives = false      # Enable for cargo-hakari replacement
detect_unused = true         # Detect unused dependencies
prune_dead_features = true   # Remove features never enabled
msrv = true                  # Compute workspace rust-version
detect_undeclared_features = true  # Find borrowed features

[release]
tag_format = "{prefix}{version}"
publish_delay = 5
sign_tags = true

[change-detection]
# Files triggering full workspace rebuild
infrastructure = [
  ".github/**",
  "scripts/**",
  "justfile",
  "rust-toolchain.toml",
]

Full documentation:

Migration Guides

Proven On Large Repos

All core workflows (plan/run, unify, split, sync, release) validated on production repos:

Repository Crates Locked Deps Validation Coverage
tokio-rs/tokio 10 224 Plan/run (5 merges), unify, split, sync, release
helix-editor/helix 14 351 Plan/run (5 merges), unify, split, sync, release
meilisearch/meilisearch 23 835 Plan/run (5 merges), unify, split, sync, release

Why this matters:

Validation isn't a single test — it's a protocol:

  1. Reproducibility: Every command in docs/large-repo-validation.md runs on forked repos with real merge history
  2. Metrics collection: Automated scripts measure execution reduction, surface accuracy, plan duration, unify impact
  3. Quality audit: Heuristics flag potential false positives/negatives for human review
  4. Real-world scenarios: Tests run on actual merge commits and real dependency graphs, not synthetic fixtures

Change detection results (15 merge scenarios):

  • 55% execution reduction rate (surfaces)
  • 64% weighted reduction rate (compute units)
  • Average plan duration: 632ms
  • Quality audit: 2 potential false-negatives, 1 potential false-positive (flagged for review)

Unify results (3 repos, 47 crates):

  • 78 dependencies unified
  • 141 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:

All examples run on real repos (tokio, helix, meilisearch) and include:

  • Rail config (.config/rail.toml)
  • Command sequence (step-by-step)
  • Expected outputs
  • Validation artifacts

Getting Help

License

Licensed under MIT.