Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 49 additions & 9 deletions .anvil.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
version = 1
tool = "anvil"
tool_version = "0.5.0"
catalog_checksum = "sha256:b9e76e5dab6d2cd8cd2eaea04bd1b4457ea8839a688795981dc4cc9a415f8092"
catalog_checksum = "sha256:f56108d6b5d2931341b900cb2fb06d3397dd7e379aa905bd4bffbf1f83e2817a"

[[file]]
path = ".anvil/container/Containerfile"
Expand Down Expand Up @@ -85,7 +85,7 @@ checksum = "sha256:50f04b4ea6c99df8ad7434d320f34dccdb6db6a77b83e3090e35de0ca3a15

[[file]]
path = "justfiles/anvil/checks/bolero.just"
checksum = "sha256:003032f39c781851b880c1d7e63b93573f5252cca9f785b98b04dfa2e858aff3"
checksum = "sha256:754827bb664723169b8d48a6f1e69f9122b5440ba0b94a91622f4d23313e2503"

[[file]]
path = "justfiles/anvil/checks/careful.just"
Expand All @@ -97,7 +97,7 @@ checksum = "sha256:681c8952690496c7d29c7bf83a5ab99c193088b05a5dc7ea3b3b0ed240090

[[file]]
path = "justfiles/anvil/checks/cargo-sort.just"
checksum = "sha256:2652ae52153eebe3fdb1dfd553aa65cab84d11054d5314470d55774a92a742b3"
checksum = "sha256:98c9f90e1b6b3beba5d818bf9a4023068eb61e9f071ffb6f9cd009a7374e9a30"

[[file]]
path = "justfiles/anvil/checks/clippy.just"
Expand Down Expand Up @@ -133,7 +133,7 @@ checksum = "sha256:d003f1d82898706c136f42c3156d5ecc8f288c18f125c964b9dca1d24fa93

[[file]]
path = "justfiles/anvil/checks/fmt.just"
checksum = "sha256:967b68ef3ed3e0dcac2a1ea22ef92c66a7db802bda718f6e5ce39f529ed8e492"
checksum = "sha256:618907282b9b7d1e5c094d0b29c1c7231465078b617fa233219fe8c4870437cb"

[[file]]
path = "justfiles/anvil/checks/license-headers.just"
Expand All @@ -145,7 +145,7 @@ checksum = "sha256:689f7d5b1eaea32672f8d0b9fff0994cc03bc56fb8ef282a4286f930fc503

[[file]]
path = "justfiles/anvil/checks/loom.just"
checksum = "sha256:4bf509bcca3507aafd9848ce19ea3691ab2cd77de05b344e042f3ba2c300e35b"
checksum = "sha256:8feb9bcb11c314f4c7825854cc7dd10b184d91ad520dd736addd098d3c7bb1bf"

[[file]]
path = "justfiles/anvil/checks/miri-race-coverage.just"
Expand Down Expand Up @@ -181,7 +181,7 @@ checksum = "sha256:d346399f288570066e53fd123baf05f7d4a57f17da8ee687a6d881204c5ba

[[file]]
path = "justfiles/anvil/checks/semver-check.just"
checksum = "sha256:1b8b67f8c054c8da03d43b746f28c20f5366996e3dea3c21cf25d04a6496a072"
checksum = "sha256:52b31bec3c5d271b25f46d9b1dd6aca4b84e394fd8cf46bfa5386a3df7ed3999"

[[file]]
path = "justfiles/anvil/checks/spellcheck.just"
Expand Down Expand Up @@ -233,7 +233,7 @@ checksum = "sha256:a00153eda6b55d4db33e8019fa74f4a905f9c80b10b0db10955179c0af3e9

[[file]]
path = "justfiles/anvil/helpers.just"
checksum = "sha256:6b71379310b986e9a44000ba9d072c3f8934397a0f7fb1bf27a31a7742ca7002"
checksum = "sha256:72a7b89f85b5bdc55f8c8c4699b78e877a6c78d0bb39e57b57518f5e289858fa"

[[file]]
path = "justfiles/anvil/impact.just"
Expand All @@ -253,11 +253,11 @@ checksum = "sha256:5b2d91569f3fe87cd5f516623a01b5c7ae855ad587870ef9a2ae6d5619dd9

