From b228e46d4d02c6093d6a0e8219203d2735f0cdc5 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Fri, 28 Aug 2026 19:18:01 +0000 Subject: [PATCH] chore(deps): update workspace dependencies and docs Refresh workspace dependencies and pinned development tools, including the migration from cel-interpreter to cel, and adapt affected APIs. Unify README generation and checking, preserve cargo-gamma process and unsafe-boundary rationale in design docs, explain mutation exclusions and the CodeQL parser pin, and remove the obsolete cargo-workspaces installation. Keep incremental cache locks alive from cache adoption through workspace preparation, with command-level concurrency coverage that proves contention and subsequent release. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 52e352a5-2ebd-47c9-9a63-0bc23be56154 --- .config/nextest.toml | 24 +-- .github/workflows/main.yml | 2 +- Cargo.lock | 139 +++++++------ Cargo.toml | 63 +++--- constants.env | 17 +- crates/cargo-anvil/Cargo.toml | 2 +- crates/cargo-anvil/README.md | 2 +- crates/cargo-anvil/docs/design/checks.md | 2 +- .../src/anvil/artifacts/justfile.rs | 6 +- .../justfiles/anvil/checks/bolero.just | 2 +- .../justfiles/anvil/checks/readme-check.just | 47 +++-- .../templates/justfiles/anvil/versions.just | 12 +- crates/cargo-anvil/tests/impact.rs | 21 ++ .../snapshots/snapshots__ado_backend.snap | 61 +++--- .../snapshots/snapshots__github_backend.snap | 61 +++--- .../snapshots/snapshots__local_only.snap | 61 +++--- crates/cargo-aprz-lib/Cargo.toml | 2 +- crates/cargo-aprz-lib/src/expr/evaluator.rs | 8 +- crates/cargo-aprz-lib/src/expr/expression.rs | 2 +- .../src/facts/crates/tables/mod.rs | 32 +-- crates/cargo-coverage-gate/README.md | 2 +- crates/cargo-gamma-attrs-impl/Cargo.toml | 4 + crates/cargo-gamma-attrs-impl/README.md | 42 +--- crates/cargo-gamma-attrs-impl/docs/DESIGN.md | 5 +- crates/cargo-gamma-attrs-impl/src/lib.rs | 1 + crates/cargo-gamma-engine/Cargo.toml | 5 + crates/cargo-gamma-engine/README.md | 10 +- crates/cargo-gamma-engine/docs/DESIGN.md | 4 +- .../src/ops/collect/collector.rs | 6 +- .../src/ops/collect/defaults.rs | 2 +- crates/cargo-gamma-lib/Cargo.toml | 5 + crates/cargo-gamma-lib/README.md | 8 +- crates/cargo-gamma-lib/docs/DESIGN.md | 3 +- crates/cargo-gamma-lib/src/commands/run.rs | 3 +- .../cargo-gamma-lib/src/exec/build/tests.rs | 3 +- crates/cargo-gamma-lib/src/exec/measure.rs | 24 ++- crates/cargo-gamma-lib/src/exec/mod.rs | 1 + crates/cargo-gamma-lib/src/exec/verdict.rs | 2 +- .../cargo-gamma-lib/src/exec/verdict/hubs.rs | 19 +- crates/cargo-gamma-lib/src/exec/workspace.rs | 22 +- crates/cargo-gamma-lib/src/testing.rs | 83 +++++++- crates/cargo-gamma-lib/tests/session.rs | 47 +++++ crates/cargo-gamma-process/Cargo.toml | 4 + crates/cargo-gamma-process/README.md | 47 +---- crates/cargo-gamma-process/docs/DESIGN.md | 18 +- crates/cargo-gamma-rt/Cargo.toml | 4 + crates/cargo-gamma-rt/README.md | 190 +----------------- crates/cargo-gamma-rt/docs/DESIGN.md | 2 + crates/cargo-gamma-rt/src/lib.rs | 1 + crates/cargo-gamma-unsafe/Cargo.toml | 4 + crates/cargo-gamma-unsafe/README.md | 23 +-- crates/cargo-gamma-unsafe/docs/DESIGN.md | 18 +- crates/cargo-heather/README.md | 2 +- deny.toml | 8 +- gamma.toml | 9 +- justfiles/anvil/checks/bolero.just | 2 +- justfiles/anvil/checks/readme-check.just | 47 +++-- justfiles/anvil/versions.just | 12 +- justfiles/basic.just | 6 +- justfiles/setup.just | 1 - 60 files changed, 666 insertions(+), 599 deletions(-) diff --git a/.config/nextest.toml b/.config/nextest.toml index 08f7cd2c..054cb657 100644 --- a/.config/nextest.toml +++ b/.config/nextest.toml @@ -1,20 +1,20 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. -# Every case in cargo-anvil's pr_title suite runs the anvil-pr-title recipe, -# which starts a PowerShell process. PowerShell intermittently aborts while -# parsing an assembly name during startup (PowerShell/PowerShell#26940), and -# concurrent interpreter startups on constrained runners hit that abort often -# enough to fail the suite. The suite carries `#[serial]` for the plain libtest -# runners (`cargo test`, `cargo careful test`), but nextest runs each case in -# its own process, where an in-process lock cannot serialize anything. This -# group is what serializes them under nextest. +# Every case in cargo-anvil's pr_title and impact suites starts PowerShell. +# PowerShell intermittently aborts while parsing an assembly name during +# startup (PowerShell/PowerShell#26940), and concurrent interpreter startups +# on constrained runners hit that abort often enough to fail the suite. The +# suites carry `#[serial]` for plain libtest runners (`cargo test`, `cargo +# careful test`), but nextest runs each case in its own process, where an +# in-process lock cannot serialize anything. This group serializes them under +# nextest. # # `binary(...)` rather than `test(...)`: the latter filters test names, none of -# which contain `pr_title`. +# which contain the integration-test binary names. [test-groups] -pr-title = { max-threads = 1 } +powershell = { max-threads = 1 } [[profile.default.overrides]] -filter = 'binary(pr_title)' -test-group = 'pr-title' +filter = 'binary(pr_title) | binary(impact)' +test-group = 'powershell' diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3465cdcd..d61280d1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -65,7 +65,7 @@ jobs: enable-sccache: true rust-toolchain: RUST_LATEST rust-components: clippy, rustfmt - cargo-tools: CARGO_DOC2README_VERSION, CARGO_WORKSPACES_VERSION, CARGO_ENSURE_NO_DEFAULT_FEATURES_VERSION, CARGO_ENSURE_NO_CYCLIC_DEPS_VERSION, CARGO_DENY_VERSION, CARGO_SORT_VERSION, JUST_VERSION + cargo-tools: CARGO_DOC2README_VERSION, CARGO_ENSURE_NO_DEFAULT_FEATURES_VERSION, CARGO_ENSURE_NO_CYCLIC_DEPS_VERSION, CARGO_DENY_VERSION, CARGO_SORT_VERSION, JUST_VERSION # execute - name: Clippy diff --git a/Cargo.lock b/Cargo.lock index 3d5d250a..2ff420ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -68,7 +68,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -79,14 +79,14 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "antlr4rust" -version = "0.3.0-rc2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d240d49ee89063f90fa0cb18aead41a5893cd544a1785983dc3bf5c3d5faa58b" +checksum = "093d520274bfff7278d776f7ea12981a0a0a6f96db90964658e0f38fc6e9a6a6" dependencies = [ "better_any", "bit-set", @@ -263,6 +263,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-buffer" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" +dependencies = [ + "hybrid-array", +] + [[package]] name = "bolero" version = "0.13.4" @@ -310,7 +319,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98a5782f2650f80d533f58ec339c6dce4cc5428f9c2755894f98156f52af81f2" dependencies = [ "bolero-generator-derive", + "either", + "getrandom 0.3.4", "rand_core 0.9.5", + "rand_xoshiro", ] [[package]] @@ -438,7 +450,7 @@ dependencies = [ "bytes", "camino", "cargo_metadata", - "cel-interpreter", + "cel", "chrono", "clap", "compact_str", @@ -576,6 +588,7 @@ version = "0.1.0" dependencies = [ "blake3", "bolero", + "bolero-engine", "camino", "compact_str", "proc-macro2", @@ -593,6 +606,7 @@ version = "0.1.0" dependencies = [ "blake3", "bolero", + "bolero-engine", "camino", "cargo-gamma-attrs-impl", "cargo-gamma-engine", @@ -716,27 +730,18 @@ dependencies = [ ] [[package]] -name = "cel-interpreter" -version = "0.10.0" +name = "cel" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a76c07820046cc8239526fceec6df147a979ae48644dbad274fc3ce38ab0973b" +checksum = "9f93082a93da8fd78394852602ced1e2e7754ed8c29dc813fa80b01a1baf9032" dependencies = [ - "cel-parser", + "antlr4rust", "chrono", + "lazy_static", "nom", - "paste", + "pastey", "serde", - "thiserror 1.0.69", -] - -[[package]] -name = "cel-parser" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "546fb134998490c5c47fc7a29c7535e725d2e403f172040e8f263d0b318bff5f" -dependencies = [ - "antlr4rust", - "lazy_static", + "thiserror 2.0.20", ] [[package]] @@ -999,6 +1004,15 @@ dependencies = [ "typenum", ] +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + [[package]] name = "csv" version = "1.4.0" @@ -1099,8 +1113,18 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", - "crypto-common", + "block-buffer 0.10.4", + "crypto-common 0.1.7", +] + +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.1", + "crypto-common 0.2.2", ] [[package]] @@ -1121,7 +1145,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -1225,7 +1249,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -1278,9 +1302,9 @@ checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" [[package]] name = "flate2" -version = "1.1.9" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +checksum = "6e634e2e0ebac1ee034020da1ca582e17ffe4e0f5e985823721e168928136dcb" dependencies = [ "crc32fast", "libz-sys", @@ -2436,6 +2460,15 @@ dependencies = [ "serde", ] +[[package]] +name = "hybrid-array" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707114b52a152fa7bdb290cd7cd5912d9467273b6d74e21b8d81aca1f8533f6b" +dependencies = [ + "typenum", +] + [[package]] name = "hyper" version = "1.11.0" @@ -3030,9 +3063,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.9" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +checksum = "b63fbc4a50860e98e7b2aa7804ded1db5cbc3aff9193adaff57a6931bf7c4b4c" dependencies = [ "adler2", "simd-adler32", @@ -3055,7 +3088,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "536bfad37a309d62069485248eeaba1e8d9853aaf951caaeaed0585a95346f08" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -3086,9 +3119,9 @@ dependencies = [ [[package]] name = "mutants" -version = "0.0.3" +version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc0287524726960e07b119cebd01678f852f147742ae0d925e6a520dca956126" +checksum = "add0ac067452ff1aca8c5002111bd6b1c895baee6e45fcbc44e0193aea17be56" [[package]] name = "nix" @@ -3232,14 +3265,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] name = "owo-colors" -version = "4.3.0" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d211803b9b6b570f68772237e415a029d5a50c65d382910b879fb19d3271f94d" +checksum = "13c45bb4a6ae1280ec0803b1ef9d3455eb50f01efbbe1447ab020f1d54fba9d8" [[package]] name = "parking_lot" @@ -3264,12 +3297,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - [[package]] name = "pastey" version = "0.2.3" @@ -3468,7 +3495,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -3801,9 +3828,9 @@ dependencies = [ [[package]] name = "rust_xlsxwriter" -version = "0.98.2" +version = "0.99.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "093d730a6f64f9620e047b2de2876e61a691fbcaca3aaf436edade9b897c7a27" +checksum = "c073c0bd3868defc46035bf95b2e70389cefc59c528b8674fe90594c9c2b3f6f" dependencies = [ "zip", ] @@ -3940,7 +3967,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -3997,7 +4024,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -4210,7 +4237,7 @@ checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8" dependencies = [ "cfg-if", "cpufeatures 0.2.17", - "digest", + "digest 0.10.7", ] [[package]] @@ -4219,19 +4246,19 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89f599ac0c323ebb1c6082821a54962b839832b03984598375bff3975b804423" dependencies = [ - "digest", + "digest 0.10.7", "sha1", ] [[package]] name = "sha2" -version = "0.10.9" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" dependencies = [ "cfg-if", - "cpufeatures 0.2.17", - "digest", + "cpufeatures 0.3.0", + "digest 0.11.3", ] [[package]] @@ -4338,7 +4365,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -4495,7 +4522,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -4505,7 +4532,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874" dependencies = [ "rustix", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -5205,7 +5232,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index da544043..2d11ae6e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,12 +23,14 @@ homepage = "https://github.com/microsoft/ox-tools" [workspace.dependencies] # external dependencies -anyhow = { version = "1.0.100", default-features = false } -assert_cmd = { version = "2.2.0", default-features = false } +anyhow = { version = "1.0.104", default-features = false } +assert_cmd = { version = "2.2.2", default-features = false } async-once-cell = { version = "0.5", default-features = false } -blake3 = { version = "1.8.5", default-features = false } +blake3 = { version = "1.8.7", default-features = false } bolero = { version = "0.13.4", default-features = false } -bytes = { version = "1.11.1", default-features = false } +# bolero-libfuzzer 0.13.0 calls bolero_engine::any without enabling the feature itself. +bolero-engine = { version = "0.13.4", default-features = false } +bytes = { version = "1.12.1", default-features = false } camino = { version = "1.2.5", default-features = false } # local dependencies cargo-aprz-lib = { path = "crates/cargo-aprz-lib", default-features = false, version = "1.1.0" } @@ -40,46 +42,47 @@ cargo-gamma-rt = { path = "crates/cargo-gamma-rt", default-features = false, ver cargo-gamma-unsafe = { path = "crates/cargo-gamma-unsafe", default-features = false, version = "0.1.0" } cargo-heather = { path = "crates/cargo-heather", default-features = false, version = "0.2.1" } cargo_metadata = { version = "0.23.1", default-features = false } -cel-interpreter = { version = "0.10.0", default-features = false } -chrono = { version = "0.4.40", default-features = false } -clap = { version = "4.5.60", default-features = false } -clap_complete = { version = "4.6.8", default-features = false } +cel = { version = "0.14.4", default-features = false } +chrono = { version = "0.4.45", default-features = false } +clap = { version = "4.6.6", default-features = false } +clap_complete = { version = "4.6.9", default-features = false } compact_str = { version = "0.10.0", default-features = false } csv = { version = "1.4.0", default-features = false } directories = { version = "6.0.0", default-features = false } duct = { version = "1.1.1", default-features = false } env_logger = { version = "0.11.11", default-features = false } -flate2 = { version = "1.1.9", default-features = false } -futures = { version = "0.3.31", default-features = false } -futures-util = { version = "0.3.31", default-features = false } +flate2 = { version = "1.1.10", default-features = false } +futures = { version = "0.3.34", default-features = false } +futures-util = { version = "0.3.34", default-features = false } humantime-serde = { version = "1.1.1", default-features = false } ignore = { version = "0.4.33", default-features = false } indicatif = { version = "0.18.6", default-features = false } -insta = { version = "1.44.1", default-features = false } +insta = { version = "1.48.0", default-features = false } layered = { version = "0.3.6", default-features = false } lcov = { version = "0.8.2", default-features = false } -libc = { version = "0.2.178", default-features = false } -log = { version = "0.4.33", default-features = false } +libc = { version = "0.2.189", default-features = false } +log = { version = "0.4.34", default-features = false } loom = { version = "0.7.2", default-features = false } -many_cpus = { version = "2.1.0", default-features = false } +many_cpus = { version = "2.4.16", default-features = false } mimalloc = { version = "0.1.52", default-features = false } mmap-rs = { version = "0.7.0", default-features = false } -mutants = { version = "0.0.3", default-features = false } +mutants = { version = "0.0.4", default-features = false } ohno = { version = "0.5.0", default-features = false } -owo-colors = { version = "4.3.0", default-features = false } +owo-colors = { version = "4.4.0", default-features = false } pastey = { version = "0.2.3", default-features = false } percent-encoding = { version = "2.3.2", default-features = false } petgraph = { version = "0.8.3", default-features = false } predicates = { version = "3.1.4", default-features = false } -proc-macro2 = { version = "1.0.103", default-features = false } +proc-macro2 = { version = "1.0.107", default-features = false } +# 0.0.349 includes a parser fixture that crashes CodeQL 2.26.4; retry the bump with a newer CodeQL release. ra_ap_syntax = { version = "0.0.331", default-features = false } -rapidhash = { version = "4.1.1", default-features = false } +rapidhash = { version = "4.5.1", default-features = false } reflink-copy = { version = "0.1.30", default-features = false } -regex = { version = "1.12.2", default-features = false } +regex = { version = "1.13.1", default-features = false } reqwest = { version = "0.13.4", default-features = false } rmp-serde = { version = "1.3.1", default-features = false } -rust_xlsxwriter = { version = "0.98.2", default-features = false } -rustc-hash = { version = "2.1.0", default-features = false } +rust_xlsxwriter = { version = "0.99.0", default-features = false } +rustc-hash = { version = "2.1.3", default-features = false } rustc_lexer = { version = "0.1.0", default-features = false } rustdoc-types-v50 = { package = "rustdoc-types", version = "0.50.0", default-features = false } rustdoc-types-v51 = { package = "rustdoc-types", version = "0.51.0", default-features = false } @@ -92,10 +95,10 @@ rustdoc-types-v57 = { package = "rustdoc-types", version = "0.57.4", default-fea rustsec = { version = "0.33.0", default-features = false } seatbelt = { version = "0.8.0", default-features = false } semver = { version = "1.0.28", default-features = false } -serde = { version = "1.0.228", default-features = false } -serde_json = { version = "1.0.145", default-features = false } +serde = { version = "1.0.229", default-features = false } +serde_json = { version = "1.0.151", default-features = false } serial_test = { version = "4.0.1", default-features = false } -sha2 = { version = "0.10.9", default-features = false } +sha2 = { version = "0.11.0", default-features = false } strsim = { version = "0.11.1", default-features = false } strum = { version = "0.28.0", default-features = false } syn = { version = "3.0.4", default-features = false } @@ -103,11 +106,11 @@ tar = { version = "0.4.46", default-features = false } tempfile = { version = "3.27.0", default-features = false } terminal_size = { version = "0.4.4", default-features = false } tick = { version = "0.6.0", default-features = false } -tokio = { version = "1.48.0", default-features = false } -toml = { version = "1.1.2", default-features = false } -toml_edit = { version = "0.25.12", default-features = false, features = ["parse", "display"] } -tracing = { version = "0.1.41", default-features = false } -tracing-subscriber = { version = "0.3.20", default-features = false } +tokio = { version = "1.53.1", default-features = false } +toml = { version = "1.1.4", default-features = false } +toml_edit = { version = "0.25.13", default-features = false, features = ["parse", "display"] } +tracing = { version = "0.1.44", default-features = false } +tracing-subscriber = { version = "0.3.23", default-features = false } url = { version = "2.5.8", default-features = false } vlen = { version = "0.4.6", default-features = false } walkdir = { version = "2.5.0", default-features = false } diff --git a/constants.env b/constants.env index 318ae2c3..f6bdda2f 100644 --- a/constants.env +++ b/constants.env @@ -17,20 +17,19 @@ RUST_NIGHTLY_EXTERNAL_TYPES=nightly-2026-03-20 # Cargo tools CARGO_CAREFUL_VERSION=0.4.10 CARGO_CHECK_EXTERNAL_TYPES_VERSION=0.5.0 -CARGO_DENY_VERSION=0.19.8 -CARGO_DOC2README_VERSION=0.7.2 +CARGO_DENY_VERSION=0.20.2 +CARGO_DOC2README_VERSION=0.7.3 CARGO_ENSURE_NO_CYCLIC_DEPS_VERSION=0.2.0 CARGO_ENSURE_NO_DEFAULT_FEATURES_VERSION=1.1.0 CARGO_HACK_VERSION=0.6.45 -CARGO_LLVM_COV_VERSION=0.8.7 -CARGO_MUTANTS_VERSION=27.0.0 -CARGO_NEXTEST_VERSION=0.9.137 -CARGO_SEMVER_CHECKS_VERSION=0.49.0 +CARGO_LLVM_COV_VERSION=0.9.0 +CARGO_MUTANTS_VERSION=27.1.0 +CARGO_NEXTEST_VERSION=0.9.143 +CARGO_SEMVER_CHECKS_VERSION=0.50.0 CARGO_SORT_VERSION=2.1.4 CARGO_SPELLCHECK_VERSION=0.15.7 CARGO_UDEPS_VERSION=0.1.61 -CARGO_WORKSPACES_VERSION=0.4.2 # Other tools -JUST_VERSION=1.46.0 -SCCACHE_VERSION=v0.13.0 +JUST_VERSION=1.58.0 +SCCACHE_VERSION=v0.17.0 diff --git a/crates/cargo-anvil/Cargo.toml b/crates/cargo-anvil/Cargo.toml index 0cbfd34d..6214d4b8 100644 --- a/crates/cargo-anvil/Cargo.toml +++ b/crates/cargo-anvil/Cargo.toml @@ -32,7 +32,7 @@ path = "src/main.rs" clap = { workspace = true, features = ["derive", "std", "help", "usage", "error-context", "string"] } mutants = { workspace = true } ohno = { workspace = true, features = ["app-err"] } -sha2 = { workspace = true, features = ["std"] } +sha2.workspace = true toml_edit = { workspace = true } tracing = { workspace = true } tracing-subscriber = { workspace = true, features = ["fmt"] } diff --git a/crates/cargo-anvil/README.md b/crates/cargo-anvil/README.md index e8843c60..a656e95e 100644 --- a/crates/cargo-anvil/README.md +++ b/crates/cargo-anvil/README.md @@ -445,7 +445,7 @@ And `docs/verification.md` for the continuous-validation strategy. This crate was developed as part of The Oxidizer Project. Browse this crate's source code. - [__cargo_doc2readme_dependencies_info]: ggGmYW0CYXZlMC43LjJhdIQbFhzZ8rzWNNYbuRaDSGWynFgbH4PMdoT7GNcbVwNPtPjAhvFhYvRhcoQblcBzF-_WZVYbCN9Rt1pYQLsblkUTM0oENsMbNe4wSAldeq9hZIGDa2NhcmdvLWFudmlsZTAuNS4wa2NhcmdvX2Fudmls + [__cargo_doc2readme_dependencies_info]: ggGmYW0CYXZlMC43LjNhdIQbFhzZ8rzWNNYbuRaDSGWynFgbH4PMdoT7GNcbVwNPtPjAhvFhYvRhcoQblcBzF-_WZVYbCN9Rt1pYQLsblkUTM0oENsMbNe4wSAldeq9hZIGDa2NhcmdvLWFudmlsZTAuNS4wa2NhcmdvX2Fudmls [__link0]: https://crates.io/crates/cargo-delta [__link1]: https://crates.io/crates/cargo-spellcheck [__link2]: https://crates.io/crates/cargo-coverage-gate diff --git a/crates/cargo-anvil/docs/design/checks.md b/crates/cargo-anvil/docs/design/checks.md index 9e5f916e..3496e885 100644 --- a/crates/cargo-anvil/docs/design/checks.md +++ b/crates/cargo-anvil/docs/design/checks.md @@ -164,7 +164,7 @@ that provided the strongest version of the check. | `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 | | `doc-build` | `RUSTDOCFLAGS='-D warnings' cargo doc --workspace --all-features --no-deps` | oxidizer-github | -| `readme-check` | `cargo doc2readme --check` for each publishable crate that does not opt out through `[package.metadata.ox-gen-readme]`; library rustdoc is preferred, with binary rustdoc used for bin-only crates | oxidizer-github | +| `readme-check` | `cargo doc2readme --check` for each publishable crate that does not opt out through `[package.metadata.ox-gen-readme]`; generation and checking share one crate-selection path, library or proc-macro rustdoc is preferred, and binary rustdoc is used for bin-only crates | oxidizer-github | | `spellcheck` | `cargo spellcheck check --code 1` | oxidizer-github | | `pr-title` | Repository policy regex applied to the title in the `PR_TITLE` env var. The accepted types (`feat`, `fix`, `chore`, `docs`, `refactor`, `test`, `build`, `ci`, `perf`, `revert`) are a deliberate subset of Conventional Commits, not the complete grammar. The title must occupy a single line, so trailing content after the description is rejected. A rejected title reports the accepted title formats and the case-insensitive type names, so the author can correct the title from the check output alone. Skipped only outside a pull request context, where `PR_TITLE` is unset or empty (local runs and cloud builds that are not pull request builds); an invalid title or failure to retrieve a known PR's title fails loudly. GitHub supplies the event title directly. ADO resolves it through the REST API because `System.PullRequest.Title` does not exist. | oxidizer-github | | `deny` | `cargo deny check` | all | diff --git a/crates/cargo-anvil/src/anvil/artifacts/justfile.rs b/crates/cargo-anvil/src/anvil/artifacts/justfile.rs index b870538a..8df8e822 100644 --- a/crates/cargo-anvil/src/anvil/artifacts/justfile.rs +++ b/crates/cargo-anvil/src/anvil/artifacts/justfile.rs @@ -364,7 +364,7 @@ mod tests { let checks = all_check_bodies(); for needle in [ "anvil-bolero: target discovery failed", - "anvil-readme-check: cargo metadata failed", + "${prefix}: cargo metadata failed", "running tests without coverage for opted-out packages", "all affected packages opted out of coverage", "could not resolve the cargo-careful executable", @@ -379,6 +379,10 @@ mod tests { checks.contains("bolero list --profile release --package $packageName"), "bolero discovery must use the execution profile once per affected package" ); + assert!( + checks.contains("bolero test --profile release --engine libfuzzer"), + "bolero execution must use the profile option accepted by the pinned cargo-bolero" + ); assert!( !checks.contains("bolero list @bareArgs"), "bolero discovery must not pass repeated --package arguments" diff --git a/crates/cargo-anvil/templates/justfiles/anvil/checks/bolero.just b/crates/cargo-anvil/templates/justfiles/anvil/checks/bolero.just index 37f7d21b..d23fb3a9 100644 --- a/crates/cargo-anvil/templates/justfiles/anvil/checks/bolero.just +++ b/crates/cargo-anvil/templates/justfiles/anvil/checks/bolero.just @@ -103,7 +103,7 @@ anvil-bolero: anvil-bolero-validate-prereqs anvil-impact exit 1 } Write-Host "anvil-bolero: ${packageName}::${testName}" - & cargo '+{{ rust_nightly }}' bolero test --release --engine libfuzzer -T 60s --package $packageName $testName + & cargo '+{{ rust_nightly }}' bolero test --profile release --engine libfuzzer -T 60s --package $packageName $testName if ($LASTEXITCODE -ne 0) { $failed = $true } } if ($failed) { exit 1 } diff --git a/crates/cargo-anvil/templates/justfiles/anvil/checks/readme-check.just b/crates/cargo-anvil/templates/justfiles/anvil/checks/readme-check.just index 1d258e25..5ec58e43 100644 --- a/crates/cargo-anvil/templates/justfiles/anvil/checks/readme-check.just +++ b/crates/cargo-anvil/templates/justfiles/anvil/checks/readme-check.just @@ -22,20 +22,28 @@ # `[package.metadata.ox-gen-readme]\ndisable = true` to their # Cargo.toml. anvil skips those crates. # -# Bin-only crates have no library rustdoc to base a README on, so they -# are skipped as well (cargo doc2readme requires a library target). +# Library or proc-macro rustdoc is preferred when a crate has one; bin-only +# crates use their binary rustdoc so every publishable crate follows the same +# generation policy. # TODO(anvil-runner): even after a `cargo ox-run` helper absorbs the # skip/splat preamble, this recipe stays multi-step: per-crate -# iteration over library targets, cargo-metadata-driven opt-outs +# iteration over eligible targets, cargo-metadata-driven opt-outs # (publish=false, [package.metadata.ox-gen-readme] disable), per-crate # Push-Location into the crate dir (cargo-doc2readme is CWD-sensitive # rather than --manifest-path-driven), and per-crate template-path -# resolution. The recipe iterates the full library-crate set every run. +# resolution. The recipe iterates the full eligible-crate set every run. -# Check that crate README files are up to date. +# Generate or check every eligible crate README with the same selection policy. [script("pwsh", "-NoProfile")] -anvil-readme-check: anvil-readme-check-validate-prereqs +_anvil-readme mode: anvil-readme-check-validate-prereqs $ErrorActionPreference = 'Stop' + $action = '{{ mode }}' + if ($action -notin @('generate', 'check')) { + [Console]::Error.WriteLine("anvil-readme: invalid mode '$action'; expected 'generate' or 'check'") + exit 2 + } + $check = $action -eq 'check' + $prefix = "anvil-readme-${action}" # Detect a workspace-level README template. Two conventional # locations: crates/README.j2 (cargo-workspaces idiom) or # README.j2 at repo root. @@ -43,26 +51,28 @@ anvil-readme-check: anvil-readme-check-validate-prereqs foreach ($candidate in 'crates/README.j2', 'README.j2') { if (Test-Path $candidate) { $template = (Resolve-Path $candidate).Path; break } } - # Iterate every library crate, then drop bin-only crates and opt-outs. + # Iterate every crate with rustdoc, then drop private crates and opt-outs. # Unscoped: a change to the workspace-level README template is a repo-level # input cargo-delta cannot map to a package, so this check always runs the - # full library set rather than an impact subset. + # full eligible-crate set rather than an impact subset. $metadataJson = & cargo metadata --no-deps --format-version 1 if ($LASTEXITCODE -ne 0) { - Write-Error 'anvil-readme-check: cargo metadata failed' + Write-Error "${prefix}: cargo metadata failed" exit $LASTEXITCODE } $meta = $metadataJson | ConvertFrom-Json $hadFailure = $false foreach ($p in $meta.packages) { $name = $p.name - if (-not ($p.targets | Where-Object { $_.kind -contains 'lib' })) { continue } + $hasLib = [bool]($p.targets | Where-Object { ($_.kind -contains 'lib') -or ($_.kind -contains 'proc-macro') }) + $hasBin = [bool]($p.targets | Where-Object { $_.kind -contains 'bin' }) + if (-not $hasLib -and -not $hasBin) { continue } # Skip private crates (publish = false). They aren't released # and rarely have a polished README. Mirrors the # `cargo workspaces exec --ignore-private` idiom adopters # commonly use. if ($p.publish -is [array] -and $p.publish.Count -eq 0) { - Write-Host "anvil-readme-check: $name (skipped: publish = false)" + Write-Host "${prefix}: $name (skipped: publish = false)" continue } $disabled = $false @@ -70,13 +80,13 @@ anvil-readme-check: anvil-readme-check-validate-prereqs $disabled = $true } if ($disabled) { - Write-Host "anvil-readme-check: $name (opted out via [package.metadata.ox-gen-readme])" + Write-Host "${prefix}: $name (opted out via [package.metadata.ox-gen-readme])" continue } - Write-Host "anvil-readme-check: $name" + Write-Host "${prefix}: $name" # cargo doc2readme writes / compares relative to its CWD (not - # --manifest-path), so chdir into the crate before invoking - # --check. We also compute a per-crate relative path to the + # --manifest-path), so chdir into the crate before invoking it. + # We also compute a per-crate relative path to the # workspace-level template so the same template file works for # every crate (parallels the cargo-workspaces idiom). $crateDir = Split-Path -Parent $p.manifest_path @@ -87,7 +97,9 @@ anvil-readme-check: anvil-readme-check-validate-prereqs } else { $null } - $args = @('doc2readme', '--check') + $target = if ($hasLib) { '--lib' } else { '--bin' } + $args = @('doc2readme', $target) + if ($check) { $args += '--check' } if ($relTemplate) { $args += @('--template', $relTemplate) } & cargo @args if ($LASTEXITCODE -ne 0) { $hadFailure = $true } @@ -97,6 +109,9 @@ anvil-readme-check: anvil-readme-check-validate-prereqs } if ($hadFailure) { exit 1 } +# Check that crate README files are up to date. +anvil-readme-check: (_anvil-readme "check") + # Install prerequisites for the `anvil-readme-check` recipe. [group("anvil-setup")] anvil-readme-check-setup installer="install": (anvil-tool-cargo-doc2readme-install installer) diff --git a/crates/cargo-anvil/templates/justfiles/anvil/versions.just b/crates/cargo-anvil/templates/justfiles/anvil/versions.just index 564a3b90..0a4aeaa1 100644 --- a/crates/cargo-anvil/templates/justfiles/anvil/versions.just +++ b/crates/cargo-anvil/templates/justfiles/anvil/versions.just @@ -56,16 +56,16 @@ cargo_careful_version := "0.4.10" cargo_check_external_types_version := "0.5.0" cargo_coverage_gate_version := "0.2.0" cargo_delta_version := "0.3.1" -cargo_deny_version := "0.19.8" -cargo_doc2readme_version := "0.7.2" +cargo_deny_version := "0.20.2" +cargo_doc2readme_version := "0.7.3" cargo_ensure_no_cyclic_deps_version := "0.2.0" cargo_ensure_no_default_features_version := "1.1.0" cargo_hack_version := "0.6.45" cargo_heather_version := "0.2.1" -cargo_llvm_cov_version := "0.8.7" -cargo_mutants_version := "27.0.0" -cargo_nextest_version := "0.9.137" -cargo_semver_checks_version := "0.49.0" +cargo_llvm_cov_version := "0.9.0" +cargo_mutants_version := "27.1.0" +cargo_nextest_version := "0.9.143" +cargo_semver_checks_version := "0.50.0" cargo_sort_version := "2.1.4" cargo_spellcheck_version := "0.15.7" cargo_udeps_version := "0.1.61" diff --git a/crates/cargo-anvil/tests/impact.rs b/crates/cargo-anvil/tests/impact.rs index 42363af8..ad0bd838 100644 --- a/crates/cargo-anvil/tests/impact.rs +++ b/crates/cargo-anvil/tests/impact.rs @@ -36,6 +36,7 @@ use std::{env, fs}; use cargo_anvil::Catalog; use cargo_anvil::test_support::{Cli, run_update}; +use serial_test::serial; use tempfile::TempDir; fn write(path: &Path, contents: &str) { @@ -265,6 +266,7 @@ fn run_impact(root: &Path) -> String { } #[test] +#[serial] fn impact_cache_regenerates_per_key_and_reuses_when_unchanged() { if !tools_available() { return; @@ -356,6 +358,7 @@ fn impact_cache_regenerates_per_key_and_reuses_when_unchanged() { } #[test] +#[serial] fn baseline_regenerates_when_delta_config_changes_without_moving_the_base() { if !tools_available() { return; @@ -414,6 +417,7 @@ fn baseline_regenerates_when_delta_config_changes_without_moving_the_base() { } #[test] +#[serial] fn current_snapshot_ignores_tracked_target_changes_without_moving_head() { if !tools_available() { return; @@ -473,6 +477,7 @@ fn current_snapshot_ignores_tracked_target_changes_without_moving_head() { } #[test] +#[serial] fn impact_empty_output_when_head_equals_base() { if !tools_available() { return; @@ -516,6 +521,7 @@ fn impact_empty_output_when_head_equals_base() { } #[test] +#[serial] fn impact_off_short_circuits_without_computing() { if !core_tools_available() { return; @@ -548,6 +554,7 @@ fn impact_off_short_circuits_without_computing() { } #[test] +#[serial] fn impact_widens_to_full_workspace_when_working_tree_is_dirty() { if !tools_available() { return; @@ -616,6 +623,7 @@ fn impact_widens_to_full_workspace_when_working_tree_is_dirty() { } #[test] +#[serial] fn impact_include_reads_zero_byte_modified_file_without_throwing() { if !tools_available() { return; @@ -651,6 +659,7 @@ fn impact_include_reads_zero_byte_modified_file_without_throwing() { } #[test] +#[serial] fn impact_dirty_tree_widens_without_needing_a_resolvable_base() { if !core_tools_available() { return; @@ -707,6 +716,7 @@ fn impact_dirty_tree_widens_without_needing_a_resolvable_base() { } #[test] +#[serial] fn impact_consume_mode_trusts_cache_without_recompute() { if !tools_available() { return; @@ -752,6 +762,7 @@ fn impact_consume_mode_trusts_cache_without_recompute() { } #[test] +#[serial] fn consumer_reuses_cache_with_unresolvable_base_only_under_consume() { if !tools_available() { return; @@ -809,6 +820,7 @@ fn consumer_reuses_cache_with_unresolvable_base_only_under_consume() { } #[test] +#[serial] fn impact_falls_back_to_full_workspace_when_base_has_no_workspace() { if !tools_available() { return; @@ -904,6 +916,7 @@ fn path_with_prefix(dir: &Path) -> OsString { } #[test] +#[serial] fn scoped_check_consumes_cached_package_list_and_skips_on_sentinel() { if !tools_available() { return; @@ -1000,6 +1013,7 @@ fn fake_cargo_with_metadata(dir: &Path, log: &Path, metadata_json: &Path) { } #[test] +#[serial] fn loom_runs_declared_targets_in_full_workspace_mode() { if !tools_available() { return; @@ -1047,6 +1061,7 @@ fn loom_runs_declared_targets_in_full_workspace_mode() { } #[test] +#[serial] fn invalid_anvil_impact_value_fails_loudly_without_computing() { if !core_tools_available() { return; @@ -1089,6 +1104,7 @@ fn invalid_anvil_impact_value_fails_loudly_without_computing() { } #[test] +#[serial] fn missing_base_ref_on_clean_tree_fails_with_fetch_guidance() { if !tools_available() { return; @@ -1121,6 +1137,7 @@ fn missing_base_ref_on_clean_tree_fails_with_fetch_guidance() { } #[test] +#[serial] fn shallow_clone_fails_with_unshallow_guidance() { if !tools_available() { return; @@ -1146,6 +1163,7 @@ fn shallow_clone_fails_with_unshallow_guidance() { } #[test] +#[serial] fn consume_without_downloaded_cache_fails_loudly() { if !core_tools_available() { return; @@ -1248,6 +1266,7 @@ fn run_format(root: &Path, tier: &str, impact_json_rel: &str) -> (String, String } #[test] +#[serial] fn impact_format_fails_hard_on_unmappable_name() { if !tools_available() { return; @@ -1272,6 +1291,7 @@ fn impact_format_fails_hard_on_unmappable_name() { } #[test] +#[serial] fn impact_format_maps_proc_macro_target_name_to_its_package() { if !tools_available() { return; @@ -1368,6 +1388,7 @@ fn snapshot_toolchain_probe(caller_toolchain: Option<&str>) -> Vec { } #[test] +#[serial] fn impact_snapshot_runs_baseline_under_active_toolchain_then_restores_caller_value() { if !core_tools_available() { return; diff --git a/crates/cargo-anvil/tests/snapshots/snapshots__ado_backend.snap b/crates/cargo-anvil/tests/snapshots/snapshots__ado_backend.snap index c0ab6168..80284f95 100644 --- a/crates/cargo-anvil/tests/snapshots/snapshots__ado_backend.snap +++ b/crates/cargo-anvil/tests/snapshots/snapshots__ado_backend.snap @@ -2887,7 +2887,7 @@ anvil-bolero: anvil-bolero-validate-prereqs anvil-impact exit 1 } Write-Host "anvil-bolero: ${packageName}::${testName}" - & cargo '+{{ rust_nightly }}' bolero test --release --engine libfuzzer -T 60s --package $packageName $testName + & cargo '+{{ rust_nightly }}' bolero test --profile release --engine libfuzzer -T 60s --package $packageName $testName if ($LASTEXITCODE -ne 0) { $failed = $true } } if ($failed) { exit 1 } @@ -4130,20 +4130,28 @@ anvil-pr-title-validate-prereqs: anvil-tool-pwsh-validate-prereqs # `[package.metadata.ox-gen-readme]\ndisable = true` to their # Cargo.toml. anvil skips those crates. # -# Bin-only crates have no library rustdoc to base a README on, so they -# are skipped as well (cargo doc2readme requires a library target). +# Library or proc-macro rustdoc is preferred when a crate has one; bin-only +# crates use their binary rustdoc so every publishable crate follows the same +# generation policy. # TODO(anvil-runner): even after a `cargo ox-run` helper absorbs the # skip/splat preamble, this recipe stays multi-step: per-crate -# iteration over library targets, cargo-metadata-driven opt-outs +# iteration over eligible targets, cargo-metadata-driven opt-outs # (publish=false, [package.metadata.ox-gen-readme] disable), per-crate # Push-Location into the crate dir (cargo-doc2readme is CWD-sensitive # rather than --manifest-path-driven), and per-crate template-path -# resolution. The recipe iterates the full library-crate set every run. +# resolution. The recipe iterates the full eligible-crate set every run. -# Check that crate README files are up to date. +# Generate or check every eligible crate README with the same selection policy. [script("pwsh", "-NoProfile")] -anvil-readme-check: anvil-readme-check-validate-prereqs +_anvil-readme mode: anvil-readme-check-validate-prereqs $ErrorActionPreference = 'Stop' + $action = '{{ mode }}' + if ($action -notin @('generate', 'check')) { + [Console]::Error.WriteLine("anvil-readme: invalid mode '$action'; expected 'generate' or 'check'") + exit 2 + } + $check = $action -eq 'check' + $prefix = "anvil-readme-${action}" # Detect a workspace-level README template. Two conventional # locations: crates/README.j2 (cargo-workspaces idiom) or # README.j2 at repo root. @@ -4151,26 +4159,28 @@ anvil-readme-check: anvil-readme-check-validate-prereqs foreach ($candidate in 'crates/README.j2', 'README.j2') { if (Test-Path $candidate) { $template = (Resolve-Path $candidate).Path; break } } - # Iterate every library crate, then drop bin-only crates and opt-outs. + # Iterate every crate with rustdoc, then drop private crates and opt-outs. # Unscoped: a change to the workspace-level README template is a repo-level # input cargo-delta cannot map to a package, so this check always runs the - # full library set rather than an impact subset. + # full eligible-crate set rather than an impact subset. $metadataJson = & cargo metadata --no-deps --format-version 1 if ($LASTEXITCODE -ne 0) { - Write-Error 'anvil-readme-check: cargo metadata failed' + Write-Error "${prefix}: cargo metadata failed" exit $LASTEXITCODE } $meta = $metadataJson | ConvertFrom-Json $hadFailure = $false foreach ($p in $meta.packages) { $name = $p.name - if (-not ($p.targets | Where-Object { $_.kind -contains 'lib' })) { continue } + $hasLib = [bool]($p.targets | Where-Object { ($_.kind -contains 'lib') -or ($_.kind -contains 'proc-macro') }) + $hasBin = [bool]($p.targets | Where-Object { $_.kind -contains 'bin' }) + if (-not $hasLib -and -not $hasBin) { continue } # Skip private crates (publish = false). They aren't released # and rarely have a polished README. Mirrors the # `cargo workspaces exec --ignore-private` idiom adopters # commonly use. if ($p.publish -is [array] -and $p.publish.Count -eq 0) { - Write-Host "anvil-readme-check: $name (skipped: publish = false)" + Write-Host "${prefix}: $name (skipped: publish = false)" continue } $disabled = $false @@ -4178,13 +4188,13 @@ anvil-readme-check: anvil-readme-check-validate-prereqs $disabled = $true } if ($disabled) { - Write-Host "anvil-readme-check: $name (opted out via [package.metadata.ox-gen-readme])" + Write-Host "${prefix}: $name (opted out via [package.metadata.ox-gen-readme])" continue } - Write-Host "anvil-readme-check: $name" + Write-Host "${prefix}: $name" # cargo doc2readme writes / compares relative to its CWD (not - # --manifest-path), so chdir into the crate before invoking - # --check. We also compute a per-crate relative path to the + # --manifest-path), so chdir into the crate before invoking it. + # We also compute a per-crate relative path to the # workspace-level template so the same template file works for # every crate (parallels the cargo-workspaces idiom). $crateDir = Split-Path -Parent $p.manifest_path @@ -4195,7 +4205,9 @@ anvil-readme-check: anvil-readme-check-validate-prereqs } else { $null } - $args = @('doc2readme', '--check') + $target = if ($hasLib) { '--lib' } else { '--bin' } + $args = @('doc2readme', $target) + if ($check) { $args += '--check' } if ($relTemplate) { $args += @('--template', $relTemplate) } & cargo @args if ($LASTEXITCODE -ne 0) { $hadFailure = $true } @@ -4205,6 +4217,9 @@ anvil-readme-check: anvil-readme-check-validate-prereqs } if ($hadFailure) { exit 1 } +# Check that crate README files are up to date. +anvil-readme-check: (_anvil-readme "check") + # Install prerequisites for the `anvil-readme-check` recipe. [group("anvil-setup")] anvil-readme-check-setup installer="install": (anvil-tool-cargo-doc2readme-install installer) @@ -6812,16 +6827,16 @@ cargo_careful_version := "0.4.10" cargo_check_external_types_version := "0.5.0" cargo_coverage_gate_version := "0.2.0" cargo_delta_version := "0.3.1" -cargo_deny_version := "0.19.8" -cargo_doc2readme_version := "0.7.2" +cargo_deny_version := "0.20.2" +cargo_doc2readme_version := "0.7.3" cargo_ensure_no_cyclic_deps_version := "0.2.0" cargo_ensure_no_default_features_version := "1.1.0" cargo_hack_version := "0.6.45" cargo_heather_version := "0.2.1" -cargo_llvm_cov_version := "0.8.7" -cargo_mutants_version := "27.0.0" -cargo_nextest_version := "0.9.137" -cargo_semver_checks_version := "0.49.0" +cargo_llvm_cov_version := "0.9.0" +cargo_mutants_version := "27.1.0" +cargo_nextest_version := "0.9.143" +cargo_semver_checks_version := "0.50.0" cargo_sort_version := "2.1.4" cargo_spellcheck_version := "0.15.7" cargo_udeps_version := "0.1.61" diff --git a/crates/cargo-anvil/tests/snapshots/snapshots__github_backend.snap b/crates/cargo-anvil/tests/snapshots/snapshots__github_backend.snap index b05e44c0..2bc8163f 100644 --- a/crates/cargo-anvil/tests/snapshots/snapshots__github_backend.snap +++ b/crates/cargo-anvil/tests/snapshots/snapshots__github_backend.snap @@ -2766,7 +2766,7 @@ anvil-bolero: anvil-bolero-validate-prereqs anvil-impact exit 1 } Write-Host "anvil-bolero: ${packageName}::${testName}" - & cargo '+{{ rust_nightly }}' bolero test --release --engine libfuzzer -T 60s --package $packageName $testName + & cargo '+{{ rust_nightly }}' bolero test --profile release --engine libfuzzer -T 60s --package $packageName $testName if ($LASTEXITCODE -ne 0) { $failed = $true } } if ($failed) { exit 1 } @@ -4009,20 +4009,28 @@ anvil-pr-title-validate-prereqs: anvil-tool-pwsh-validate-prereqs # `[package.metadata.ox-gen-readme]\ndisable = true` to their # Cargo.toml. anvil skips those crates. # -# Bin-only crates have no library rustdoc to base a README on, so they -# are skipped as well (cargo doc2readme requires a library target). +# Library or proc-macro rustdoc is preferred when a crate has one; bin-only +# crates use their binary rustdoc so every publishable crate follows the same +# generation policy. # TODO(anvil-runner): even after a `cargo ox-run` helper absorbs the # skip/splat preamble, this recipe stays multi-step: per-crate -# iteration over library targets, cargo-metadata-driven opt-outs +# iteration over eligible targets, cargo-metadata-driven opt-outs # (publish=false, [package.metadata.ox-gen-readme] disable), per-crate # Push-Location into the crate dir (cargo-doc2readme is CWD-sensitive # rather than --manifest-path-driven), and per-crate template-path -# resolution. The recipe iterates the full library-crate set every run. +# resolution. The recipe iterates the full eligible-crate set every run. -# Check that crate README files are up to date. +# Generate or check every eligible crate README with the same selection policy. [script("pwsh", "-NoProfile")] -anvil-readme-check: anvil-readme-check-validate-prereqs +_anvil-readme mode: anvil-readme-check-validate-prereqs $ErrorActionPreference = 'Stop' + $action = '{{ mode }}' + if ($action -notin @('generate', 'check')) { + [Console]::Error.WriteLine("anvil-readme: invalid mode '$action'; expected 'generate' or 'check'") + exit 2 + } + $check = $action -eq 'check' + $prefix = "anvil-readme-${action}" # Detect a workspace-level README template. Two conventional # locations: crates/README.j2 (cargo-workspaces idiom) or # README.j2 at repo root. @@ -4030,26 +4038,28 @@ anvil-readme-check: anvil-readme-check-validate-prereqs foreach ($candidate in 'crates/README.j2', 'README.j2') { if (Test-Path $candidate) { $template = (Resolve-Path $candidate).Path; break } } - # Iterate every library crate, then drop bin-only crates and opt-outs. + # Iterate every crate with rustdoc, then drop private crates and opt-outs. # Unscoped: a change to the workspace-level README template is a repo-level # input cargo-delta cannot map to a package, so this check always runs the - # full library set rather than an impact subset. + # full eligible-crate set rather than an impact subset. $metadataJson = & cargo metadata --no-deps --format-version 1 if ($LASTEXITCODE -ne 0) { - Write-Error 'anvil-readme-check: cargo metadata failed' + Write-Error "${prefix}: cargo metadata failed" exit $LASTEXITCODE } $meta = $metadataJson | ConvertFrom-Json $hadFailure = $false foreach ($p in $meta.packages) { $name = $p.name - if (-not ($p.targets | Where-Object { $_.kind -contains 'lib' })) { continue } + $hasLib = [bool]($p.targets | Where-Object { ($_.kind -contains 'lib') -or ($_.kind -contains 'proc-macro') }) + $hasBin = [bool]($p.targets | Where-Object { $_.kind -contains 'bin' }) + if (-not $hasLib -and -not $hasBin) { continue } # Skip private crates (publish = false). They aren't released # and rarely have a polished README. Mirrors the # `cargo workspaces exec --ignore-private` idiom adopters # commonly use. if ($p.publish -is [array] -and $p.publish.Count -eq 0) { - Write-Host "anvil-readme-check: $name (skipped: publish = false)" + Write-Host "${prefix}: $name (skipped: publish = false)" continue } $disabled = $false @@ -4057,13 +4067,13 @@ anvil-readme-check: anvil-readme-check-validate-prereqs $disabled = $true } if ($disabled) { - Write-Host "anvil-readme-check: $name (opted out via [package.metadata.ox-gen-readme])" + Write-Host "${prefix}: $name (opted out via [package.metadata.ox-gen-readme])" continue } - Write-Host "anvil-readme-check: $name" + Write-Host "${prefix}: $name" # cargo doc2readme writes / compares relative to its CWD (not - # --manifest-path), so chdir into the crate before invoking - # --check. We also compute a per-crate relative path to the + # --manifest-path), so chdir into the crate before invoking it. + # We also compute a per-crate relative path to the # workspace-level template so the same template file works for # every crate (parallels the cargo-workspaces idiom). $crateDir = Split-Path -Parent $p.manifest_path @@ -4074,7 +4084,9 @@ anvil-readme-check: anvil-readme-check-validate-prereqs } else { $null } - $args = @('doc2readme', '--check') + $target = if ($hasLib) { '--lib' } else { '--bin' } + $args = @('doc2readme', $target) + if ($check) { $args += '--check' } if ($relTemplate) { $args += @('--template', $relTemplate) } & cargo @args if ($LASTEXITCODE -ne 0) { $hadFailure = $true } @@ -4084,6 +4096,9 @@ anvil-readme-check: anvil-readme-check-validate-prereqs } if ($hadFailure) { exit 1 } +# Check that crate README files are up to date. +anvil-readme-check: (_anvil-readme "check") + # Install prerequisites for the `anvil-readme-check` recipe. [group("anvil-setup")] anvil-readme-check-setup installer="install": (anvil-tool-cargo-doc2readme-install installer) @@ -6691,16 +6706,16 @@ cargo_careful_version := "0.4.10" cargo_check_external_types_version := "0.5.0" cargo_coverage_gate_version := "0.2.0" cargo_delta_version := "0.3.1" -cargo_deny_version := "0.19.8" -cargo_doc2readme_version := "0.7.2" +cargo_deny_version := "0.20.2" +cargo_doc2readme_version := "0.7.3" cargo_ensure_no_cyclic_deps_version := "0.2.0" cargo_ensure_no_default_features_version := "1.1.0" cargo_hack_version := "0.6.45" cargo_heather_version := "0.2.1" -cargo_llvm_cov_version := "0.8.7" -cargo_mutants_version := "27.0.0" -cargo_nextest_version := "0.9.137" -cargo_semver_checks_version := "0.49.0" +cargo_llvm_cov_version := "0.9.0" +cargo_mutants_version := "27.1.0" +cargo_nextest_version := "0.9.143" +cargo_semver_checks_version := "0.50.0" cargo_sort_version := "2.1.4" cargo_spellcheck_version := "0.15.7" cargo_udeps_version := "0.1.61" diff --git a/crates/cargo-anvil/tests/snapshots/snapshots__local_only.snap b/crates/cargo-anvil/tests/snapshots/snapshots__local_only.snap index dc0bc298..1f003367 100644 --- a/crates/cargo-anvil/tests/snapshots/snapshots__local_only.snap +++ b/crates/cargo-anvil/tests/snapshots/snapshots__local_only.snap @@ -1639,7 +1639,7 @@ anvil-bolero: anvil-bolero-validate-prereqs anvil-impact exit 1 } Write-Host "anvil-bolero: ${packageName}::${testName}" - & cargo '+{{ rust_nightly }}' bolero test --release --engine libfuzzer -T 60s --package $packageName $testName + & cargo '+{{ rust_nightly }}' bolero test --profile release --engine libfuzzer -T 60s --package $packageName $testName if ($LASTEXITCODE -ne 0) { $failed = $true } } if ($failed) { exit 1 } @@ -2882,20 +2882,28 @@ anvil-pr-title-validate-prereqs: anvil-tool-pwsh-validate-prereqs # `[package.metadata.ox-gen-readme]\ndisable = true` to their # Cargo.toml. anvil skips those crates. # -# Bin-only crates have no library rustdoc to base a README on, so they -# are skipped as well (cargo doc2readme requires a library target). +# Library or proc-macro rustdoc is preferred when a crate has one; bin-only +# crates use their binary rustdoc so every publishable crate follows the same +# generation policy. # TODO(anvil-runner): even after a `cargo ox-run` helper absorbs the # skip/splat preamble, this recipe stays multi-step: per-crate -# iteration over library targets, cargo-metadata-driven opt-outs +# iteration over eligible targets, cargo-metadata-driven opt-outs # (publish=false, [package.metadata.ox-gen-readme] disable), per-crate # Push-Location into the crate dir (cargo-doc2readme is CWD-sensitive # rather than --manifest-path-driven), and per-crate template-path -# resolution. The recipe iterates the full library-crate set every run. +# resolution. The recipe iterates the full eligible-crate set every run. -# Check that crate README files are up to date. +# Generate or check every eligible crate README with the same selection policy. [script("pwsh", "-NoProfile")] -anvil-readme-check: anvil-readme-check-validate-prereqs +_anvil-readme mode: anvil-readme-check-validate-prereqs $ErrorActionPreference = 'Stop' + $action = '{{ mode }}' + if ($action -notin @('generate', 'check')) { + [Console]::Error.WriteLine("anvil-readme: invalid mode '$action'; expected 'generate' or 'check'") + exit 2 + } + $check = $action -eq 'check' + $prefix = "anvil-readme-${action}" # Detect a workspace-level README template. Two conventional # locations: crates/README.j2 (cargo-workspaces idiom) or # README.j2 at repo root. @@ -2903,26 +2911,28 @@ anvil-readme-check: anvil-readme-check-validate-prereqs foreach ($candidate in 'crates/README.j2', 'README.j2') { if (Test-Path $candidate) { $template = (Resolve-Path $candidate).Path; break } } - # Iterate every library crate, then drop bin-only crates and opt-outs. + # Iterate every crate with rustdoc, then drop private crates and opt-outs. # Unscoped: a change to the workspace-level README template is a repo-level # input cargo-delta cannot map to a package, so this check always runs the - # full library set rather than an impact subset. + # full eligible-crate set rather than an impact subset. $metadataJson = & cargo metadata --no-deps --format-version 1 if ($LASTEXITCODE -ne 0) { - Write-Error 'anvil-readme-check: cargo metadata failed' + Write-Error "${prefix}: cargo metadata failed" exit $LASTEXITCODE } $meta = $metadataJson | ConvertFrom-Json $hadFailure = $false foreach ($p in $meta.packages) { $name = $p.name - if (-not ($p.targets | Where-Object { $_.kind -contains 'lib' })) { continue } + $hasLib = [bool]($p.targets | Where-Object { ($_.kind -contains 'lib') -or ($_.kind -contains 'proc-macro') }) + $hasBin = [bool]($p.targets | Where-Object { $_.kind -contains 'bin' }) + if (-not $hasLib -and -not $hasBin) { continue } # Skip private crates (publish = false). They aren't released # and rarely have a polished README. Mirrors the # `cargo workspaces exec --ignore-private` idiom adopters # commonly use. if ($p.publish -is [array] -and $p.publish.Count -eq 0) { - Write-Host "anvil-readme-check: $name (skipped: publish = false)" + Write-Host "${prefix}: $name (skipped: publish = false)" continue } $disabled = $false @@ -2930,13 +2940,13 @@ anvil-readme-check: anvil-readme-check-validate-prereqs $disabled = $true } if ($disabled) { - Write-Host "anvil-readme-check: $name (opted out via [package.metadata.ox-gen-readme])" + Write-Host "${prefix}: $name (opted out via [package.metadata.ox-gen-readme])" continue } - Write-Host "anvil-readme-check: $name" + Write-Host "${prefix}: $name" # cargo doc2readme writes / compares relative to its CWD (not - # --manifest-path), so chdir into the crate before invoking - # --check. We also compute a per-crate relative path to the + # --manifest-path), so chdir into the crate before invoking it. + # We also compute a per-crate relative path to the # workspace-level template so the same template file works for # every crate (parallels the cargo-workspaces idiom). $crateDir = Split-Path -Parent $p.manifest_path @@ -2947,7 +2957,9 @@ anvil-readme-check: anvil-readme-check-validate-prereqs } else { $null } - $args = @('doc2readme', '--check') + $target = if ($hasLib) { '--lib' } else { '--bin' } + $args = @('doc2readme', $target) + if ($check) { $args += '--check' } if ($relTemplate) { $args += @('--template', $relTemplate) } & cargo @args if ($LASTEXITCODE -ne 0) { $hadFailure = $true } @@ -2957,6 +2969,9 @@ anvil-readme-check: anvil-readme-check-validate-prereqs } if ($hadFailure) { exit 1 } +# Check that crate README files are up to date. +anvil-readme-check: (_anvil-readme "check") + # Install prerequisites for the `anvil-readme-check` recipe. [group("anvil-setup")] anvil-readme-check-setup installer="install": (anvil-tool-cargo-doc2readme-install installer) @@ -5564,16 +5579,16 @@ cargo_careful_version := "0.4.10" cargo_check_external_types_version := "0.5.0" cargo_coverage_gate_version := "0.2.0" cargo_delta_version := "0.3.1" -cargo_deny_version := "0.19.8" -cargo_doc2readme_version := "0.7.2" +cargo_deny_version := "0.20.2" +cargo_doc2readme_version := "0.7.3" cargo_ensure_no_cyclic_deps_version := "0.2.0" cargo_ensure_no_default_features_version := "1.1.0" cargo_hack_version := "0.6.45" cargo_heather_version := "0.2.1" -cargo_llvm_cov_version := "0.8.7" -cargo_mutants_version := "27.0.0" -cargo_nextest_version := "0.9.137" -cargo_semver_checks_version := "0.49.0" +cargo_llvm_cov_version := "0.9.0" +cargo_mutants_version := "27.1.0" +cargo_nextest_version := "0.9.143" +cargo_semver_checks_version := "0.50.0" cargo_sort_version := "2.1.4" cargo_spellcheck_version := "0.15.7" cargo_udeps_version := "0.1.61" diff --git a/crates/cargo-aprz-lib/Cargo.toml b/crates/cargo-aprz-lib/Cargo.toml index e009c06e..0180aeb9 100644 --- a/crates/cargo-aprz-lib/Cargo.toml +++ b/crates/cargo-aprz-lib/Cargo.toml @@ -60,7 +60,7 @@ internals = [] bytes = { workspace = true } camino = { workspace = true } cargo_metadata = { workspace = true } -cel-interpreter = { workspace = true, features = ["chrono"] } +cel = { workspace = true, features = ["chrono"] } chrono = { workspace = true, features = ["serde", "clock", "std", "now"] } clap = { workspace = true, features = [ "std", diff --git a/crates/cargo-aprz-lib/src/expr/evaluator.rs b/crates/cargo-aprz-lib/src/expr/evaluator.rs index 2bc968fd..5fc590f1 100644 --- a/crates/cargo-aprz-lib/src/expr/evaluator.rs +++ b/crates/cargo-aprz-lib/src/expr/evaluator.rs @@ -8,8 +8,8 @@ use std::sync::{Arc, LazyLock}; -use cel_interpreter::objects::Map; -use cel_interpreter::{Context, Program, Value}; +use cel::objects::Map; +use cel::{Context, Program, Value}; use chrono::{DateTime, Local}; use super::{Appraisal, Expression, ExpressionDisposition, ExpressionOutcome, Risk}; @@ -144,7 +144,7 @@ fn evaluate_expression(program: &Program, name: &str, context: &Context) -> Resu /// `&'static str`, so they are identical for every crate. Building them once and handing out /// `Arc` clones avoids allocating a fresh `String` per metric per crate, which would otherwise /// scale with the size of the dependency graph. -#[expect(clippy::rc_buffer, reason = "cel-interpreter's map keys are Arc")] +#[expect(clippy::rc_buffer, reason = "cel's map keys are Arc")] static METRIC_KEYS: LazyLock>> = LazyLock::new(|| { let mut keys = crate::hash_map_with_capacity(METRIC_DEFINITIONS.len()); for def in METRIC_DEFINITIONS { @@ -157,7 +157,7 @@ static METRIC_KEYS: LazyLock>> = LazyLo /// Returns the interned CEL key for a metric's suffix, falling back to a fresh allocation for /// names that are not part of the built-in metric set (as used by some tests). -#[expect(clippy::rc_buffer, reason = "cel-interpreter's map keys are Arc")] +#[expect(clippy::rc_buffer, reason = "cel's map keys are Arc")] fn metric_key(name: &str, suffix: &str) -> Arc { METRIC_KEYS.get(name).map_or_else(|| Arc::new(suffix.to_string()), Arc::clone) } diff --git a/crates/cargo-aprz-lib/src/expr/expression.rs b/crates/cargo-aprz-lib/src/expr/expression.rs index c05c229c..91219d3b 100644 --- a/crates/cargo-aprz-lib/src/expr/expression.rs +++ b/crates/cargo-aprz-lib/src/expr/expression.rs @@ -5,7 +5,7 @@ use std::sync::Arc; -use cel_interpreter::Program; +use cel::Program; use serde::de::Error as DeError; use serde::{Deserialize, Deserializer, Serialize, Serializer}; diff --git a/crates/cargo-aprz-lib/src/facts/crates/tables/mod.rs b/crates/cargo-aprz-lib/src/facts/crates/tables/mod.rs index df2fecfe..8bef21ce 100644 --- a/crates/cargo-aprz-lib/src/facts/crates/tables/mod.rs +++ b/crates/cargo-aprz-lib/src/facts/crates/tables/mod.rs @@ -28,22 +28,22 @@ //! Each table is stored as a binary file with the following structure: //! //! ```text -//! ┌──────────────────────────────────────────────────────┐ -//! │ FORMAT_MAGIC: u64 (8 bytes) │ -//! │ - Magic number identifying format version │ -//! ├──────────────────────────────────────────────────────┤ -//! │ ROW_COUNT: u64 (8 bytes) │ -//! │ - Total number of rows in table │ -//! ├──────────────────────────────────────────────────────┤ -//! │ TIMESTAMP: u64 (8 bytes) │ -//! │ - Unix epoch seconds when table was created │ -//! │ - Used for TTL validation │ -//! ├──────────────────────────────────────────────────────┤ -//! │ Row Data (variable length) │ -//! │ - Variable-length encoded integers (vlen crate) │ -//! │ - Strings stored as length prefix + UTF-8 bytes │ -//! │ - Optional fields use discriminant byte │ -//! └──────────────────────────────────────────────────────┘ +//! +------------------------------------------------------+ +//! | FORMAT_MAGIC: u64 (8 bytes) | +//! | - Magic number identifying format version | +//! +------------------------------------------------------+ +//! | ROW_COUNT: u64 (8 bytes) | +//! | - Total number of rows in table | +//! +------------------------------------------------------+ +//! | TIMESTAMP: u64 (8 bytes) | +//! | - Unix epoch seconds when table was created | +//! | - Used for TTL validation | +//! +------------------------------------------------------+ +//! | Row Data (variable length) | +//! | - Variable-length encoded integers (vlen crate) | +//! | - Strings stored as length prefix + UTF-8 bytes | +//! | - Optional fields use discriminant byte | +//! +------------------------------------------------------+ //! ``` mod categories_table; diff --git a/crates/cargo-coverage-gate/README.md b/crates/cargo-coverage-gate/README.md index 0554abcb..293cba5e 100644 --- a/crates/cargo-coverage-gate/README.md +++ b/crates/cargo-coverage-gate/README.md @@ -105,7 +105,7 @@ plus the appropriate exit code. This crate was developed as part of The Oxidizer Project. Browse this crate's source code. - [__cargo_doc2readme_dependencies_info]: ggGmYW0CYXZlMC43LjJhdIQbFhzZ8rzWNNYbuRaDSGWynFgbH4PMdoT7GNcbVwNPtPjAhvFhYvRhcoQbDzRwf0qddWQbQiTzhu0-bE0bX-rutkvfDuYbITgXvtMXiRVhZIGDc2NhcmdvLWNvdmVyYWdlLWdhdGVlMC4zLjBzY2FyZ29fY292ZXJhZ2VfZ2F0ZQ + [__cargo_doc2readme_dependencies_info]: ggGmYW0CYXZlMC43LjNhdIQbFhzZ8rzWNNYbuRaDSGWynFgbH4PMdoT7GNcbVwNPtPjAhvFhYvRhcoQbDzRwf0qddWQbQiTzhu0-bE0bX-rutkvfDuYbITgXvtMXiRVhZIGDc2NhcmdvLWNvdmVyYWdlLWdhdGVlMC4zLjBzY2FyZ29fY292ZXJhZ2VfZ2F0ZQ [__link0]: https://github.com/taiki-e/cargo-llvm-cov [__link1]: https://docs.rs/cargo-coverage-gate/0.3.0/cargo_coverage_gate/fn.evaluate.html [__link2]: https://docs.rs/cargo-coverage-gate/0.3.0/cargo_coverage_gate/struct.EvaluatedReport.html diff --git a/crates/cargo-gamma-attrs-impl/Cargo.toml b/crates/cargo-gamma-attrs-impl/Cargo.toml index 24a038a2..74fca384 100644 --- a/crates/cargo-gamma-attrs-impl/Cargo.toml +++ b/crates/cargo-gamma-attrs-impl/Cargo.toml @@ -19,6 +19,10 @@ repository = "https://github.com/microsoft/ox-tools/tree/main/crates/cargo-gamma [package.metadata.docs.rs] all-features = true +[package.metadata.ox-gen-readme] +# This is an implementation detail of `cargo-gamma-attrs`; its README is an explicit stability warning. +disable = true + [package.metadata.cargo_check_external_types] # This implementation crate exists to provide proc-macro entry points, so its # public API necessarily accepts and returns proc-macro token streams. diff --git a/crates/cargo-gamma-attrs-impl/README.md b/crates/cargo-gamma-attrs-impl/README.md index 04f216a6..dd1d696d 100644 --- a/crates/cargo-gamma-attrs-impl/README.md +++ b/crates/cargo-gamma-attrs-impl/README.md @@ -13,49 +13,11 @@ -The implementation behind [`cargo-gamma-attrs`][__link0], -which is where the inert `#[gamma::skip]`, `#[gamma::expect_survived]` and -`#[gamma::expect_killed]` attributes are actually exposed. - -You almost certainly want that crate instead. This one is a normal library rather than a -proc-macro crate so its logic can be called by ordinary tests, covered, and mutation tested. -What remains in the proc-macro crate is a shim thin enough to read at a glance. - -## Why this crate exists - -`cargo-gamma-attrs` is a proc-macro crate, and a proc macro’s code runs only inside `rustc`, -while some *other* crate is being compiled. That puts it beyond the reach of both measurements -this project cares about: - -* A coverage harness collects counters from test binaries. A proc macro increments its counters - inside the compiler, which writes no profile the harness sees. -* A mutation run selects one mutant per test process at run time. A proc macro has already - finished by then, so none of its mutants can be active while a test is watching. - -Splitting the logic into an ordinary library makes it reachable by coverage and mutation tests. -The proc-macro crate remains a thin shim. - -## What the macros accept - -See the [`cargo-gamma-attrs`][__link1] documentation for the -user-facing description. In brief: a comma-separated selector list, optionally followed by -`reason = "..."` and `tag = "..."`, both of which must be string literals. - -`#[gamma::value()]` instead takes an expression. It is checked by [`value`][__link2], because its -argument is spliced into the user’s crate as a mutant and must be exactly one expression. - -## Stability - -This crate is an implementation detail of `cargo-gamma-attrs` and carries no stability -guarantee of its own. Depend on `cargo-gamma-attrs`. +This is an implementation detail of the cargo-gamma-attrs crate. Do not take a dependency on this +crate as it may change in incompatible ways without warning.
This crate was developed as part of The Oxidizer Project. Browse this crate's source code. - - [__cargo_doc2readme_dependencies_info]: ggGmYW0CYXZlMC43LjJhdIQbFhzZ8rzWNNYbuRaDSGWynFgbH4PMdoT7GNcbVwNPtPjAhvFhYvRhcoQbH5RUmmY8e-sbYyqmHPyeK9obgdLJAJ7T65AbUAUW0Y4uz2thZIGDdmNhcmdvLWdhbW1hLWF0dHJzLWltcGxlMC4xLjB2Y2FyZ29fZ2FtbWFfYXR0cnNfaW1wbA - [__link0]: https://crates.io/crates/cargo-gamma-attrs - [__link1]: https://docs.rs/cargo-gamma-attrs - [__link2]: https://docs.rs/cargo-gamma-attrs-impl/0.1.0/cargo_gamma_attrs_impl/?search=value diff --git a/crates/cargo-gamma-attrs-impl/docs/DESIGN.md b/crates/cargo-gamma-attrs-impl/docs/DESIGN.md index 61270db4..02120d97 100644 --- a/crates/cargo-gamma-attrs-impl/docs/DESIGN.md +++ b/crates/cargo-gamma-attrs-impl/docs/DESIGN.md @@ -19,5 +19,6 @@ attributes exported by `cargo-gamma-attrs`. ## Stability The crate is published only to support `cargo-gamma-attrs`. Its Rust API is an -implementation detail; the diagnostics and accepted attribute syntax are the -user-visible contract. +implementation detail, so its rustdoc is hidden and its hand-written README +warns downstream users not to depend on it. The diagnostics and accepted +attribute syntax are the user-visible contract. diff --git a/crates/cargo-gamma-attrs-impl/src/lib.rs b/crates/cargo-gamma-attrs-impl/src/lib.rs index 61b274e7..dd9765a6 100644 --- a/crates/cargo-gamma-attrs-impl/src/lib.rs +++ b/crates/cargo-gamma-attrs-impl/src/lib.rs @@ -2,6 +2,7 @@ // Licensed under the MIT License. #![cfg(not(all(test, miri)))] +#![doc(hidden)] #![forbid( unsafe_code, reason = "every raw platform call in this workspace lives in `cargo-gamma-unsafe`, behind a safe interface" diff --git a/crates/cargo-gamma-engine/Cargo.toml b/crates/cargo-gamma-engine/Cargo.toml index 1c2ac055..0d312fe2 100644 --- a/crates/cargo-gamma-engine/Cargo.toml +++ b/crates/cargo-gamma-engine/Cargo.toml @@ -19,6 +19,10 @@ repository = "https://github.com/microsoft/ox-tools/tree/main/crates/cargo-gamma [package.metadata.docs.rs] all-features = true +[package.metadata.ox-gen-readme] +# This is an implementation detail of `cargo-gamma`; its README is an explicit stability warning. +disable = true + # Establish the cross-platform CI baseline for this new crate. Raise it as coverage improves. [package.metadata.coverage-gate] min-lines-percent = 99.2 @@ -36,6 +40,7 @@ syn = { workspace = true, features = ["full", "parsing", "printing", "visit", "e [dev-dependencies] bolero = { workspace = true } +bolero-engine = { workspace = true, features = ["any"] } walkdir = { workspace = true } # >>> anvil-managed: anvil-lints diff --git a/crates/cargo-gamma-engine/README.md b/crates/cargo-gamma-engine/README.md index 48f0a32d..9f7100dc 100644 --- a/crates/cargo-gamma-engine/README.md +++ b/crates/cargo-gamma-engine/README.md @@ -13,17 +13,11 @@ -This crate is an internal implementation detail of -[`cargo-gamma`][__link0]. It contains the Rust source parsing, -mutation collection, stable identity, and schema instrumentation pipeline. - -Do not depend on it directly. Its API may change incompatibly without notice; it is published -only so that `cargo-gamma` can be installed through crates.io. +This is an implementation detail of the cargo-gamma tool. Do not take a dependency on this crate +as it may change in incompatible ways without warning.
This crate was developed as part of The Oxidizer Project. Browse this crate's source code. - - [__link0]: https://crates.io/crates/cargo-gamma diff --git a/crates/cargo-gamma-engine/docs/DESIGN.md b/crates/cargo-gamma-engine/docs/DESIGN.md index 0914e076..a82f854e 100644 --- a/crates/cargo-gamma-engine/docs/DESIGN.md +++ b/crates/cargo-gamma-engine/docs/DESIGN.md @@ -25,4 +25,6 @@ identity, mutator selection, and mutant-schema instrumentation. ## Stability The crate is published so `cargo-gamma` can be installed from crates.io. Its -Rust API is internal and carries no independent compatibility guarantee. +Rust API is internal and carries no independent compatibility guarantee. Its +rustdoc is hidden, and its hand-written README warns downstream users not to +depend on it. diff --git a/crates/cargo-gamma-engine/src/ops/collect/collector.rs b/crates/cargo-gamma-engine/src/ops/collect/collector.rs index b4f5c989..9d76700a 100644 --- a/crates/cargo-gamma-engine/src/ops/collect/collector.rs +++ b/crates/cargo-gamma-engine/src/ops/collect/collector.rs @@ -673,19 +673,19 @@ impl<'a> Collector<'a> { let wildcard = node .arms .iter() - .position(|arm| is_catch_all(&arm.pat) && self.cfg.holds_for(&arm.attrs)); + .position(|arm| !matches!(&arm.pat, Pat::Guard(_)) && is_catch_all(&arm.pat) && self.cfg.holds_for(&arm.attrs)); for (index, arm) in node.arms.iter().enumerate() { if self.skipped(&arm.attrs) { continue; } - if let Pat::Guard(guard) = &arm.pat { + if let Pat::Guard(guarded) = &arm.pat { self.condition( "match_guard.negate", "match_guard.always_true", "match_guard.always_false", - &guard.guard, + &guarded.guard, ); // An arm that already has a guard is disabled by forcing that guard false, which diff --git a/crates/cargo-gamma-engine/src/ops/collect/defaults.rs b/crates/cargo-gamma-engine/src/ops/collect/defaults.rs index 020f7112..1a39f71d 100644 --- a/crates/cargo-gamma-engine/src/ops/collect/defaults.rs +++ b/crates/cargo-gamma-engine/src/ops/collect/defaults.rs @@ -500,7 +500,7 @@ impl<'ast> Visit<'ast> for Defaults { // because the alias's target is what names the error either way. if name_of(&node.ty).as_deref() == Some("Result") { let defaulted = node.generics.params.iter().find_map(|param| match param { - GenericParam::Type(ty) => ty.default.as_ref().and_then(|(_, default)| name_of(default)), + GenericParam::Type(ty) => ty.default.as_ref().and_then(|(_equals, ty)| name_of(ty)), _ => None, }); diff --git a/crates/cargo-gamma-lib/Cargo.toml b/crates/cargo-gamma-lib/Cargo.toml index 08b539dc..afb297f8 100644 --- a/crates/cargo-gamma-lib/Cargo.toml +++ b/crates/cargo-gamma-lib/Cargo.toml @@ -19,6 +19,10 @@ repository = "https://github.com/microsoft/ox-tools/tree/main/crates/cargo-gamma [package.metadata.docs.rs] all-features = true +[package.metadata.ox-gen-readme] +# This is an implementation detail of `cargo-gamma`; its README is an explicit stability warning. +disable = true + # Establish the cross-platform CI baseline for this new crate. Raise it as coverage improves. [package.metadata.coverage-gate] min-lines-percent = 96.8 @@ -68,6 +72,7 @@ libc = { workspace = true, features = ["std"] } [dev-dependencies] bolero = { workspace = true } +bolero-engine = { workspace = true, features = ["any"] } # Only the integration test that pins the proc-macro's hand-copied limit constants against this # library's own needs it; it is never used outside `#[cfg(test)]`/`tests/`. cargo-gamma-attrs-impl = { workspace = true } diff --git a/crates/cargo-gamma-lib/README.md b/crates/cargo-gamma-lib/README.md index ba093fbe..ee3d80f1 100644 --- a/crates/cargo-gamma-lib/README.md +++ b/crates/cargo-gamma-lib/README.md @@ -13,15 +13,11 @@ -Internal implementation library for [`cargo-gamma`][__link0]. - -This crate is an implementation detail. Do not depend on it: it may change in incompatible -ways without warning, and it carries no semver commitment to anything it exposes. +This is an implementation detail of the cargo-gamma tool. Do not take a dependency on this crate +as it may change in incompatible ways without warning.
This crate was developed as part of The Oxidizer Project. Browse this crate's source code. - - [__link0]: https://crates.io/crates/cargo-gamma diff --git a/crates/cargo-gamma-lib/docs/DESIGN.md b/crates/cargo-gamma-lib/docs/DESIGN.md index 2d76f02d..b0346d3c 100644 --- a/crates/cargo-gamma-lib/docs/DESIGN.md +++ b/crates/cargo-gamma-lib/docs/DESIGN.md @@ -24,4 +24,5 @@ verdicts, incremental reuse, reporting, and command dispatch. The primary public contract is the `cargo gamma` command surface and its configuration, reports, diagnostics, and exit codes. The Rust API is an -implementation detail used by the thin executable crate. +implementation detail used by the thin executable crate. Its rustdoc is hidden, +and its hand-written README warns downstream users not to depend on it. diff --git a/crates/cargo-gamma-lib/src/commands/run.rs b/crates/cargo-gamma-lib/src/commands/run.rs index abc930f8..b6308b02 100644 --- a/crates/cargo-gamma-lib/src/commands/run.rs +++ b/crates/cargo-gamma-lib/src/commands/run.rs @@ -750,7 +750,6 @@ fn measured(host: &mut H, args: &RunArgs, progress_when: When, styler: || (crate::HashMap::default(), 0, Vec::new()), |prepared| adopt(args, &mut survey, &prepared.base, Some(&prepared.context), &prepared.inputs), ); - drop(cache_locks); // A dry run reports on the whole population and builds nothing, so there is no package-by- // package sequence to interleave the scan with; it is simply scanned. @@ -790,7 +789,7 @@ fn measured(host: &mut H, args: &RunArgs, progress_when: When, styler: verdict_log: VerdictLog::default(), }; - let outcome = exec::run(&survey, &selection, &config, &mut events); + let outcome = exec::run_with_locks(&survey, &selection, &config, &mut events, cache_locks); // A phase that failed never got to say what it found, so the line it opened is still waiting // for an ending. Close it before the error is printed, or the error arrives as the rest of diff --git a/crates/cargo-gamma-lib/src/exec/build/tests.rs b/crates/cargo-gamma-lib/src/exec/build/tests.rs index 895b1ab9..87f5a132 100644 --- a/crates/cargo-gamma-lib/src/exec/build/tests.rs +++ b/crates/cargo-gamma-lib/src/exec/build/tests.rs @@ -1066,7 +1066,8 @@ fn the_rollback_limit_error_describes_only_the_build_that_hit_it() { ); assert!(error.contains("1 of the 1 rollback rounds"), "{error}"); - assert!(!error.contains("41"), "{error}"); + assert!(!error.contains("41 blamed during this build"), "{error}"); + assert!(!error.contains("last rounds of this build: 41"), "{error}"); assert!(!error.contains("10 of"), "{error}"); } diff --git a/crates/cargo-gamma-lib/src/exec/measure.rs b/crates/cargo-gamma-lib/src/exec/measure.rs index b5827781..a7df1303 100644 --- a/crates/cargo-gamma-lib/src/exec/measure.rs +++ b/crates/cargo-gamma-lib/src/exec/measure.rs @@ -43,12 +43,22 @@ const GROUP_LIMIT: usize = 5; /// baseline does not pass — a failing baseline means every comparison in the run has nothing to /// compare against. pub fn run(survey: &Survey, selection: &Selection, config: &Config, events: &mut impl Events) -> Result { + run_with_locks(survey, selection, config, events, None) +} + +pub(crate) fn run_with_locks( + survey: &Survey, + selection: &Selection, + config: &Config, + events: &mut impl Events, + locks: Option, +) -> Result { let Measured { mut plan, built, stuck, dropped, - } = measure(survey, selection, config, events)?; + } = measure_with_locks(survey, selection, config, events, locks)?; // Nothing was live, so nothing was copied, built or measured. The plan still describes every // mutant that was found and why each one is not being run, which is what the caller reports. @@ -456,6 +466,16 @@ struct Cleared { /// made to succeed, or the baseline does not pass — a failing baseline means every comparison in /// the run has nothing to compare against. pub fn measure(survey: &Survey, selection: &Selection, config: &Config, events: &mut impl Events) -> Result { + measure_with_locks(survey, selection, config, events, None) +} + +fn measure_with_locks( + survey: &Survey, + selection: &Selection, + config: &Config, + events: &mut impl Events, + locks: Option, +) -> Result { let started = Instant::now(); let (memory, unbounded) = admit_memory_control(config)?; @@ -481,7 +501,7 @@ pub fn measure(survey: &Survey, selection: &Selection, config: &Config, events: // workspace can spend as much duplicating itself as compiling, and the aggregate cannot say // which. let copy_started = Instant::now(); - let mut work = Workspace::prepare(&plan.root, config, events)?; + let mut work = Workspace::prepare_with_locks(&plan.root, config, events, locks)?; let copy = copy_started.elapsed(); // Settled once, before anything is spawned, so the baseline and the sweep cannot disagree about diff --git a/crates/cargo-gamma-lib/src/exec/mod.rs b/crates/cargo-gamma-lib/src/exec/mod.rs index c7fcde73..402ce1cd 100644 --- a/crates/cargo-gamma-lib/src/exec/mod.rs +++ b/crates/cargo-gamma-lib/src/exec/mod.rs @@ -53,6 +53,7 @@ pub use events::Events; pub use incremental_mode::IncrementalMode; pub use loader::UNDER_GAMMA_VAR; pub(crate) use manifest::RUNTIME_CRATE; +pub(crate) use measure::run_with_locks; pub use measure::{Built, Measured, Oracle, measure, run}; pub use memory::{DEFAULT_HEADROOM, DEFAULT_MULTIPLIER, Demand, MemoryControl, MemoryPolicy}; // Named for its subject at this level, where `support` alone would say nothing about what is diff --git a/crates/cargo-gamma-lib/src/exec/verdict.rs b/crates/cargo-gamma-lib/src/exec/verdict.rs index d638a57f..93d45043 100644 --- a/crates/cargo-gamma-lib/src/exec/verdict.rs +++ b/crates/cargo-gamma-lib/src/exec/verdict.rs @@ -2175,7 +2175,7 @@ mod tests { active: Some(1), timeout: Some(Duration::from_secs(5)), stall: Stall { - budget: Some(measured.saturating_mul(2)), + budget: Some(measured.saturating_mul(4)), }, request: MemoryRequest::default(), only: Only::All, diff --git a/crates/cargo-gamma-lib/src/exec/verdict/hubs.rs b/crates/cargo-gamma-lib/src/exec/verdict/hubs.rs index b5df5ee8..894a6aa1 100644 --- a/crates/cargo-gamma-lib/src/exec/verdict/hubs.rs +++ b/crates/cargo-gamma-lib/src/exec/verdict/hubs.rs @@ -271,22 +271,27 @@ mod loom_models { /// The live count returns to exactly zero after all four production reader roles finish. /// - /// The coordinator first establishes the four-reader high-water mark, then all four independent - /// reader threads finish concurrently. The contended `live` observation also models diagnostics - /// racing abandoned readers as they finally close. Every increment must still have exactly one - /// decrement. + /// The coordinator first establishes the four-reader high-water mark, then the two jobs finish + /// their two readers concurrently. Modeling each job as one thread preserves cross-job + /// contention and all four decrements without exploring equivalent permutations among four + /// symmetric finisher threads. The contended `live` observation also models diagnostics racing + /// abandoned readers as they finally close. pub(super) fn a_readers_gauge_returns_to_exactly_zero_under_any_interleaving() { loom::model(|| { let readers = Arc::new(Readers::new()); - let mut finishers = Vec::with_capacity(CONCURRENT_READER_STREAMS); + let mut finishers = Vec::with_capacity(CONCURRENT_JOBS); for _stream in 0..CONCURRENT_READER_STREAMS { readers.started(); } - for _stream in 0..CONCURRENT_READER_STREAMS { + for _job in 0..CONCURRENT_JOBS { let readers = Arc::clone(&readers); - finishers.push(loom::thread::spawn(move || readers.finished())); + finishers.push(loom::thread::spawn(move || { + for _stream in 0..STREAMS_PER_JOB { + readers.finished(); + } + })); } assert!( diff --git a/crates/cargo-gamma-lib/src/exec/workspace.rs b/crates/cargo-gamma-lib/src/exec/workspace.rs index f634baab..9181acb4 100644 --- a/crates/cargo-gamma-lib/src/exec/workspace.rs +++ b/crates/cargo-gamma-lib/src/exec/workspace.rs @@ -29,6 +29,8 @@ use crate::Result; use crate::discover::TargetFile; use crate::error::error; +pub(crate) type CacheLocks = (File, Option); + /// The guard runtime's sources, embedded so that the vendored copy cannot drift from the real one. const RUNTIME_SOURCES: [(&str, &str); 3] = [ ("lib.rs", include_str!("../../../cargo-gamma-rt/src/lib.rs")), @@ -192,7 +194,18 @@ impl Workspace { } /// Copies and instruments the tree. + #[cfg(test)] pub(super) fn prepare(source: &Utf8Path, config: &Config, events: &mut impl Events) -> Result { + Self::prepare_with_locks(source, config, events, None) + } + + /// Copies and instruments the tree while retaining locks taken before cache adoption. + pub(super) fn prepare_with_locks( + source: &Utf8Path, + config: &Config, + events: &mut impl Events, + locks: Option, + ) -> Result { config.cargo.validate()?; // Absolute from here on, so that the copy's own exclusion — which compares the scratch base @@ -214,7 +227,12 @@ impl Workspace { fs::create_dir_all(base.as_std_path()) .map_err(|cause| error!("could not create the scratch directory at `{base}`").caused_by(cause))?; - let (workspace_lock, cache_lock) = claim_cache(source, config.cache_dir.as_deref())?; + let (workspace_lock, cache_lock) = match locks { + Some(locks) => locks, + None => claim_cache(source, config.cache_dir.as_deref())?, + }; + #[cfg(feature = "internals")] + crate::testing::pause_during_workspace_preparation(source); migrate_legacy_directory(&base, "tree", "workspace")?; migrate_legacy_directory(&base, "build", "target")?; @@ -1147,7 +1165,7 @@ pub(crate) fn claim_workspace(root: &Utf8Path) -> Result { } /// Takes every lock needed to use one workspace's reusable state. -pub(crate) fn claim_cache(root: &Utf8Path, cache: Option<&Utf8Path>) -> Result<(File, Option)> { +pub(crate) fn claim_cache(root: &Utf8Path, cache: Option<&Utf8Path>) -> Result { let workspace = claim_workspace(root)?; let base = gamma_base(root, cache); let default = gamma_base(root, None); diff --git a/crates/cargo-gamma-lib/src/testing.rs b/crates/cargo-gamma-lib/src/testing.rs index 3801fd54..31f484e7 100644 --- a/crates/cargo-gamma-lib/src/testing.rs +++ b/crates/cargo-gamma-lib/src/testing.rs @@ -29,14 +29,93 @@ use core::time::Duration; use std::io::{self, Write}; use std::path::Path; use std::process::Command; -use std::sync::{OnceLock, mpsc}; +use std::sync::{Mutex, OnceLock, mpsc}; use std::{fs, panic, thread}; -use camino::Utf8PathBuf; +use camino::{Utf8Path, Utf8PathBuf}; use crate::commands::Host; use crate::exec::Workspace; +type PauseChannels = (mpsc::SyncSender<()>, mpsc::Receiver<()>); + +static WORKSPACE_PREPARATION_PAUSES: OnceLock>> = OnceLock::new(); + +/// A deterministic pause after workspace preparation receives a command's cache locks. +#[derive(Debug)] +pub struct WorkspacePreparationPause { + reached: mpsc::Receiver<()>, + release: Option>, + waiting: bool, +} + +impl WorkspacePreparationPause { + /// Waits until workspace preparation receives the cache locks. + pub fn wait(&mut self) { + self.reached + .recv_timeout(Duration::from_mins(2)) + .expect("the registered command should reach workspace preparation before the test budget expires"); + self.waiting = true; + } + + /// Allows the paused command to continue. + pub fn release(mut self) { + self.release_waiter(); + } + + fn release_waiter(&mut self) { + if self.waiting { + let sender = self + .release + .take() + .expect("a pause that reached the boundary retains exactly one release sender"); + let _released = sender.send(()); + self.waiting = false; + } + } +} + +impl Drop for WorkspacePreparationPause { + fn drop(&mut self) { + self.release_waiter(); + } +} + +/// Registers a workspace-preparation pause for one workspace root. +pub fn hold_during_workspace_preparation(root: Utf8PathBuf) -> WorkspacePreparationPause { + let (reached_sender, reached) = mpsc::sync_channel(0); + let (release, release_receiver) = mpsc::sync_channel(0); + let pauses = WORKSPACE_PREPARATION_PAUSES.get_or_init(|| Mutex::new(crate::HashMap::default())); + let prior = pauses + .lock() + .expect("a prior workspace-preparation test panicked while registering its pause") + .insert(root, (reached_sender, release_receiver)); + + assert!(prior.is_none(), "only one workspace-preparation pause may target a workspace"); + + WorkspacePreparationPause { + reached, + release: Some(release), + waiting: false, + } +} + +pub(crate) fn pause_during_workspace_preparation(root: &Utf8Path) { + let Some(pauses) = WORKSPACE_PREPARATION_PAUSES.get() else { + return; + }; + let pause = pauses + .lock() + .expect("a prior workspace-preparation test panicked while taking its pause") + .remove(root); + + if let Some((reached, release)) = pause + && reached.send(()).is_ok() + { + let _released = release.recv(); + } +} + /// A [`Host`] that captures both streams in memory. /// /// The defaults describe a plain redirected pipe: not a terminal, no width, and an empty diff --git a/crates/cargo-gamma-lib/tests/session.rs b/crates/cargo-gamma-lib/tests/session.rs index 54c13e4d..3dff59af 100644 --- a/crates/cargo-gamma-lib/tests/session.rs +++ b/crates/cargo-gamma-lib/tests/session.rs @@ -387,6 +387,53 @@ fn a_second_run_reestablishes_the_first_runs_kills() { assert!(!output.contains("Iterating"), "a kill must never be carried: {output}"); } +#[test] +fn an_incremental_command_holds_its_cache_lock_from_adoption_through_preparation() { + step_aside_if_nested!(); + let dir = std::sync::Arc::new(workspace(SUBJECT)); + let cache = TempDir::new().expect("could not create the redirected cache"); + let cache = cache.path().to_str().expect("cache path is not UTF-8").to_owned(); + let args = ["--mutators", "relational", "--incremental", "build", "--cache-dir", cache.as_str()]; + let (warmed, output) = session(&dir, &args); + + assert_eq!(warmed, EXIT_OK, "{output}"); + + let root = Utf8PathBuf::from_path_buf(dir.path().to_path_buf()).expect("workspace path is not UTF-8"); + let mut pause = cargo_gamma_lib::testing::hold_during_workspace_preparation(root); + let first_dir = std::sync::Arc::clone(&dir); + let first_cache = cache.clone(); + let first = std::thread::spawn(move || { + session( + &first_dir, + &[ + "--mutators", + "relational", + "--incremental", + "build", + "--cache-dir", + first_cache.as_str(), + ], + ) + }); + + pause.wait(); + + let (blocked, output) = session(&dir, &args); + + assert_ne!(blocked, EXIT_OK, "{output}"); + assert!(output.contains("already using"), "{output}"); + + pause.release(); + + let (first_code, output) = first.join().expect("the paused incremental command should finish after release"); + + assert_eq!(first_code, EXIT_OK, "{output}"); + + let (reacquired, output) = session(&dir, &args); + + assert_eq!(reacquired, EXIT_OK, "{output}"); +} + #[test] fn an_unasserted_side_effect_leaves_a_survivor() { step_aside_if_nested!(); diff --git a/crates/cargo-gamma-process/Cargo.toml b/crates/cargo-gamma-process/Cargo.toml index 6b3b582d..98f88b42 100644 --- a/crates/cargo-gamma-process/Cargo.toml +++ b/crates/cargo-gamma-process/Cargo.toml @@ -19,6 +19,10 @@ repository = "https://github.com/microsoft/ox-tools/tree/main/crates/cargo-gamma [package.metadata.docs.rs] all-features = true +[package.metadata.ox-gen-readme] +# This is an implementation detail of `cargo-gamma`; its README is an explicit stability warning. +disable = true + # Establish the cross-platform CI baseline for this new crate. Raise it as coverage improves. [package.metadata.coverage-gate] min-lines-percent = 91.6 diff --git a/crates/cargo-gamma-process/README.md b/crates/cargo-gamma-process/README.md index ca15ae7b..a539a1a8 100644 --- a/crates/cargo-gamma-process/README.md +++ b/crates/cargo-gamma-process/README.md @@ -13,54 +13,11 @@ -This crate is an internal implementation detail of -[`cargo-gamma`][__link0]. It contains cargo-gamma’s bounded -process-tree containment, accounting, observation, and termination lifecycle. - -Do not depend on it directly. Its API may change incompatibly without notice; it is published -only so that `cargo-gamma` can be installed through crates.io. - -## Process-tree lifecycle - -Killing the process a run started is not enough. A test that shells out to a server, a database -or another build leaves those behind when the harness above them is cut off, and they take two -things with them: file locks inside the scratch tree, which turn the next run into a failure -that has nothing to do with any mutant, and inherited pipe handles, which keep whoever is -reading this tool’s output from ever seeing end of file. A run that ends with a hung consumer is -worse than one that ends with a wrong verdict, because nobody can even see the verdict. - -Both platforms have a way to say “this process and everything descended from it” — a process -group on Unix and a job object on Windows — but neither is reachable from `std`. The raw calls -live in `cargo-gamma-unsafe`, which exposes safe interfaces; this crate composes them into the -lifecycle used by the rest of the tool. - -The same boundary accounts for memory because it is the only place that knows the whole process -tree rather than only its leader. A [`MemoryRequest`][__link1] passed to [`prepare`][__link2] asks for measurement, -a ceiling, or neither; [`ProcessTree::usage`][__link3] answers once the process tree is gone. On Windows -requested accounting requires a dedicated job carrying the limit and accounting; on Linux a -cgroup leaf supplied by `cargo-gamma-unsafe` serves both purposes. - -The boundary is in force from the child’s first instruction. On Linux the child moves itself -into the cgroup between fork and exec; on Windows it normally starts suspended, enters the -dedicated job, and only then runs. A child that cannot enter a job already created for it is -rejected because an inherited job cannot be opened later to terminate its descendants. Failure -to create a job is tolerated only when no accounting was requested, degrading termination to -the leader alone. A peak that reached the limit is therefore enforced by the kernel rather than -inferred after the fact. - -A terminal delivers `Ctrl-C` to the whole foreground process group, so a child sharing this -process’s group dies with it automatically while a child leading its own group does not. Windows -normally preserves that guarantee through a dedicated job that dies with its last handle. Unix -installs explicit interruption handling through `cargo-gamma-unsafe`. +This is an implementation detail of the cargo-gamma tool. Do not take a dependency on this crate +as it may change in incompatible ways without warning.
This crate was developed as part of The Oxidizer Project. Browse this crate's source code. - - [__cargo_doc2readme_dependencies_info]: ggGmYW0CYXZlMC43LjJhdIQbFhzZ8rzWNNYbuRaDSGWynFgbH4PMdoT7GNcbVwNPtPjAhvFhYvRhcoQbyT97-uOZjGMbJSTUNtzD3T0bGSLqnro0bgkbPvyEineYDERhZIGDc2NhcmdvLWdhbW1hLXByb2Nlc3NlMC4xLjBzY2FyZ29fZ2FtbWFfcHJvY2Vzcw - [__link0]: https://crates.io/crates/cargo-gamma - [__link1]: https://docs.rs/cargo-gamma-process/0.1.0/cargo_gamma_process/?search=MemoryRequest - [__link2]: https://docs.rs/cargo-gamma-process/0.1.0/cargo_gamma_process/?search=prepare - [__link3]: https://docs.rs/cargo-gamma-process/0.1.0/cargo_gamma_process/?search=ProcessTree::usage diff --git a/crates/cargo-gamma-process/docs/DESIGN.md b/crates/cargo-gamma-process/docs/DESIGN.md index a0ead882..53e3496b 100644 --- a/crates/cargo-gamma-process/docs/DESIGN.md +++ b/crates/cargo-gamma-process/docs/DESIGN.md @@ -8,6 +8,13 @@ This crate provides cargo-gamma's bounded process-tree lifecycle: launch, observation, memory accounting, interruption, and termination. +Killing only the process a run started is insufficient. A test can spawn a +server, database, or nested build that outlives its harness, retaining scratch +tree file locks that break the next run. Descendants can also inherit output +pipe handles, preventing consumers from observing end of file and leaving an +otherwise completed run hung before its verdict can be read. Containment +therefore covers the complete descendant tree. + ## Boundaries - Policy such as timeout and memory-limit calculation belongs in @@ -24,7 +31,16 @@ observation, memory accounting, interruption, and termination. - The `fault-injection` feature is test-only infrastructure. - The crate forbids unsafe code. +Containment is active from the child's first instruction so descendants cannot +escape during a launch-time gap. Linux moves the child into its cgroup between +fork and exec. Windows normally creates the child suspended, assigns it to its +dedicated job, and resumes it only after assignment succeeds. This boundary +also owns memory accounting because it observes the complete tree rather than +only its leader; kernel-enforced cgroup or job limits cannot be reconstructed +reliably from a post-exit peak. + ## Stability The crate is published only as an implementation dependency of cargo-gamma and -does not promise a stable downstream API. +does not promise a stable downstream API. Its rustdoc is hidden, and its +hand-written README warns downstream users not to depend on it. diff --git a/crates/cargo-gamma-rt/Cargo.toml b/crates/cargo-gamma-rt/Cargo.toml index c67bfa41..3bcf8bba 100644 --- a/crates/cargo-gamma-rt/Cargo.toml +++ b/crates/cargo-gamma-rt/Cargo.toml @@ -19,6 +19,10 @@ repository = "https://github.com/microsoft/ox-tools/tree/main/crates/cargo-gamma [package.metadata.docs.rs] all-features = true +[package.metadata.ox-gen-readme] +# This is an implementation detail of `cargo-gamma`; its README is an explicit stability warning. +disable = true + # Establish the lowest Windows baseline for this new crate. Raise it as coverage improves. [package.metadata.coverage-gate] min-lines-percent = 90.8 diff --git a/crates/cargo-gamma-rt/README.md b/crates/cargo-gamma-rt/README.md index 0501dc26..31c6aa56 100644 --- a/crates/cargo-gamma-rt/README.md +++ b/crates/cargo-gamma-rt/README.md @@ -13,197 +13,11 @@ -Runtime support injected into crates under mutation test by `cargo-gamma`. - -This crate is injected into the dependency graph of the crate under test while a mutation run -is in progress. You should never need to depend on it directly. - -`cargo-gamma` rewrites the crate under test so that every mutation site carries a *guard*: a -cheap runtime check that activates exactly one mutant. That lets a whole population of mutants -live in a single compiled artifact — the *mutant schema*, after Untch, Offutt and Harrold, who -introduced the construction in 1993 — instead of requiring one build per mutant. Since a build -is by far the most expensive step in the loop, testing a mutant drops from minutes to the cost -of launching a process. - -## What a guard looks like - -[`a`][__link0] is the only function the instrumented source calls, and it appears in one of three shapes, -chosen by what Rust will accept in that position: - -```text -// an expression, whose value the mutant replaces -(if ::gamma_rt::a(7u32) { (a) <= (b) } else { a < b }) - -// a block, whose body the mutant replaces -{ if ::gamma_rt::a(12u32) { Default::default() } else { ..the real body.. } } - -// a statement, which the mutant deletes -if !::gamma_rt::a(19u32) { self.entries.push(value); } -``` - -Sites nest — in `a + b < c` the `<` site contains the `+` site — and only the `else` arm carries -instrumented children. Exactly one mutant is live in a process, so if the `<` mutant is active -then no `+` mutant can be, and the taken arm can hold plain original text. That is what keeps -the encoding linear in the size of the source rather than exponential in nesting depth. - -## You do not depend on this crate - -`cargo-gamma` copies the workspace to a scratch tree, writes this crate into it, and adds the -dependency there. Nothing is added to your manifest, nothing is fetched from the network, and -your own build is never instrumented. The package is `cargo-gamma-rt` but its library is named -`gamma_rt`, which is why instrumented source can say `::gamma_rt::a` without a rename. - -The copy embedded in the tool is this exact source, so the vendored runtime cannot drift from -the one the guards were generated against. - -## Why this crate has no dependencies - -It has zero dependencies, no features, no build script and no `std`, by design. Anything else -would perturb feature unification in *the user’s* tree, which could change what their code -compiles to and therefore what their tests prove, or stop a `no_std` tree from building once -the shim is injected into it. Zero dependencies is a correctness requirement, not a -preference. - -For the same reason [`a`][__link1] must stay trivial. It is called at every mutation site of every -execution of the suite, so its cost is multiplied by the whole population: a cached atomic load -and a comparison, behind a branch the predictor learns immediately. - -## A worked example - -Given this function, and a mutant that turns `<` into `<=`: - -```rust -fn below(a: u32, b: u32) -> bool { - a < b -} -``` - -`cargo-gamma` rewrites it in the scratch tree as: - -```rust -fn below(a: u32, b: u32) -> bool { - if ::gamma_rt::a(7u32) { - (a) <= (b) - } else { - a < b - } -} -``` - -The whole population lives in one binary, and the run launches it once per mutant: - -```text -GAMMA_ACTIVE=7 ./target/debug/deps/my_crate-abc123 # mutant 7 is live -GAMMA_ACTIVE=8 ./target/debug/deps/my_crate-abc123 # mutant 8 is live -./target/debug/deps/my_crate-abc123 # nothing is live: the baseline -``` - -## Selection protocol - -The active mutant is named by the [`ACTIVE_VAR`][__link2] environment variable, captured exactly once -during process startup before user code can start threads. The value is a decimal mutant -ordinal. [`NONE`][__link3] means no mutant is active, which is how the baseline run and every ordinary -build behave — including builds of proc macros, where an active mutant could otherwise hang the -compiler. - -An unset, empty, or unparsable value all mean [`NONE`][__link4]. A build that links this crate but is not -being driven by a mutation run must behave exactly as it did before, and the ordinals are -1-based precisely so that “absent” and “explicitly unmutated” are the same answer. Failure to -acquire the startup environment is different: the runtime emits [`ENVIRONMENT_ERROR_MARKER`][__link5] -and exits, so the parent cannot mistake a mutant that never activated for a survivor. - -```rust -use gamma_rt::{ACTIVE_VAR, CENSUS_VAR, NONE, a, active, any}; - -// Stated for an ordinary process. A census is its own mode: it activates no mutant, so -// `active` reports `NONE`, and every guard answers `false` while recording the site it stands -// at — including the sites this example would walk past. -if std::env::var_os(CENSUS_VAR).is_none_or(|path| path.is_empty()) { - // In an ordinary process nothing is selected, so every guard takes its original arm. - if active() == NONE { - assert!(!any()); - assert!(!a(NONE)); - assert!(!a(1)); - assert!(!a(9_999)); - } - - // Only a positive ordinal can select a mutant. - if active() != NONE { - assert!(a(active())); - } -} - -assert_eq!(ACTIVE_VAR, "GAMMA_ACTIVE"); -``` - -Reading it once, rather than per call, is what makes the guard cheap; it also means a test that -sets the variable on itself changes nothing, which is the honest behavior. The run drives -selection by launching a fresh process per mutant. - -## The three entry points - -[`a`][__link6] is what the guards call, and the only one of the three that instrumented source contains. -[`active`][__link7] and [`any`][__link8] are there for the tool’s own diagnostics and for anyone inspecting a -scratch tree by hand: - -```rust -use gamma_rt::{active, any}; - -// Useful in a scratch tree when you are trying to work out which mutant a failing -// reproduction actually ran. -if any() { - println!("mutant {} is live", active()); -} else { - println!("baseline"); -} -``` - -## Making two iterators one type - -[`Either`][__link9] is the one other thing instrumented source mentions, and it exists because the guard -is an `if`. A function returning `impl Iterator` returns a single concrete type that -its body picks, so `if a(n) { core::iter::empty() } else { ..the real body.. }` has arms of two -different types and will not compile. Wrapping each arm in a variant makes them one type: - -```text -{ if ::gamma_rt::a(4u32) { ::gamma_rt::Either::L(core::iter::empty()) } - else { ::gamma_rt::Either::R({ ..the real body.. }) } } -``` - -See [`Either`][__link10] for why this is not a `Box`. - -## `no_std`, and what it does not buy - -This crate is `#![no_std]`. It has to be: it is injected into the dependency graph of every -crate the tool instruments, so a shim that linked `std` could not be instrumented into a tree -whose target has no `std` in its sysroot — and that failure is not attributable to any mutant, -so the rollback loop cannot withdraw anything to rescue it. The whole tree simply stops -building. The tests link `std`, which costs nothing because they are never compiled into a -user’s build. - -What `no_std` does not buy is a mutation run on a target with no environment. A mutant is -selected by reading `GAMMA_ACTIVE`, which needs POSIX `getenv` or the Win32 equivalent; a -target with neither gets the arm that reports no mutant at all, so the instrumented code -compiles and runs the original everywhere. That is deliberately the safe direction — every -mutant is reported as surviving rather than a mutated program being reported as correct — but -it means the useful case is a `no_std` *crate* whose tests are run on a hosted target, which is -how nearly every `no_std` library is tested anyway. +This is an implementation detail of the cargo-gamma tool. Do not take a dependency on this crate +as it may change in incompatible ways without warning.
This crate was developed as part of The Oxidizer Project. Browse this crate's source code. - - [__cargo_doc2readme_dependencies_info]: ggGmYW0CYXZlMC43LjJhdIQbFhzZ8rzWNNYbuRaDSGWynFgbH4PMdoT7GNcbVwNPtPjAhvFhYvRhcoQbw7tiVz7VIbMbp0tv-g1QPj8b-zYmwf0qFaIbBB-y9UIz0QRhZIGDbmNhcmdvLWdhbW1hLXJ0ZTAuMS4wbmNhcmdvX2dhbW1hX3J0 - [__link0]: https://docs.rs/cargo-gamma-rt/0.1.0/cargo_gamma_rt/?search=a - [__link1]: https://docs.rs/cargo-gamma-rt/0.1.0/cargo_gamma_rt/?search=a - [__link10]: https://docs.rs/cargo-gamma-rt/0.1.0/cargo_gamma_rt/?search=Either - [__link2]: https://docs.rs/cargo-gamma-rt/0.1.0/cargo_gamma_rt/?search=ACTIVE_VAR - [__link3]: https://docs.rs/cargo-gamma-rt/0.1.0/cargo_gamma_rt/?search=NONE - [__link4]: https://docs.rs/cargo-gamma-rt/0.1.0/cargo_gamma_rt/?search=NONE - [__link5]: https://docs.rs/cargo-gamma-rt/0.1.0/cargo_gamma_rt/?search=ENVIRONMENT_ERROR_MARKER - [__link6]: https://docs.rs/cargo-gamma-rt/0.1.0/cargo_gamma_rt/?search=a - [__link7]: https://docs.rs/cargo-gamma-rt/0.1.0/cargo_gamma_rt/?search=active - [__link8]: https://docs.rs/cargo-gamma-rt/0.1.0/cargo_gamma_rt/?search=any - [__link9]: https://docs.rs/cargo-gamma-rt/0.1.0/cargo_gamma_rt/?search=Either diff --git a/crates/cargo-gamma-rt/docs/DESIGN.md b/crates/cargo-gamma-rt/docs/DESIGN.md index f611e5a3..7f2183f2 100644 --- a/crates/cargo-gamma-rt/docs/DESIGN.md +++ b/crates/cargo-gamma-rt/docs/DESIGN.md @@ -15,6 +15,8 @@ equivalent to the original program. - No features and no build script. - `no_std` compatibility. - The library target remains named `gamma_rt`. +- Rustdoc is hidden, and the hand-written README warns downstream users not to + depend on this implementation crate. These constraints prevent injection from perturbing dependency resolution, feature unification, offline builds, or the target crate's standard-library diff --git a/crates/cargo-gamma-rt/src/lib.rs b/crates/cargo-gamma-rt/src/lib.rs index e0093e8d..174dd390 100644 --- a/crates/cargo-gamma-rt/src/lib.rs +++ b/crates/cargo-gamma-rt/src/lib.rs @@ -2,6 +2,7 @@ // Licensed under the MIT License. #![no_std] +#![doc(hidden)] #![cfg_attr( all(any(unix, windows), miri), expect( diff --git a/crates/cargo-gamma-unsafe/Cargo.toml b/crates/cargo-gamma-unsafe/Cargo.toml index 8ebf1453..4444d579 100644 --- a/crates/cargo-gamma-unsafe/Cargo.toml +++ b/crates/cargo-gamma-unsafe/Cargo.toml @@ -19,6 +19,10 @@ repository = "https://github.com/microsoft/ox-tools/tree/main/crates/cargo-gamma [package.metadata.docs.rs] all-features = true +[package.metadata.ox-gen-readme] +# This is an implementation detail of `cargo-gamma`; its README is an explicit stability warning. +disable = true + # Establish the lowest cross-platform baseline for this new crate. Raise it as coverage improves. [package.metadata.coverage-gate] min-lines-percent = 86.5 diff --git a/crates/cargo-gamma-unsafe/README.md b/crates/cargo-gamma-unsafe/README.md index cf8a1181..e523893e 100644 --- a/crates/cargo-gamma-unsafe/README.md +++ b/crates/cargo-gamma-unsafe/README.md @@ -13,30 +13,11 @@ -The platform calls [`cargo-gamma`][__link0] cannot make safely, -behind an interface that is safe to call. This crate is an implementation detail of the tool; -you should never need to depend on it directly. - -Two things the tool does have no safe expression in `std`: killing a whole process subtree (a -process group on Unix, a job object on Windows) and bounding what that subtree allocates (a -cgroup leaf on Linux, the same job object on Windows). Neither is a case of reaching for -`unsafe` to go faster — there is no safe version to prefer. - -Concentrating those calls here is what lets every other crate in the workspace carry -`#![forbid(unsafe_code)]`, which turns “we reviewed the unsafe code” into a property the -compiler checks on every build. `cargo-gamma-rt` is the one exception, and only because it is -injected into the dependency graph of the crate under test and so can depend on nothing at -all. - -Policy does not live here. What a memory ceiling *should* be is arithmetic on a baseline -measurement, and it stays in `cargo-gamma-lib` where it can be tested without a kernel. This -crate answers “what can the platform do, and do it”; its caller answers “what should we ask -for”. +This is an implementation detail of the cargo-gamma tool. Do not take a dependency on this crate +as it may change in incompatible ways without warning.
This crate was developed as part of The Oxidizer Project. Browse this crate's source code. - - [__link0]: https://crates.io/crates/cargo-gamma diff --git a/crates/cargo-gamma-unsafe/docs/DESIGN.md b/crates/cargo-gamma-unsafe/docs/DESIGN.md index 7cb3fd61..3d5c8eb8 100644 --- a/crates/cargo-gamma-unsafe/docs/DESIGN.md +++ b/crates/cargo-gamma-unsafe/docs/DESIGN.md @@ -8,6 +8,14 @@ This crate contains the operating-system calls cargo-gamma cannot express safely through the standard library, exposed through safe interfaces. +Process-subtree termination and memory containment have no safe standard +library equivalents; the unsafe code here is required for platform capability, +not performance. Concentrating those calls in this crate lets the rest of the +workspace use `#![forbid(unsafe_code)]`, turning the boundary from a review +convention into a property checked by the compiler on every build. +`cargo-gamma-rt` is the sole exception because it is injected into the subject +crate's dependency graph and must remain dependency-free. + ## Boundaries - Unix process groups and Windows job objects provide process-tree control. @@ -21,7 +29,9 @@ safely through the standard library, exposed through safe interfaces. - Linux controller delegation moves only cargo-gamma itself and refuses a cgroup shared with any other process. - Policy and limit calculation remain in `cargo-gamma-lib`; this crate only - reports and applies platform capabilities. + reports and applies platform capabilities. For example, choosing a memory + ceiling is testable arithmetic in `cargo-gamma-lib`; this crate answers only + what the host can enforce and applies the requested value. - Unsafe blocks are concentrated here, documented, and hidden behind invariants that callers can satisfy safely. @@ -30,3 +40,9 @@ safely through the standard library, exposed through safe interfaces. Unix and Windows implementations are selected with target-specific dependencies. Unsupported hosts report the missing capability rather than silently substituting weaker semantics. + +## Stability + +The crate is published only as an implementation dependency of cargo-gamma. Its +rustdoc is hidden, and its hand-written README warns downstream users not to +depend on its unstable Rust API. diff --git a/crates/cargo-heather/README.md b/crates/cargo-heather/README.md index 118795db..9b764276 100644 --- a/crates/cargo-heather/README.md +++ b/crates/cargo-heather/README.md @@ -193,7 +193,7 @@ is supplied. This crate was developed as part of The Oxidizer Project. Browse this crate's source code. - [__cargo_doc2readme_dependencies_info]: ggGmYW0CYXZlMC43LjJhdIQbFhzZ8rzWNNYbuRaDSGWynFgbH4PMdoT7GNcbVwNPtPjAhvFhYvRhcoQbtpJMWoUHDG0bJGvg_sbiCH8b66-weBRIetcbfF7fD5ccBythZIGDbWNhcmdvLWhlYXRoZXJlMC4zLjBtY2FyZ29faGVhdGhlcg + [__cargo_doc2readme_dependencies_info]: ggGmYW0CYXZlMC43LjNhdIQbFhzZ8rzWNNYbuRaDSGWynFgbH4PMdoT7GNcbVwNPtPjAhvFhYvRhcoQbtpJMWoUHDG0bJGvg_sbiCH8b66-weBRIetcbfF7fD5ccBythZIGDbWNhcmdvLWhlYXRoZXJlMC4zLjBtY2FyZ29faGVhdGhlcg [__link0]: https://doc.rust-lang.org/stable/std/?search=io::Read [__link1]: https://doc.rust-lang.org/stable/std/?search=io::Write [__link10]: https://docs.rs/cargo-heather/0.3.0/cargo_heather/?search=FileKind::detect diff --git a/deny.toml b/deny.toml index 523d092b..25c9016a 100644 --- a/deny.toml +++ b/deny.toml @@ -9,13 +9,7 @@ yanked = "deny" unmaintained = "all" # Advisories we have consciously accepted. Each entry needs a reason and, # where the fix is out of our hands, the upstream we are waiting on. -ignore = [ - # `paste` is unmaintained but has no safe upgrade. It reaches us only - # transitively through `cel-interpreter`, which `cargo-aprz` uses to - # evaluate appraisal expressions. Revisit when cel-interpreter moves to - # `pastey` (which this workspace already depends on directly). - "RUSTSEC-2024-0436", -] +ignore = [] # <<< anvil-managed: anvil-deny-advisories # >>> anvil-managed: anvil-deny-bans diff --git a/gamma.toml b/gamma.toml index 9a77b728..52e87d90 100644 --- a/gamma.toml +++ b/gamma.toml @@ -1,4 +1,11 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. -exclude-files = ["crates/cargo-gamma-lib/src/testing.rs"] +exclude-files = [ + # Repository automation invokes external tools rather than implementing product behavior. + "crates/automation/**", + # Integration-test fixtures are deliberately compiled through the `internals` feature. + "crates/cargo-gamma-lib/src/testing.rs", + # Process fault injection exists only to force test-only operating-system failure paths. + "crates/cargo-gamma-process/src/faults.rs", +] diff --git a/justfiles/anvil/checks/bolero.just b/justfiles/anvil/checks/bolero.just index 37f7d21b..d23fb3a9 100644 --- a/justfiles/anvil/checks/bolero.just +++ b/justfiles/anvil/checks/bolero.just @@ -103,7 +103,7 @@ anvil-bolero: anvil-bolero-validate-prereqs anvil-impact exit 1 } Write-Host "anvil-bolero: ${packageName}::${testName}" - & cargo '+{{ rust_nightly }}' bolero test --release --engine libfuzzer -T 60s --package $packageName $testName + & cargo '+{{ rust_nightly }}' bolero test --profile release --engine libfuzzer -T 60s --package $packageName $testName if ($LASTEXITCODE -ne 0) { $failed = $true } } if ($failed) { exit 1 } diff --git a/justfiles/anvil/checks/readme-check.just b/justfiles/anvil/checks/readme-check.just index 1d258e25..5ec58e43 100644 --- a/justfiles/anvil/checks/readme-check.just +++ b/justfiles/anvil/checks/readme-check.just @@ -22,20 +22,28 @@ # `[package.metadata.ox-gen-readme]\ndisable = true` to their # Cargo.toml. anvil skips those crates. # -# Bin-only crates have no library rustdoc to base a README on, so they -# are skipped as well (cargo doc2readme requires a library target). +# Library or proc-macro rustdoc is preferred when a crate has one; bin-only +# crates use their binary rustdoc so every publishable crate follows the same +# generation policy. # TODO(anvil-runner): even after a `cargo ox-run` helper absorbs the # skip/splat preamble, this recipe stays multi-step: per-crate -# iteration over library targets, cargo-metadata-driven opt-outs +# iteration over eligible targets, cargo-metadata-driven opt-outs # (publish=false, [package.metadata.ox-gen-readme] disable), per-crate # Push-Location into the crate dir (cargo-doc2readme is CWD-sensitive # rather than --manifest-path-driven), and per-crate template-path -# resolution. The recipe iterates the full library-crate set every run. +# resolution. The recipe iterates the full eligible-crate set every run. -# Check that crate README files are up to date. +# Generate or check every eligible crate README with the same selection policy. [script("pwsh", "-NoProfile")] -anvil-readme-check: anvil-readme-check-validate-prereqs +_anvil-readme mode: anvil-readme-check-validate-prereqs $ErrorActionPreference = 'Stop' + $action = '{{ mode }}' + if ($action -notin @('generate', 'check')) { + [Console]::Error.WriteLine("anvil-readme: invalid mode '$action'; expected 'generate' or 'check'") + exit 2 + } + $check = $action -eq 'check' + $prefix = "anvil-readme-${action}" # Detect a workspace-level README template. Two conventional # locations: crates/README.j2 (cargo-workspaces idiom) or # README.j2 at repo root. @@ -43,26 +51,28 @@ anvil-readme-check: anvil-readme-check-validate-prereqs foreach ($candidate in 'crates/README.j2', 'README.j2') { if (Test-Path $candidate) { $template = (Resolve-Path $candidate).Path; break } } - # Iterate every library crate, then drop bin-only crates and opt-outs. + # Iterate every crate with rustdoc, then drop private crates and opt-outs. # Unscoped: a change to the workspace-level README template is a repo-level # input cargo-delta cannot map to a package, so this check always runs the - # full library set rather than an impact subset. + # full eligible-crate set rather than an impact subset. $metadataJson = & cargo metadata --no-deps --format-version 1 if ($LASTEXITCODE -ne 0) { - Write-Error 'anvil-readme-check: cargo metadata failed' + Write-Error "${prefix}: cargo metadata failed" exit $LASTEXITCODE } $meta = $metadataJson | ConvertFrom-Json $hadFailure = $false foreach ($p in $meta.packages) { $name = $p.name - if (-not ($p.targets | Where-Object { $_.kind -contains 'lib' })) { continue } + $hasLib = [bool]($p.targets | Where-Object { ($_.kind -contains 'lib') -or ($_.kind -contains 'proc-macro') }) + $hasBin = [bool]($p.targets | Where-Object { $_.kind -contains 'bin' }) + if (-not $hasLib -and -not $hasBin) { continue } # Skip private crates (publish = false). They aren't released # and rarely have a polished README. Mirrors the # `cargo workspaces exec --ignore-private` idiom adopters # commonly use. if ($p.publish -is [array] -and $p.publish.Count -eq 0) { - Write-Host "anvil-readme-check: $name (skipped: publish = false)" + Write-Host "${prefix}: $name (skipped: publish = false)" continue } $disabled = $false @@ -70,13 +80,13 @@ anvil-readme-check: anvil-readme-check-validate-prereqs $disabled = $true } if ($disabled) { - Write-Host "anvil-readme-check: $name (opted out via [package.metadata.ox-gen-readme])" + Write-Host "${prefix}: $name (opted out via [package.metadata.ox-gen-readme])" continue } - Write-Host "anvil-readme-check: $name" + Write-Host "${prefix}: $name" # cargo doc2readme writes / compares relative to its CWD (not - # --manifest-path), so chdir into the crate before invoking - # --check. We also compute a per-crate relative path to the + # --manifest-path), so chdir into the crate before invoking it. + # We also compute a per-crate relative path to the # workspace-level template so the same template file works for # every crate (parallels the cargo-workspaces idiom). $crateDir = Split-Path -Parent $p.manifest_path @@ -87,7 +97,9 @@ anvil-readme-check: anvil-readme-check-validate-prereqs } else { $null } - $args = @('doc2readme', '--check') + $target = if ($hasLib) { '--lib' } else { '--bin' } + $args = @('doc2readme', $target) + if ($check) { $args += '--check' } if ($relTemplate) { $args += @('--template', $relTemplate) } & cargo @args if ($LASTEXITCODE -ne 0) { $hadFailure = $true } @@ -97,6 +109,9 @@ anvil-readme-check: anvil-readme-check-validate-prereqs } if ($hadFailure) { exit 1 } +# Check that crate README files are up to date. +anvil-readme-check: (_anvil-readme "check") + # Install prerequisites for the `anvil-readme-check` recipe. [group("anvil-setup")] anvil-readme-check-setup installer="install": (anvil-tool-cargo-doc2readme-install installer) diff --git a/justfiles/anvil/versions.just b/justfiles/anvil/versions.just index 564a3b90..0a4aeaa1 100644 --- a/justfiles/anvil/versions.just +++ b/justfiles/anvil/versions.just @@ -56,16 +56,16 @@ cargo_careful_version := "0.4.10" cargo_check_external_types_version := "0.5.0" cargo_coverage_gate_version := "0.2.0" cargo_delta_version := "0.3.1" -cargo_deny_version := "0.19.8" -cargo_doc2readme_version := "0.7.2" +cargo_deny_version := "0.20.2" +cargo_doc2readme_version := "0.7.3" cargo_ensure_no_cyclic_deps_version := "0.2.0" cargo_ensure_no_default_features_version := "1.1.0" cargo_hack_version := "0.6.45" cargo_heather_version := "0.2.1" -cargo_llvm_cov_version := "0.8.7" -cargo_mutants_version := "27.0.0" -cargo_nextest_version := "0.9.137" -cargo_semver_checks_version := "0.49.0" +cargo_llvm_cov_version := "0.9.0" +cargo_mutants_version := "27.1.0" +cargo_nextest_version := "0.9.143" +cargo_semver_checks_version := "0.50.0" cargo_sort_version := "2.1.4" cargo_spellcheck_version := "0.15.7" cargo_udeps_version := "0.1.61" diff --git a/justfiles/basic.just b/justfiles/basic.just index 424fa08a..f65219c6 100644 --- a/justfiles/basic.just +++ b/justfiles/basic.just @@ -98,12 +98,10 @@ miri-example EXAMPLE_NAME: } # Update README.md files for all packages. -readme: - cargo workspaces exec --ignore-private cargo doc2readme --lib --template ../README.j2 +readme: (_anvil-readme "generate") # Check that README.md files for all packages are up to date. -readme-check: - cargo workspaces exec --ignore-private cargo doc2readme --check --lib --template ../README.j2 +readme-check: (_anvil-readme "check") # Runs unit and integration tests. [script] diff --git a/justfiles/setup.just b/justfiles/setup.just index 11b424a2..df084b79 100644 --- a/justfiles/setup.just +++ b/justfiles/setup.just @@ -32,7 +32,6 @@ install-tools: cargo-semver-checks@$env:CARGO_SEMVER_CHECKS_VERSION ` cargo-sort@$env:CARGO_SORT_VERSION ` cargo-udeps@$env:CARGO_UDEPS_VERSION ` - cargo-workspaces@$env:CARGO_WORKSPACES_VERSION ` --locked # This tool is not well maintained and fails to actually build if using locked dependencies. Okay then.