Skip to content

Commit 6fd5bf6

Browse files
committed
cargo-rail: fixing conditional compilation feature flag pruning; updating cmd/cfg in readme
1 parent 670f72b commit 6fd5bf6

File tree

5 files changed

+410
-228
lines changed

5 files changed

+410
-228
lines changed

README.md

Lines changed: 58 additions & 215 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# cargo-rail
22

3-
**Graph-aware monorepo orchestration for Rust workspaces.**
3+
**Monorepo orchestration for Rust workspaces.**
44

55
[![Crates.io](https://img.shields.io/crates/v/cargo-rail.svg)](https://crates.io/crates/cargo-rail)
66
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
@@ -14,174 +14,50 @@ cargo install cargo-rail
1414

1515
---
1616

17-
## The Problem
18-
19-
Rust monorepos are powerful but painful:
20-
21-
- **CI tests everything** on every PR. 50 crates, 12 minutes, every time.
22-
- **Dependency fragmentation** causes duplicate compilations. You're maintaining a workspace-hack crate or living with the overhead.
23-
- **Extracting crates** means git-filter-repo scripts, lost commit mappings, and manual coordination.
24-
- **Releasing** requires juggling cargo-release, git-cliff, and custom scripts.
25-
26-
**cargo-rail** solves all of these with one tool and 12 dependencies.
27-
28-
---
29-
3017
## What It Does
3118

32-
### Test only what changed
19+
**Test only what changed** — Graph-aware change detection. Auto-detects nextest. 70-90% CI reduction.
3320

3421
```bash
35-
$ cargo rail test
36-
37-
Changed: crates/core/src/lib.rs
38-
39-
Affected crates:
40-
core (direct change)
41-
api (depends on core)
42-
cli (depends on api)
43-
44-
Skipped (no dependency path):
45-
examples
46-
benchmarks
47-
48-
Running: cargo nextest run -p core -p api -p cli
22+
cargo rail test
4923
```
5024

51-
Graph-aware change detection. Auto-detects nextest. **70-90% reduction in CI time.**
52-
53-
### Unify dependencies without workspace-hack
25+
**Unify dependencies** — Resolution-based `[workspace.dependencies]` without workspace-hack crates.
5426

5527
```bash
56-
$ cargo rail unify
57-
58-
Analyzed 3 target triples
59-
Unified 23 dependencies to [workspace.dependencies]
60-
Pinned 8 transitive-only deps
61-
Computed MSRV: 1.75.0
62-
63-
Features: minimal set for full functionality
64-
Versions: resolved from Cargo's actual resolver
65-
TOML: comments and formatting preserved
28+
cargo rail unify --check # Preview
29+
cargo rail unify # Apply
6630
```
6731

68-
- **Multi-target resolution** — Analyzes all your target triples, produces one correct dependency graph
69-
- **Minimum features** — Intersection of required features, not union. Leaner builds.
70-
- **Prune dead features** — Removes features declared but never used (`prune_dead_features = true`)
71-
- **Detect & remove unused deps** — Finds deps declared but not in the resolved graph (`detect_unused = true`, `remove_unused = true`)
72-
- **Automatic MSRV** — Computes workspace MSRV from dependency requirements (`msrv = true`)
73-
- **No workspace-hack crate** — Native `[workspace.dependencies]`, nothing extra
74-
75-
### Split crates with full git history
32+
**Split crates with history** — Extract crates to standalone repos. Deterministic SHAs, git-notes mapping.
7633

7734
```bash
78-
$ cargo rail split run my-crate
79-
80-
Extracted my-crate to standalone repo
81-
347 commits preserved
82-
Same commit SHAs (deterministic)
83-
Commit mapping stored in git-notes
35+
cargo rail split run my-crate
8436
```
8537

86-
Three modes:
87-
88-
| Mode | Use Case |
89-
|------|----------|
90-
| `single` | One crate → one repo |
91-
| `multi` | Multiple crates → one repo (separate directories) |
92-
| `workspace` | Multiple crates → one monorepo (with workspace Cargo.toml) |
93-
94-
### Sync with safe defaults
38+
**Sync bidirectionally** — Monorepo ↔ split repo. Changes from external repos create PR branches.
9539

9640
```bash
97-
$ cargo rail sync my-crate
98-
99-
Monorepo → split: pushes to main
100-
Split → monorepo: creates PR branch (never direct to main)
41+
cargo rail sync my-crate
10142
```
10243

103-
Bidirectional sync with safety guardrails. Changes from external contributors come in as PRs for review.
104-
105-
### Release with dependency ordering
44+
**Release with ordering** — Dependency-ordered publishing. Native changelog generation.
10645

10746
```bash
108-
$ cargo rail release run --all --bump minor --check
109-
110-
Planning release:
111-
1. core 0.4.2 → 0.5.0
112-
2. api 0.3.1 → 0.4.0 (depends on core)
113-
3. cli 0.2.0 → 0.3.0 (depends on api)
114-
115-
Changelog generated from conventional commits
116-
117-
118-
Run without --check to apply.
47+
cargo rail release run --all --bump minor --check
11948
```
12049

121-
Native changelog generation. Configurable publish delays. GitHub releases via `gh`.
122-
123-
---
124-
125-
## Real-World Impact
126-
127-
`cargo rail affected` on recent commits (HEAD~10):
128-
129-
| Repository | Crates | Affected | Skipped | Reduction |
130-
|------------|--------|----------|---------|-----------|
131-
| [helix](https://github.com/helix-editor/helix) | 13 | 5 | 8 | 62% |
132-
| [ripgrep](https://github.com/BurntSushi/ripgrep) | 10 | 4 | 6 | 60% |
133-
| [vello](https://github.com/linebender/vello) | 26 | 13 | 13 | 50% |
134-
| [tikv](https://github.com/tikv/tikv) | 83 | 42 | 41 | 49% |
135-
| [ruff](https://github.com/astral-sh/ruff) | 43 | 23 | 20 | 47% |
136-
| [meilisearch](https://github.com/meilisearch/meilisearch) | 19 | 17 | 2 | 11% |
137-
| [polars](https://github.com/pola-rs/polars) | 33 | 32 | 1 | 3% |
138-
139-
*Results vary by commit range. Monorepos with focused PRs see larger reductions.*
140-
14150
---
14251

14352
## Why cargo-rail
14453

145-
### 12 dependencies, 98 resolved
146-
147-
Compare: cargo-hakari pulls ~40 direct via guppy. release-plz pulls 600+ resolved.
148-
149-
Minimal supply chain. Easy to audit. Fast to compile.
150-
151-
### Correct by construction
152-
153-
cargo-rail queries Cargo's actual resolver across all your target triples:
154-
155-
```toml
156-
# crate-a: tokio = "1.0" (needs rt)
157-
# crate-b: tokio = "^1.5" (needs net)
158-
# crate-c: tokio = "1" (needs sync, linux-only)
159-
160-
# cargo-rail resolves across x86_64-linux, aarch64-darwin, x86_64-windows:
161-
[workspace.dependencies]
162-
tokio = { version = "1.5", features = ["rt", "net", "sync"] }
163-
```
164-
165-
- **Versions**: Always the resolved version, not the requested range
166-
- **Features**: Minimum set required across all targets (intersection, not union)
167-
- **MSRV**: Auto-computed from dependency requirements when `msrv = true`
168-
169-
### System git, no libgit2
170-
171-
Split and sync use the system `git` binary directly. No libgit2 or gitoxide dependencies:
54+
**11 dependencies, ~100 resolved.** Compare: cargo-hakari pulls ~40 direct via guppy. release-plz pulls 600+ resolved.
17255

173-
- Maximum fidelity with your existing git workflows
174-
- Deterministic: same input → same commit SHAs
175-
- git-notes for rebase-safe commit mapping
56+
**Multi-target resolution.** Queries Cargo's actual resolver across all your target triples. Computes intersection of required features (not union).
17657

177-
### Lossless TOML editing
58+
**System git.** Uses `git` binary directly. No libgit2/gitoxide. Deterministic SHAs. git-notes for rebase-safe commit mapping.
17859

179-
Uses `toml_edit` to preserve comments, formatting, and structure:
180-
181-
```toml
182-
# Your carefully written comments
183-
tokio = { version = "1.5" } # ← stays exactly here
184-
```
60+
**Lossless TOML.** Uses `toml_edit` to preserve comments and formatting.
18561

18662
---
18763

@@ -195,110 +71,77 @@ tokio = { version = "1.5" } # ← stays exactly here
19571
| `split` | Extract crate to standalone repo with history |
19672
| `sync` | Bidirectional monorepo ↔ split repo sync |
19773
| `release` | Version bump, changelog, tag, publish |
198-
| `init` | Generate `rail.toml` configuration |
74+
| `init` | Generate `rail.toml` |
19975
| `check` | Validate release readiness |
20076
| `clean` | Remove generated artifacts |
201-
| `config` | Validate configuration file |
77+
| `config` | Validate configuration |
20278

203-
Run `cargo rail <command> --help` for details, or see [docs/commands.md](docs/commands.md)
79+
Run `cargo rail <command> --help` for details, or see [docs/commands.md](docs/commands.md).
20480

20581
---
20682

20783
## Configuration
20884

20985
```bash
210-
cargo rail init # Auto-detects targets, generates .config/rail.toml
86+
cargo rail init # Generates .config/rail.toml
21187
```
21288

213-
Generated config with all options and defaults:
89+
Searched in order: `rail.toml`, `.rail.toml`, `.cargo/rail.toml`, `.config/rail.toml`
21490

21591
```toml
216-
# .config/rail.toml (or rail.toml, .rail.toml, .cargo/rail.toml)
217-
218-
# Platform targets for multi-target validation (auto-detected from rust-toolchain.toml, .cargo/config.toml, etc.)
219-
targets = ["x86_64-unknown-linux-gnu", "aarch64-apple-darwin", "x86_64-pc-windows-msvc"]
92+
targets = ["x86_64-unknown-linux-gnu", "aarch64-apple-darwin"]
22093

22194
[unify]
222-
include_paths = true # Handle path dependencies (default: true)
223-
include_renamed = false # Handle renamed deps with package = "..." (default: false)
224-
pin_transitives = false # Pin transitive-only deps - enable for hakari users (default: false)
225-
transitive_host = "root" # Where to put pinned transitive dev-deps: "root" or "crates/foo"
226-
exclude = [] # Dependencies to skip unification
227-
include = [] # Dependencies to force-include
228-
msrv = true # Compute workspace MSRV from deps (default: true)
229-
strict_version_compat = true # Treat version mismatches as errors (default: true)
230-
exact_pin_handling = "warn" # How to handle =x.y.z pins: skip, preserve, warn (default: warn)
231-
detect_unused = true # Detect unused dependencies (default: true)
232-
remove_unused = true # Auto-remove unused deps (default: true)
233-
max_backups = 3 # Number of backup files to keep (default: 3)
234-
prune_dead_features = true # Remove features never enabled in graph (default: true)
95+
pin_transitives = false # Enable for hakari/workspace-hack replacement
96+
prune_dead_features = true # Remove features never enabled in graph
97+
detect_unused = true # Find deps not in resolved graph
98+
remove_unused = true # Auto-remove unused deps
99+
msrv = true # Compute workspace MSRV from deps
235100

236101
[release]
237-
tag_prefix = "v" # Prefix for tags
238-
tag_format = "{crate}-{prefix}{version}" # Tag template: {crate}, {prefix}, {version}
239-
require_clean = true # Require clean working directory
240-
publish_delay = 5 # Seconds between crates.io publishes
241-
create_github_release = false # Create GitHub releases via gh CLI
242-
sign_tags = false # Sign tags with GPG/SSH
243-
changelog_path = "CHANGELOG.md" # Path to changelog file
244-
changelog_relative_to = "crate" # "crate" or "workspace"
245-
skip_changelog_for = [] # Crates to skip changelog generation
246-
require_changelog_entries = false # Error if no changelog entries
102+
tag_format = "{crate}-{prefix}{version}"
103+
create_github_release = false
104+
publish_delay = 5
247105

248106
[change-detection]
249-
# Files that trigger full workspace rebuild
250-
infrastructure = [".github/**", "scripts/**", "justfile", "Makefile", "*.sh"]
251-
# custom.verify = ["verify/**/*.rs"] # Custom path categories
107+
infrastructure = [".github/**", "justfile"]
252108

253-
# Per-crate configuration
254109
[crates.my-crate.split]
255110
remote = "[email protected]:org/my-crate.git"
256111
branch = "main"
257-
mode = "single" # single, combined, or monorepo
258-
259-
[crates.my-crate.release]
260-
publish = true
112+
mode = "single"
261113
```
262114

115+
Full reference: [docs/config.md](docs/config.md)
116+
117+
---
118+
119+
## Real-World Impact
120+
121+
`cargo rail unify` on tested monorepos:
122+
123+
| Repository | Crates | Deps Unified | Edits Saved |
124+
|------------|--------|--------------|-------------|
125+
| [tikv](https://github.com/tikv/tikv) | 83 | 57 | 516 |
126+
| [meilisearch](https://github.com/meilisearch/meilisearch) | 19 | 46 | 209 |
127+
| [helix](https://github.com/helix-editor/helix) | 13 | 16 | 66 |
128+
| [tokio](https://github.com/tokio-rs/tokio) | 10 | 10 | 35 |
129+
| [ripgrep](https://github.com/BurntSushi/ripgrep) | 10 | 9 | 35 |
130+
131+
See [examples/](examples/) for full results across 12 repositories.
132+
263133
---
264134

265135
## Replaces
266136

267-
| Tool | What cargo-rail does instead |
268-
|------|------------------------------|
269-
| cargo-hakari | Resolution-based unification, no workspace-hack crate |
270-
| cargo-hackerman | Same — workspace-hack replacement via `pin_transitives` |
271-
| cargo-udeps | Unused dep detection via `detect_unused`, removal via `remove_unused` |
272-
| cargo-machete | Same — detects deps not in resolved graph |
273-
| cargo-shear | Same — unused dep detection and removal |
274-
| cargo-msrv | Auto-compute workspace MSRV via `msrv = true` |
275-
| cargo-release | Dependency-ordered publishing, native changelog |
276-
| release-plz | Same, with 600 fewer dependencies |
277-
| git-cliff | Built-in conventional commit parsing |
278-
| Copybara | Deterministic split/sync with git-notes |
279-
| CI shell scripts | Graph-aware `affected` with nextest integration |
280-
281-
### cargo-rail vs cargo-hakari
282-
283-
| Feature | cargo-hakari | cargo-rail |
284-
|---------|--------------|------------|
285-
| Requires extra crate | Yes (workspace-hack) | No |
286-
| Preserves TOML comments | No | Yes |
287-
| Multi-target aware | No | Yes |
288-
| Feature computation | Union (all features) | Intersection (minimal) |
289-
| Dependencies | ~40 via guppy | 12 |
290-
291-
### cargo-rail vs release-plz
292-
293-
| Feature | release-plz | cargo-rail |
294-
|---------|-------------|------------|
295-
| Version bumping | Yes | Yes |
296-
| Changelog generation | Yes | Yes |
297-
| Dependency ordering | Yes | Yes |
298-
| GitHub releases | Yes | Yes |
299-
| Dependencies | ~600 resolved | 12 |
300-
| Crate splitting | No | Yes |
301-
| Bidirectional sync | No | Yes |
137+
| Tool | cargo-rail equivalent |
138+
|------|----------------------|
139+
| cargo-hakari | `unify` with `pin_transitives = true` |
140+
| cargo-udeps / cargo-machete | `detect_unused = true`, `remove_unused = true` |
141+
| cargo-msrv | `msrv = true` |
142+
| cargo-release / release-plz | `release` command |
143+
| git-cliff | Built-in changelog generation |
144+
| Copybara | `split` + `sync` commands |
302145

303146
---
304147

examples/ripgrep/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ cargo rail unify
2929
| Workspace members | **10** (not 1!) |
3030
| Dependencies unified | 9 |
3131
| Member edits | 35 |
32-
| Transitives pinned | 5 |
32+
| Dead features pruned | 11 |
3333

3434
**Dependencies unified:** serde_json, regex, serde, bstr, memchr, termcolor, globset, walkdir, log
3535

examples/tikv/README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,10 @@ cargo rail unify
2828
|--------|-------|
2929
| Workspace members | 83 |
3030
| Dependencies unified | 57 |
31-
| Member edits | **520** |
32-
| Dead features pruned | 156 |
31+
| Member edits | **516** |
32+
| Dead features pruned | 112 |
3333

34-
**MASSIVE IMPACT:** 520 manual file edits automated!
34+
**MASSIVE IMPACT:** 516 manual file edits automated!
3535

3636
## Configuration
3737

@@ -55,7 +55,7 @@ detect_unused = true
5555
- Distributed transactional key-value store (PingCAP)
5656
- Largest workspace tested (83 crates)
5757
- Shows cargo-rail's diagnostic capabilities (auto-detects and skips multi-version conflicts)
58-
- 520 file edits saved - massive enterprise impact
59-
- 156 dead features pruned
58+
- 516 file edits saved - massive enterprise impact
59+
- 112 dead features pruned
6060

6161
Details in [`summary.toml`](./summary.toml).

0 commit comments

Comments
 (0)