Test the downstream impact of crate changes before publishing to crates.io
Test your crate changes with Cargo Copter and practice responsible API evolution.
CRITICAL: This program executes arbitrary untrusted code from crates.io. Always run in sandboxed environments (Docker, VMs, containers).
# Install
git clone https://github.com/imazen/cargo-copter
cd cargo-copter
cargo build --release
export PATH=$PATH:$(pwd)/target/release/
# Run
cd /path/to/your/crate
cargo-copterOutput:
Testing 2 reverse dependencies of rgb
this = 0.8.91 4cc3e60* (your work-in-progress version)
Legend: I=Install (cargo fetch), C=Check (cargo check), T=Test (cargo test)
┌────────────────────┬──────────┬─────────────────┬─────────────────────┬─────────────────────┐
│ Offered │ Spec │ Resolved │ Dependent │ Result Time │
├────────────────────┼──────────┼─────────────────┼─────────────────────┼─────────────────────┤
│ - baseline │ ^0.8.48 │ 0.8.51 📦 │ image 0.25.8 │ PASSED ✓✓✓ 2.1s │
│ ✓ =this(0.8.91) │ ^0.8.48 │ 0.8.91 📁 │ image 0.25.8 │ PASSED ✓✓✓ 1.9s │
├────────────────────┼──────────┼─────────────────┼─────────────────────┼─────────────────────┤
│ - baseline │ ^0.8.0 │ 0.8.51 📦 │ lodepng 3.10.5 │ PASSED ✓✓✓ 1.7s │
│ ✓ =this(0.8.91) │ ^0.8.0 │ 0.8.91 📁 │ lodepng 3.10.5 │ PASSED ✓✓✓ 1.5s │
└────────────────────┴──────────┴─────────────────┴─────────────────────┴─────────────────────┘
Summary:
✓ Passed: 2
✗ Regressed: 0
⊘ Skipped: 0
HTML report: copter-report.html
Markdown report: copter-report.md
# Test top 10 dependents
cargo-copter --top-dependents 10
# Test specific crates (supports version pinning)
cargo-copter --dependents image:0.25.8 serde tokio
# Parallel testing with a custom caching dir (10x faster)
cargo-copter --jobs 4 --staging-dir .copter/staging
# Fast check-only (skip tests)
cargo-copter --no-test --jobs 8
# Test against multiple crate versions
cargo-copter --test-versions "0.8.0 0.8.48" 0.8.91
# Force version testing (bypass semver)
cargo-copter --test-versions 0.9.0--force-versions 0.7.1
# Test with specific features enabled
cargo-copter --features "serde unstable"
# Test different crate path
cargo-copter --path ~/my-crate
# Test published crate without local source
cargo-copter --crate rgb --test-versions 0.8.50 0.8.51-p, --path <PATH> Path to crate (directory or Cargo.toml)
-c, --crate <NAME> Crate name (for testing published crates)
--top-dependents <N> Test top N by downloads [default: 5]
--dependents <CRATE[:VER]>... Test specific crates (supports version pins)
--dependent-paths <PATH>... Test local crates
-j, --jobs <N> Parallel jobs [default: 1]
--staging-dir <PATH> Cache directory [default: .copter/staging]
--output <PATH> HTML output [default: copter-report.html]
--no-check Skip cargo check
--no-test Skip cargo test
--json JSON output
--test-versions <VER>... Test specific versions (space-delimited supported)
--force-versions <VER>... Force testing specific versions (bypass semver requirements)
--features <FEATURES>... Feature flags passed to cargo commands
# Pin specific versions
cargo-copter --dependents image:0.25.8 serde:1.0.0
# Test multiple versions (space-delimited within args or across args)
cargo-copter --test-versions "0.8.0 0.8.48" 0.8.91
# Pass feature flags to cargo
cargo-copter --features "default serde" --features rgb/unstable| Status | Icon | Description |
|---|---|---|
| PASSED | ✓ | Compiled and tested successfully with offered version |
| REGRESSED | ✗ | Baseline passed but offered version failed |
| BROKEN | ✗ | Both baseline and offered version failed |
| Skipped | ⊘ | Version offered but not tested (resolved elsewhere) |
Icon meanings in Offered column:
✓= Test ran with this version and passed✗= Test ran with this version and failed⊘= Version was offered but skipped (not used by cargo)-= Baseline test row== Cargo resolved to exact offered version↑= Cargo upgraded to newer compatible version≠= Version mismatch or forced
Testing 2 reverse dependencies of rgb
this = 0.8.91 a138e69* (your work-in-progress version)
features: default
Legend: I=Install (cargo fetch), C=Check (cargo check), T=Test (cargo test)
┌────────────────────┬──────────┬─────────────────┬─────────────────────┬─────────────────────┐
│ Offered │ Spec │ Resolved │ Dependent │ Result Time │
├────────────────────┼──────────┼─────────────────┼─────────────────────┼─────────────────────┤
│ - baseline │ ^0.8.52 │ 0.8.51 📦 │ image 0.25.8 │ PASSED ✓✓✓ 2.1s │
│ ✓ =this(0.8.91) │ ^0.8.52 │ 0.8.91 📁 │ image 0.25.8 │ PASSED ✓✓✓ 1.9s │
├────────────────────┼──────────┼─────────────────┼─────────────────────┼─────────────────────┤
│ - baseline │ ^0.8 │ 0.8.51 📦 │ pixels 0.14 │ PASSED ✓✓✓ 1.5s │
│ ✗ =this(0.8.91) │ ^0.8 │ 0.8.91 📁 │ pixels 0.14 │ REGRESSED ✓✗- 1.4s │
│ ├──────────┘ └─────────────────────┘ │
│ │ cargo check failed on pixels:0.14 │
│ │ • error[E0061]: function takes 2 arguments │
└────────────────────┴─────────────────────────────────────────────────────────────────────────┘
Summary:
✓ Passed: 1
✗ Regressed: 1
⊘ Skipped: 0
Features:
- Baseline + offered versions for each dependent
- Separator lines between different dependents
- Error details expand with L-shaped borders (columns 2-5)
- Multi-version tree display with
├─prefixes - Forced versions show
[≠→!]suffix
See CONSOLE-FORMAT.md for complete format specification and all demo scenarios.
- Visual summary cards with statistics
- Detailed compilation logs for each dependent
- Expandable error sections
- Color-coded statuses
- Regressions first (most actionable)
- Structured error details with JSON diagnostics
- Concise passing section
- Ready for LLM analysis
- ~14.3s per dependent (first run)
- Full compilation from scratch
- ~1.4s per dependent (subsequent runs)
- 10x faster with persistent cache
- ~770MB disk usage for build artifacts
- Use
--jobs N(N = CPU cores) - ~4x speedup on 4-core systems
- Parallelizes among dependents, not within
- Configuration - Read Cargo.toml, extract name/version, capture git state
- Discovery - Query crates.io API for reverse dependencies (paginated)
- Testing - ThreadPool tests each dependent in parallel
- Classification - Determine PASSED/REGRESSED/BROKEN/ERROR
- Reporting - Generate console, HTML, and markdown reports
- Source cache:
.copter/staging/{crate}-{version}/(unpacked sources) - Build artifacts: Same location, includes
target/directory - Downloads:
.copter/crate-cache/(original .crate files)
Current: Uses .cargo/config with paths = [...]
Phase 5 Target: Use cargo --config 'patch.crates-io.{crate}.path="..."' for cleaner multi-version testing
See PLAN.md for Phase 5+ roadmap:
- Multi-version testing (
--test-versions) - 3-step ICT flow (Install/Check/Test with early stopping)
- Refactor to
--configflag - Live integration tests
- JSON output format
All contributions welcome! Priority areas:
- Complete Phase 5 multi-version testing
- Add live crates.io integration tests
- Implement JSON output
- Docker images for security
- ✅ Run in Docker/VM/containers (isolated execution)
- ✅ Network isolation (limit egress to crates.io only)
- ✅ Resource limits (CPU, memory, disk quotas)
- ✅ Non-root execution (never run as root)
- ✅ Regular cache cleanup (remove old artifacts)
Docker Example:
docker run --rm -v $(pwd):/work imazen/cargo-copter \
--path /work --top-dependents 5Error: "Cannot specify both --no-check and --no-test" → Choose one or neither
Error: "Must specify at least one dependent source"
→ Use --top-dependents N, --dependents, or --dependent-paths
Disk space exhausted
→ Clear cache: rm -rf .copter/
Compilation timeout
→ Use --no-test for faster check-only runs
0- Success, no regressions detected-2- Regressions detected (breaking changes found)- Other - Internal error
# Build and test
cargo build --release
cargo test
# Test against real crate
RUST_LOG=debug ./target/release/cargo-copter --path ~/rust-rgb --top-dependents 1Project Structure:
src/
├── main.rs # Entry point, orchestration
├── cli.rs # CLI parsing
├── api.rs # crates.io API client
├── compile.rs # Compilation logic
├── report.rs # Report generation
└── error_extract.rs # Error parsing
tests/
├── api_integration_test.rs
├── cli_parsing_test.rs
└── offline_integration.rs
test-crates/integration-fixtures/ # Test fixtures
Test Fixtures (in test-crates/integration-fixtures/):
- base-crate-v1/v2: Published baseline vs. WIP with breaking change
- dependent-passing: Uses only stable API → PASSED
- dependent-regressed: Uses removed API → REGRESSED
- dependent-broken: Has type error → BROKEN
- dependent-test-failing: Tests fail with new version
# .github/workflows/copter.yml
name: Test Downstream Impact
on: [pull_request]
jobs:
copter:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Install cargo-copter
run: |
git clone https://github.com/imazen/cargo-copter
cd cargo-copter
cargo install --path .
- name: Test top 10 dependents
run: cargo-copter --top-dependents 10 --jobs 4
- name: Upload report
if: always()
uses: actions/upload-artifact@v3
with:
name: copter-report
path: copter-report.htmlRecently updated from 7-year-old dependencies:
- Rust 2021 edition
ureq2.10 (replaced oldcurl)serde1.0 (replaced deprecatedrustc-serialize)tempfile3.8 (replacedtempdir0.3)toml0.8 (updated from 0.1)crates_io_api0.12 (new integration)clap4.5 for robust CLI parsing
Added:
- Enhanced error diagnostics with JSON parsing
- Persistent caching infrastructure (10x speedup)
- AI-optimized markdown reports
- Git version tracking
- Parallel testing
MIT/Apache-2.0
This is the standard license for Rust projects.
- GitHub: https://github.com/imazen/cargo-copter
- crates.io: https://crates.io
- Rust API Evolution RFC: https://github.com/rust-lang/rfcs/blob/master/text/1105-api-evolution.md
- Development Roadmap: PLAN.md
Ready to take flight? Run cargo-copter in your crate and ensure your changes don't break the ecosystem! 🚁