[[file]]
path = "justfiles/anvil/tools.just"
checksum = "sha256:a1e44ca16f172b487afa3997f102512733d3b65a4418cf894cbd749a3abc17dc"
checksum = "sha256:c1f5181d14a734cde8ccc32bbf5acece485791573b7a95cfff61b63d0f1fcb97"

[[file]]
path = "justfiles/anvil/versions.just"
checksum = "sha256:acbea93d5117db747537f4f7b9a5eb90b7d3e0dd3e8684cc0e4dc1dcb15ac93e"
checksum = "sha256:699fe1053c28257d33516de880caf3224b23365f33dd8c9885c9bb96a0e3f94c"

[[region]]
host = ".delta.toml"
Expand Down Expand Up @@ -324,6 +324,46 @@ host = "crates/cargo-ensure-no-default-features/Cargo.toml"
id = "anvil-lints"
checksum = "sha256:2dd7c0f21339fd17092b8dedfe924aa86732c3520baab84f914c2d8f4103ac40"

[[region]]
host = "crates/cargo-gamma-attrs-impl/Cargo.toml"
id = "anvil-lints"
checksum = "sha256:2dd7c0f21339fd17092b8dedfe924aa86732c3520baab84f914c2d8f4103ac40"

[[region]]
host = "crates/cargo-gamma-attrs/Cargo.toml"
id = "anvil-lints"
checksum = "sha256:2dd7c0f21339fd17092b8dedfe924aa86732c3520baab84f914c2d8f4103ac40"

[[region]]
host = "crates/cargo-gamma-engine/Cargo.toml"
id = "anvil-lints"
checksum = "sha256:2dd7c0f21339fd17092b8dedfe924aa86732c3520baab84f914c2d8f4103ac40"

[[region]]
host = "crates/cargo-gamma-lib/Cargo.toml"
id = "anvil-lints"
checksum = "sha256:2dd7c0f21339fd17092b8dedfe924aa86732c3520baab84f914c2d8f4103ac40"

[[region]]
host = "crates/cargo-gamma-process/Cargo.toml"
id = "anvil-lints"
checksum = "sha256:2dd7c0f21339fd17092b8dedfe924aa86732c3520baab84f914c2d8f4103ac40"

[[region]]
host = "crates/cargo-gamma-rt/Cargo.toml"
id = "anvil-lints"
checksum = "sha256:2dd7c0f21339fd17092b8dedfe924aa86732c3520baab84f914c2d8f4103ac40"

[[region]]
host = "crates/cargo-gamma-unsafe/Cargo.toml"
id = "anvil-lints"
checksum = "sha256:2dd7c0f21339fd17092b8dedfe924aa86732c3520baab84f914c2d8f4103ac40"

[[region]]
host = "crates/cargo-gamma/Cargo.toml"
id = "anvil-lints"
checksum = "sha256:2dd7c0f21339fd17092b8dedfe924aa86732c3520baab84f914c2d8f4103ac40"

[[region]]
host = "crates/cargo-heather/Cargo.toml"
id = "anvil-lints"
Expand Down
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ anyhow = { version = "1.0.100", default-features = false }
assert_cmd = { version = "2.2.0", default-features = false }
async-once-cell = { version = "0.5", default-features = false }
blake3 = { version = "1.8.5", default-features = false }
bolero = { version = "0.13.4", default-features = false }
bolero = { version = "0.13.4", default-features = false, features = ["std"] }
bytes = { version = "1.11.1", default-features = false }
camino = { version = "1.2.5", default-features = false }
# local dependencies
Expand Down
12 changes: 6 additions & 6 deletions crates/cargo-anvil/docs/design/checks.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,9 +157,9 @@ that provided the strongest version of the check.

| Check | Invocation | Source |
|--------------------------------|-----------------------------------------------------------|--------|
| `fmt` | `cargo +<pinned-nightly> fmt --all --check` | all |
| `fmt` | `cargo each --workspace --keep-going -- cargo +<pinned-nightly> fmt --manifest-path {manifest} --check`. `cargo-each` resolves workspace membership and invokes rustfmt once per manifest, keeping child commands bounded on every platform while reporting every failing member. Unlike `cargo fmt --all`, local path dependencies outside the workspace are not included. | all |
| `clippy` | `cargo clippy --workspace --all-targets --all-features --locked -- -D warnings` | all |
| `cargo-sort` | `cargo sort --workspace --grouped --check --check-format` | oxidizer-github |
| `cargo-sort` | `cargo sort --workspace --grouped --check --check-format`. Since cargo-sort 2.1.2, formatting-only differences are warnings unless `--check-format` is set; Anvil keeps it load-bearing so dependency ordering and Cargo manifest formatting are both enforced. `--grouped` preserves intentional blank-line-separated dependency groups. | oxidizer-github |
| `license-headers` | `cargo heather --workspace` | oxidizer (`heather`), oxidizer-github |
| `ensure-no-cyclic-deps` | `cargo ensure-no-cyclic-deps --workspace` | oxidizer-github (sibling crate in `ox-tools-gh`) |
| `ensure-no-default-features` | `cargo ensure-no-default-features --workspace` | oxidizer-github |
Expand All @@ -170,7 +170,7 @@ that provided the strongest version of the check.
| `deny` | `cargo deny check` | all |
| `audit` | `cargo audit` | oxidizer |
| `udeps` | `cargo +<pinned-nightly> udeps --workspace --all-features` run **twice** — once with default targets (lib + bins) and once with `--all-targets`. cargo-udeps only analyzes the targets it's told to, and each run catches a variant the other masks: the default-targets run surfaces a dep in `[dependencies]` referenced only by tests/benches/examples (it should be a dev-dep; `--all-targets` would see it as "used"), while the `--all-targets` run surfaces unused `[dev-dependencies]` (never compiled by the default-targets run). Together they cover unused deps, unused dev-deps, and deps that should be dev-deps. | oxidizer, oxidizer-github |
| `semver-check` | `cargo semver-checks --baseline-rev <baseline>` per affected library crate. The PR target is the baseline. Exit 100 is a completed check with deny-level findings; exit 101 or another nonzero status means the comparison was inconclusive. Both outcomes write `target/anvil/comments/semver.md` and remain advisory, matching the repository's native `semver` job (`continue-on-error: true`). Proven rename and bin→lib transitions with no comparable baseline, and dependencies proven to be yanked only in the checked-out baseline tree, are skipped without a comment. Anvil preflight failures such as invalid current-workspace metadata or an unavailable baseline ref still fail because the recipe cannot establish what to compare. | oxidizer-github |
| `semver-check` | `cargo semver-checks --baseline-rev <baseline>` per affected publishable library crate. Crates with `publish = false` and bin-only crates have no registry consumer contract and are skipped. The PR target is the baseline. Exit 100 is a completed check with deny-level findings; exit 101 or another nonzero status means the comparison was inconclusive. Both outcomes write `target/anvil/comments/semver.md` and remain advisory, matching the repository's native `semver` job (`continue-on-error: true`). Proven rename and bin→lib transitions with no comparable baseline, and dependencies proven to be yanked only in the checked-out baseline tree, are skipped without a comment. Anvil preflight failures such as invalid current-workspace metadata or an unavailable baseline ref still fail because the recipe cannot establish what to compare. | oxidizer-github |
| `external-types` | `cargo +<catalog-nightly-rustdoc-schema> check-external-types --manifest-path` per library crate (per-manifest because the tool has no `--workspace`/`--package`; bin-only crates have no public API surface and are skipped). Setup installs the catalog version but validation accepts newer installed tools. The selected nightly is tested with the catalog version; an incompatible newer tool fails closed with a tool/nightly compatibility diagnostic rather than silently selecting a different schema. | oxidizer-github |

### `pr-slow`
Expand Down Expand Up @@ -198,8 +198,8 @@ This is the same set of checks that used to live in the standalone `pr-test` gro
|-----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------|
| `miri` | `cargo +<pinned-nightly> miri test --all-features --tests` over the impact-affected packages. Uses libtest (one process per test binary), **not** `cargo miri nextest run`: under miri, nextest's process-per-test model pays miri's expensive std-initialization re-interpretation for *every* test and roughly doubles wall-time on a large suite (the dominant cost on the PR critical path). `--tests` runs lib/bin unit tests and integration tests (the same target set nextest ran) while excluding doctests, which miri can't run; it is used in preference to `--lib --tests` because `--lib` errors with "no library targets found" on a bin-only affected package under impact scoping. Slow tests opt out per-test with `#[cfg_attr(miri, ignore)]` -- anvil doesn't pass exotic `MIRIFLAGS`; the per-test opt-out is the canonical mechanism. libtest exits 0 when a binary's tests are all skipped, so no `--no-tests=pass` workaround is needed. The recipe reads its scope from the `target/anvil/impact/` cache via `_anvil-impact-include`; because it depends on `anvil-impact`, a clean direct or PR invocation is impact-scoped (unaffected packages are skipped). It runs the full workspace only when scoping is off — the scheduled/full tiers set `ANVIL_IMPACT=off`, and a dirty local tree widens for safety. | oxidizer, oxidizer-github |
| `careful` | `cargo +<catalog-nightly> careful test --all-features --locked` over the impact-affected packages. cargo-careful uses a debug-instrumented std in a stable cache path. Because Cargo fingerprints the sysroot path rather than its contents, the recipe records the actual `rustc -vV` and SHA-256 of the resolved `cargo-careful` executable in `target/anvil/careful-sysroot.id`; either changing triggers `cargo clean`. The executable hash is used because cargo-careful rejects version-only invocations. This remains correct when validation accepts a newer installed cargo-careful. | oxidizer-github |
| `loom` | For each `[[test]]` target that declares `required-features = ["loom"]`, `cargo test -p <pkg> --release --all-features --locked --test <target> -- --test-threads=1` with `RUSTFLAGS="--cfg loom"`. [`loom`](https://crates.io/crates/loom) is a permutation-based concurrency model checker that explores thread interleavings. Targets are detected **structurally** from `cargo metadata` (a test target whose `kind` contains `test` and whose `required-features` contains `loom`) -- not via a filename/cfg/comment heuristic -- and only those targets run, so loom never touches a crate's ordinary tests. The `loom` feature selects the target (`required-features`); `--cfg loom` activates loom (source swaps std↔loom atomics on `#[cfg(loom)]`, and `[target.'cfg(loom)'.dependencies] loom` links only under the cfg) -- both are required. Scoped per-package with `-p` (never `--workspace`) so the global cfg never leaks into deps reachable only through other members. **Fail-loud**: a crate that declares loom support (a `loom` feature or a `cfg(loom)` dependency) but exposes no such test target errors out rather than silently no-opping. When no crate ships a loom target the recipe skips (exit 0). | oxidizer-github |
| `bolero` | Uses the catalog nightly and release profile consistently to discover targets one package at a time, then runs each affected libfuzzer target for 60 seconds on Linux. Explicitly selecting `release` avoids cargo-bolero's implicit, adopter-defined `fuzz` profile and matches target execution. Per-package discovery is required because `cargo-bolero list` accepts only one `--package`; local whole-workspace runs enumerate workspace members before discovery. A successful empty discovery is a no-op; metadata, discovery, or parsing failure fails the check. Non-Linux hosts skip because cargo-bolero's native dependencies are unsupported there, while harnesses still run as ordinary tests. | oxidizer-github |
| `loom` | For each `[[test]]` target that declares `required-features = ["loom"]`, `cargo test -p <pkg> --release --all-features --locked --test <target> -- --test-threads=1` with `RUSTFLAGS="--cfg loom"`. [`loom`](https://crates.io/crates/loom) is a permutation-based concurrency model checker that explores thread interleavings. CI uses Loom's documented two-preemption bound by default, preventing one large model from consuming the whole job while retaining systematic coverage; adopters can override `LOOM_MAX_PREEMPTIONS` for deeper or exhaustive runs. Targets are detected **structurally** from `cargo metadata` (a test target whose `kind` contains `test` and whose `required-features` contains `loom`) -- not via a filename/cfg/comment heuristic -- and only those targets run, so loom never touches a crate's ordinary tests. The `loom` feature selects the target (`required-features`); `--cfg loom` activates loom (source swaps std↔loom atomics on `#[cfg(loom)]`, and `[target.'cfg(loom)'.dependencies] loom` links only under the cfg) -- both are required. Scoped per-package with `-p` (never `--workspace`) so the global cfg never leaks into deps reachable only through other members. **Fail-loud**: a crate that declares loom support (a `loom` feature or a `cfg(loom)` dependency) but exposes no such test target errors out rather than silently no-opping. When no crate ships a loom target the recipe skips (exit 0). | oxidizer-github |
| `bolero` | Uses the catalog nightly and release profile consistently to discover targets one package at a time, then runs each affected libfuzzer target for 60 seconds on Linux. Explicitly selecting `release` avoids cargo-bolero's implicit, adopter-defined `fuzz` profile and matches target execution. Adopters that disable `bolero`'s default features must enable its `std` feature; that feature supplies the compile-time `bolero-engine/any` support required by the libfuzzer adapter. Per-package discovery is required because `cargo-bolero list` accepts only one `--package`; local whole-workspace runs enumerate workspace members before discovery. A successful empty discovery is a no-op; metadata, discovery, or parsing failure fails the check. Non-Linux hosts skip because cargo-bolero's native dependencies are unsupported there, while harnesses still run as ordinary tests. | oxidizer-github |

#### `pr-mutants` (mutation testing)

Expand Down Expand Up @@ -345,7 +345,7 @@ Each catalog check is tagged with one of four buckets:

| Bucket | `$include` tier | Behavior when a tier value is present | Behavior when unscoped (`ANVIL_IMPACT=off` / no cache) |
|-----------|-------------------------------|-----------------------------------------------------------------------------|--------------------------------------|
| modified | `_anvil-impact-include modified` | If `--skip`: exit 0. Otherwise run unconditionally (tool is workspace-wide). | Run unconditionally. |
| modified | `_anvil-impact-include modified` | If `--skip`: exit 0. Otherwise run the check's complete selected scope without package splicing. | Run the complete selected scope. |
| affected | `_anvil-impact-include affected` | If `--skip`: exit 0. Otherwise splice the value into the cargo invocation. | Default to `--workspace`. |
| required | `_anvil-impact-include required` | If `--skip`: exit 0. Otherwise splice the value into the cargo invocation. | Default to `--workspace`. |
| unscoped | *(none)* | Always run. | Always run. |
Expand Down
14 changes: 13 additions & 1 deletion crates/cargo-anvil/docs/design/local.md
Original file line number Diff line number Diff line change
Expand Up @@ -489,7 +489,7 @@ Each check requests one cargo-delta **category** — the selector it passes to

| Category | What recipes do with it |
|------------|------------------------------------------------------------------------------------------------|
| `modified` | `--skip` → recipe exits 0. Otherwise: run unconditionally (modified-category tools are workspace-wide). |
| `modified` | `--skip` → recipe exits 0. Otherwise: run the check's complete selected scope without package splicing. |
| `affected` | `--skip` → recipe exits 0. Otherwise: splice the value into the cargo invocation, defaulting to `--workspace` when empty. |
| `required` | Same semantics as affected, but consumed by recipes that need the transitive dep graph in scope (doc-build, cargo-hack, udeps). |

Expand All @@ -514,6 +514,18 @@ identifier '<name>'` to stderr and exits non-zero rather than guessing (and risk
silently under-scoped tier that skips a check). Failing hard surfaces the mapping gap so it
gets fixed, instead of masking it behind a silently full-workspace run.

A typical modified-tier recipe checks every workspace member while keeping each
rustfmt child command bounded. Unlike `cargo fmt --all`, this intentionally does
not discover non-member local path dependencies:

```just
anvil-fmt:
$include = (& "{{ just_executable() }}" _anvil-impact-include modified)
if ($include -eq '--skip') { exit 0 }
cargo each --workspace --keep-going '--' \
cargo '+{{ rust_nightly }}' fmt --manifest-path '{manifest}' --check
```

The mapping from check to bucket is fixed in the catalog (see
[checks.md §5](./checks.md#5-impact-scoping-check--include-mapping)). Unscoped checks —
`pr-title`, `deny`, `audit`, `aprz`, `mutants-full`, and the repo-level-input checks
Expand Down
Loading
Loading