diff --git a/.anvil.lock b/.anvil.lock index 649f032b9..eb10892f4 100644 --- a/.anvil.lock +++ b/.anvil.lock @@ -1,7 +1,7 @@ version = 1 tool = "anvil" tool_version = "0.11.1" -catalog_checksum = "sha256:b5e3da36fa7754d780c43c77e2cff46232de54a440b0798c91f085d2830eee3c" +catalog_checksum = "sha256:50ec8969dd7f576cef150439b044e29cef03a1c72c0f1838197cd5bb6d8a6b9e" [[file]] path = ".anvil/container/Dockerfile.dockerignore" @@ -65,27 +65,27 @@ checksum = "sha256:ef33da620683628208b4c5ad51f96f5f6424fe1d56c2d0475487228b1f1ae [[file]] path = "justfiles/anvil/checks/bench.just" -checksum = "sha256:850793443ada2151a895ee2728e81f03da5430facde16522bcc2e0c314a28577" +checksum = "sha256:3647e347fc7c418097bff446dfa5c3ae98a278bbddb0d30bf07718b6ef693879" [[file]] path = "justfiles/anvil/checks/bolero.just" -checksum = "sha256:5a1767c4ff911cec3b0870d675cbefb563193146cedd3a1cbfe1667215bdb264" +checksum = "sha256:594f7569101942b9583955f5993eeec4d1c7049c0241eb0405974b0d4809c923" [[file]] path = "justfiles/anvil/checks/careful.just" -checksum = "sha256:ea06cf7ebd7d5955b409db500a9ab193311af584437412d7bf0429fa70e43fac" +checksum = "sha256:da56c7fc7fce8acdb2c8421eb0ec97917128b8a82828da65c0137e324c4d9a2c" [[file]] path = "justfiles/anvil/checks/cargo-hack.just" -checksum = "sha256:4eca78f197c2cf428b693c49cf0af4afb735b9c6caa8c83a71e5f5fd312a9bc1" +checksum = "sha256:0e9bc3484c29de8bd27fd422d239b129f4a09675a8985cbad31130dcd0a8239d" [[file]] path = "justfiles/anvil/checks/cargo-sort.just" -checksum = "sha256:ab342c014199ffb58a9346b7b9fcea88201b5b60a07b06cbc9c4e5ef03bd18f5" +checksum = "sha256:5980f815098ed337c617d08b626c84f71d7190663efc68e3d333ba13b8934dad" [[file]] path = "justfiles/anvil/checks/clippy.just" -checksum = "sha256:945a1bf3bb1372b2bfbbfec00d520a97da56ebc5575b1e6292c421b52cc77243" +checksum = "sha256:10f4cc362ca15fd481bdcdb6a9935c6228d942b6838d8c550a1449d91dc653bb" [[file]] path = "justfiles/anvil/checks/deny.just" @@ -93,43 +93,43 @@ checksum = "sha256:c5604e8cb0eaa2baf4e2e2c70032d037d21873090375ffcf1ee99983965f6 [[file]] path = "justfiles/anvil/checks/doc-build.just" -checksum = "sha256:5ef9f04007813668a12c99c8415667800cef45843fdd7c4b263cea25547fedd4" +checksum = "sha256:21b2f589e85400792391950305c70c135ea976e99f1b0fc7b95530d7d2320518" [[file]] path = "justfiles/anvil/checks/doc-test.just" -checksum = "sha256:0e54c767de8e57a523a8b1b589a88ab6aca3169ac97d157c42d509d429f4b024" +checksum = "sha256:189e0432abb14257fe22fe153cb421673373851f27887d85740a67aaf7b41540" [[file]] path = "justfiles/anvil/checks/ensure-no-cyclic-deps.just" -checksum = "sha256:2d19930c33a93b49d0c9e9672a31d85e1ad4fb92faecc0fb599803139b11c18f" +checksum = "sha256:dbf12bf9ec44f6fe0b8bd0adbadd27ff1e3ef8eb523feb889795684101a1a623" [[file]] path = "justfiles/anvil/checks/ensure-no-default-features.just" -checksum = "sha256:38e089afe176aea92c93ac6758154b206b6fc8940931035c0d059449fa824d6c" +checksum = "sha256:c44cc763381c1c1902dd96c0789a7766cc5e9c4e79ec0d43032f8ed88e09efc3" [[file]] path = "justfiles/anvil/checks/examples.just" -checksum = "sha256:8f60f5c660ab68d7e559ed2f6234a2094f005df9856cb56f77c79955ff3aeafb" +checksum = "sha256:e0b94b511c35158ac0ccfd4baa55fc3ea874f5b95c93dc2d7ae4ef2d31d1ef3d" [[file]] path = "justfiles/anvil/checks/external-types.just" -checksum = "sha256:7e67d90a9d8bd8cd5c8adb68baf41fdd4625bff4d425b5bf18b75baee62892a8" +checksum = "sha256:f185b747b894933bd8ff44cfee8a1636901685eac2975749bd6ae5d21f8f88ae" [[file]] path = "justfiles/anvil/checks/fmt.just" -checksum = "sha256:01bb1913af9d8096290a09e51cb1c06726988484a2d72f54709bc26c8712dd3e" +checksum = "sha256:34c31bd2cdb1d8940f7a24260aa78ffb163b50181f58692c3725ee06fc2e8f60" [[file]] path = "justfiles/anvil/checks/license-headers.just" -checksum = "sha256:7aa0560dc9088b33e80cd55aee0a25090cbd3d3610652afd3734d0ca52ee7b47" +checksum = "sha256:6ca3789824cfb3e54e69e4ce25387576bbc315b870c9c371e4adf3b217926101" [[file]] path = "justfiles/anvil/checks/llvm-cov.just" -checksum = "sha256:5c4d7454b67503ce68a4d68df1783989f5afabc763df5e343bf7abddf974c387" +checksum = "sha256:e89475df42ed2adc8046038e5bb34ece09cca1734341d6526c2992c4b1f067dc" [[file]] path = "justfiles/anvil/checks/loom.just" -checksum = "sha256:f4822a8f9cb282bc426790355ba83b63e778db9327e986ba3c86ae6205cfdabb" +checksum = "sha256:96d11a30ea8c97df09a8f5acae57f3fa05ec9b0ce9068914fe763e9cbad943de" [[file]] path = "justfiles/anvil/checks/miri-race-coverage.just" @@ -145,7 +145,7 @@ checksum = "sha256:ea3349941fd3cf3811bce48a679490fe466dddafdb6d0955c1f5471155669 [[file]] path = "justfiles/anvil/checks/miri.just" -checksum = "sha256:8866d9ec87c13e30ecbefbda507315c7ed80cf7f114f32d242b4b6cb16a15ff3" +checksum = "sha256:cff2cab953041055a0321ce1e7f6ce41d377f5fe0f231a84894aa64fb93ccb17" [[file]] path = "justfiles/anvil/checks/msrv-test.just" @@ -153,7 +153,7 @@ checksum = "sha256:adc3b25f4f0b563695335f85870d9f319606c5ca80fa3d118cf221388f4bb [[file]] path = "justfiles/anvil/checks/mutants-diff.just" -checksum = "sha256:e8d23688dcb86f82bf537e82c35ab8af9296d5530898645ddfe95132f9192bd0" +checksum = "sha256:3023759370048767babfb2aaf990114fc31ce2261a70a177df37a5854d83ae7f" [[file]] path = "justfiles/anvil/checks/mutants-full.just" @@ -169,7 +169,7 @@ checksum = "sha256:750354ba4528621f8fcf545fb6f5702a7cfe64526a83b5b98243ecb4d935f [[file]] path = "justfiles/anvil/checks/semver-check.just" -checksum = "sha256:be0430a53007301db00602d4bc9296f51ad9b2f011583084d069c51b30939b74" +checksum = "sha256:7ab10c18ecfa2faf7a8ca702c4ebfe2b2eec2b7eab9602cc1151a87cff1fe3ff" [[file]] path = "justfiles/anvil/checks/spellcheck.just" @@ -177,7 +177,7 @@ checksum = "sha256:2b2063f7e494dba51f805534bce5235132be1eeabde3a118d42d87ce49d51 [[file]] path = "justfiles/anvil/checks/udeps.just" -checksum = "sha256:6efd7378a2cd0f5d86519bd32fd86f2055a60191187dd77a8842b374b8eb7102" +checksum = "sha256:99f44d7b6d05b1ee54403359445c7649e202dacb919ada1db354447314dfe636" [[file]] path = "justfiles/anvil/container.just" @@ -229,7 +229,7 @@ checksum = "sha256:04679222579a090769403f5aae7c9ffabbf5bd559ddb1f17b5a0655152172 [[file]] path = "justfiles/anvil/helpers.just" -checksum = "sha256:5c901c3889ada98362a516364fe8188546dab1a6475d17751066f39e0457a3dd" +checksum = "sha256:d4d62d49984157f5022342f558efa66a71758ea65d97752ab0ecddfdadf4616d" [[file]] path = "justfiles/anvil/impact.just" diff --git a/crates/cargo-anvil/docs/design/checks.md b/crates/cargo-anvil/docs/design/checks.md index 149b5c9a9..88696b29e 100644 --- a/crates/cargo-anvil/docs/design/checks.md +++ b/crates/cargo-anvil/docs/design/checks.md @@ -390,7 +390,11 @@ dependency. Every **impact-scoped** per-crate check depends on `anvil-impact` and resolves its category's scope by calling `_anvil-impact-include ` into a local `$include` -variable, then consumes it (the unscoped checks below take no such dependency). The +variable, propagates a nonzero resolver exit before reading that variable, then consumes +it (the unscoped checks below take no such dependency). The propagation matters because +an unguarded failure would leave `$include` empty, silently widening the check to its +unscoped default and leaving the run green — the same failure mode `anvil-impact` already +refuses in consume mode. The **same** cache is read in cloud workflows — the impact job uploads `target/anvil/impact/` as an artifact and each group job downloads it — so the identical code path runs locally and in CI, with no scoping threaded through environment variables. Scoping is on by diff --git a/crates/cargo-anvil/src/anvil/artifacts/justfile.rs b/crates/cargo-anvil/src/anvil/artifacts/justfile.rs index dc4777691..6e516db33 100644 --- a/crates/cargo-anvil/src/anvil/artifacts/justfile.rs +++ b/crates/cargo-anvil/src/anvil/artifacts/justfile.rs @@ -415,6 +415,32 @@ mod tests { } } + #[test] + fn impact_scoped_checks_propagate_scope_resolution_failures() { + const CALL: &str = "$include = (& \"{{ just_executable() }}\" _anvil-impact-include"; + let mut scoped = 0; + for (path, body) in CHECK_FILES { + let lines: Vec<&str> = body.lines().collect(); + for (index, line) in lines.iter().enumerate() { + if !line.contains(CALL) { + continue; + } + scoped += 1; + assert_eq!( + lines.get(index + 1).copied(), + Some(" $impactExit = $LASTEXITCODE"), + "{path} must capture the impact resolver exit code before using its output" + ); + assert_eq!( + lines.get(index + 2).copied(), + Some(" if ($impactExit -ne 0) { exit $impactExit }"), + "{path} must propagate the impact resolver exit code before using its output" + ); + } + } + assert_eq!(scoped, 21, "every impact-scoped check must be covered"); + } + #[test] fn miri_profiles_inherit_the_parallel_artifact_runner() { let miri = CHECK_FILES diff --git a/crates/cargo-anvil/templates/justfiles/anvil/checks/bench.just b/crates/cargo-anvil/templates/justfiles/anvil/checks/bench.just index b26880a81..074caf3cb 100644 --- a/crates/cargo-anvil/templates/justfiles/anvil/checks/bench.just +++ b/crates/cargo-anvil/templates/justfiles/anvil/checks/bench.just @@ -13,6 +13,8 @@ anvil-bench: anvil-bench-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-bench: no affected packages; skipping'; exit 0 } & cargo {{_anvil_stable_toolchain_args}} bench @(if ($include) { -split $include } else { '--workspace' }) --all-features --no-run if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } diff --git a/crates/cargo-anvil/templates/justfiles/anvil/checks/bolero.just b/crates/cargo-anvil/templates/justfiles/anvil/checks/bolero.just index 344fabdcc..e9590ac30 100644 --- a/crates/cargo-anvil/templates/justfiles/anvil/checks/bolero.just +++ b/crates/cargo-anvil/templates/justfiles/anvil/checks/bolero.just @@ -33,6 +33,8 @@ anvil-bolero: anvil-bolero-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if (-not $IsLinux) { Write-Host 'anvil-bolero: non-Linux host -- skipping (cargo-bolero/libfuzzer is Linux-only; harnesses still run under anvil-llvm-cov)' exit 0 diff --git a/crates/cargo-anvil/templates/justfiles/anvil/checks/careful.just b/crates/cargo-anvil/templates/justfiles/anvil/checks/careful.just index c937a5dc2..be342ffc2 100644 --- a/crates/cargo-anvil/templates/justfiles/anvil/checks/careful.just +++ b/crates/cargo-anvil/templates/justfiles/anvil/checks/careful.just @@ -11,6 +11,8 @@ anvil-careful: anvil-careful-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-careful: no affected packages; skipping'; exit 0 } $pkg = @(if ($include) { -split $include } else { '--workspace' }) # Guard against cargo-careful's stale-sysroot hazard. cargo-careful diff --git a/crates/cargo-anvil/templates/justfiles/anvil/checks/cargo-hack.just b/crates/cargo-anvil/templates/justfiles/anvil/checks/cargo-hack.just index 0377fb4da..994e80799 100644 --- a/crates/cargo-anvil/templates/justfiles/anvil/checks/cargo-hack.just +++ b/crates/cargo-anvil/templates/justfiles/anvil/checks/cargo-hack.just @@ -15,6 +15,8 @@ anvil-cargo-hack: anvil-cargo-hack-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include required) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-cargo-hack: no affected packages; skipping'; exit 0 } & cargo {{_anvil_stable_toolchain_args}} hack @(if ($include) { -split $include } else { '--workspace' }) --feature-powerset --depth 2 check if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } diff --git a/crates/cargo-anvil/templates/justfiles/anvil/checks/cargo-sort.just b/crates/cargo-anvil/templates/justfiles/anvil/checks/cargo-sort.just index 9744892f0..ac9053e0d 100644 --- a/crates/cargo-anvil/templates/justfiles/anvil/checks/cargo-sort.just +++ b/crates/cargo-anvil/templates/justfiles/anvil/checks/cargo-sort.just @@ -20,6 +20,8 @@ anvil-cargo-sort: anvil-cargo-sort-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include modified) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-cargo-sort: no modified packages; skipping'; exit 0 } & cargo {{_anvil_stable_toolchain_args}} sort --workspace --grouped --check --check-format if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } diff --git a/crates/cargo-anvil/templates/justfiles/anvil/checks/clippy.just b/crates/cargo-anvil/templates/justfiles/anvil/checks/clippy.just index a0354f2c4..223f0750c 100644 --- a/crates/cargo-anvil/templates/justfiles/anvil/checks/clippy.just +++ b/crates/cargo-anvil/templates/justfiles/anvil/checks/clippy.just @@ -18,6 +18,8 @@ anvil-clippy: anvil-clippy-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-clippy: no affected packages; skipping'; exit 0 } & cargo {{_anvil_stable_toolchain_args}} clippy @(if ($include) { -split $include } else { '--workspace' }) --all-targets --all-features --locked "--" '-D' 'warnings' if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } diff --git a/crates/cargo-anvil/templates/justfiles/anvil/checks/doc-build.just b/crates/cargo-anvil/templates/justfiles/anvil/checks/doc-build.just index ab0095978..117179966 100644 --- a/crates/cargo-anvil/templates/justfiles/anvil/checks/doc-build.just +++ b/crates/cargo-anvil/templates/justfiles/anvil/checks/doc-build.just @@ -16,6 +16,8 @@ anvil-doc-build open="false": anvil-doc-build-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include required) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-doc-build: no affected packages; skipping'; exit 0 } $env:RUSTDOCFLAGS = '-D warnings' $pkg = @(if ($include) { -split $include } else { '--workspace' }) diff --git a/crates/cargo-anvil/templates/justfiles/anvil/checks/doc-test.just b/crates/cargo-anvil/templates/justfiles/anvil/checks/doc-test.just index ca6ade8c9..190e8b8d5 100644 --- a/crates/cargo-anvil/templates/justfiles/anvil/checks/doc-test.just +++ b/crates/cargo-anvil/templates/justfiles/anvil/checks/doc-test.just @@ -18,6 +18,8 @@ anvil-doc-test: anvil-doc-test-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-doc-test: no affected packages; skipping'; exit 0 } $pkg = @(if ($include) { -split $include } else { '--workspace' }) & cargo {{_anvil_stable_toolchain_args}} test --doc @pkg --all-features --locked diff --git a/crates/cargo-anvil/templates/justfiles/anvil/checks/ensure-no-cyclic-deps.just b/crates/cargo-anvil/templates/justfiles/anvil/checks/ensure-no-cyclic-deps.just index 57f360170..34bac20b0 100644 --- a/crates/cargo-anvil/templates/justfiles/anvil/checks/ensure-no-cyclic-deps.just +++ b/crates/cargo-anvil/templates/justfiles/anvil/checks/ensure-no-cyclic-deps.just @@ -13,6 +13,8 @@ anvil-ensure-no-cyclic-deps: anvil-ensure-no-cyclic-deps-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include modified) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-ensure-no-cyclic-deps: no modified packages; skipping'; exit 0 } & cargo {{_anvil_stable_toolchain_args}} ensure-no-cyclic-deps if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } diff --git a/crates/cargo-anvil/templates/justfiles/anvil/checks/ensure-no-default-features.just b/crates/cargo-anvil/templates/justfiles/anvil/checks/ensure-no-default-features.just index cee4d24d5..0b2bddace 100644 --- a/crates/cargo-anvil/templates/justfiles/anvil/checks/ensure-no-default-features.just +++ b/crates/cargo-anvil/templates/justfiles/anvil/checks/ensure-no-default-features.just @@ -13,6 +13,8 @@ anvil-ensure-no-default-features: anvil-ensure-no-default-features-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include modified) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-ensure-no-default-features: no modified packages; skipping'; exit 0 } & cargo {{_anvil_stable_toolchain_args}} ensure-no-default-features if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } diff --git a/crates/cargo-anvil/templates/justfiles/anvil/checks/examples.just b/crates/cargo-anvil/templates/justfiles/anvil/checks/examples.just index efdbe6a91..b38d87733 100644 --- a/crates/cargo-anvil/templates/justfiles/anvil/checks/examples.just +++ b/crates/cargo-anvil/templates/justfiles/anvil/checks/examples.just @@ -26,6 +26,8 @@ anvil-examples run="false" package="" example="" timeout="30": anvil-examples-va exit 2 } $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if (-not $package -and $include -eq '--skip') { Write-Host 'anvil-examples: no affected packages; skipping'; exit 0 } $pkgArgs = @(if ($package) { '--package'; $package } elseif ($include) { -split $include } else { '--workspace' }) $exampleArgs = @(if ($example) { '--example'; $example } else { '--examples' }) diff --git a/crates/cargo-anvil/templates/justfiles/anvil/checks/external-types.just b/crates/cargo-anvil/templates/justfiles/anvil/checks/external-types.just index d659a3d4d..f25f11ff2 100644 --- a/crates/cargo-anvil/templates/justfiles/anvil/checks/external-types.just +++ b/crates/cargo-anvil/templates/justfiles/anvil/checks/external-types.just @@ -24,6 +24,8 @@ anvil-external-types: anvil-external-types-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-external-types: no affected packages; skipping' exit 0 diff --git a/crates/cargo-anvil/templates/justfiles/anvil/checks/fmt.just b/crates/cargo-anvil/templates/justfiles/anvil/checks/fmt.just index 299cb33ac..e4eb7ff41 100644 --- a/crates/cargo-anvil/templates/justfiles/anvil/checks/fmt.just +++ b/crates/cargo-anvil/templates/justfiles/anvil/checks/fmt.just @@ -26,6 +26,8 @@ anvil-fmt fix="false": anvil-fmt-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include modified) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-fmt: no modified packages; skipping'; exit 0 } cargo '+{{ rust_nightly }}' each --workspace --keep-going '--' cargo '+{{ rust_nightly }}' fmt --manifest-path '{manifest}' {{ if fix == "true" { "" } else { "--check" } }} if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } diff --git a/crates/cargo-anvil/templates/justfiles/anvil/checks/license-headers.just b/crates/cargo-anvil/templates/justfiles/anvil/checks/license-headers.just index fb794c32e..7839b444b 100644 --- a/crates/cargo-anvil/templates/justfiles/anvil/checks/license-headers.just +++ b/crates/cargo-anvil/templates/justfiles/anvil/checks/license-headers.just @@ -13,6 +13,8 @@ anvil-license-headers: anvil-license-headers-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include modified) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-license-headers: no modified packages; skipping'; exit 0 } & cargo {{_anvil_stable_toolchain_args}} heather if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } diff --git a/crates/cargo-anvil/templates/justfiles/anvil/checks/llvm-cov.just b/crates/cargo-anvil/templates/justfiles/anvil/checks/llvm-cov.just index a4531ecb3..5b75bdb0a 100644 --- a/crates/cargo-anvil/templates/justfiles/anvil/checks/llvm-cov.just +++ b/crates/cargo-anvil/templates/justfiles/anvil/checks/llvm-cov.just @@ -18,6 +18,8 @@ anvil-llvm-cov: anvil-llvm-cov-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-llvm-cov: no affected packages; skipping' exit 0 diff --git a/crates/cargo-anvil/templates/justfiles/anvil/checks/loom.just b/crates/cargo-anvil/templates/justfiles/anvil/checks/loom.just index 4703a71f3..8acc6994e 100644 --- a/crates/cargo-anvil/templates/justfiles/anvil/checks/loom.just +++ b/crates/cargo-anvil/templates/justfiles/anvil/checks/loom.just @@ -53,6 +53,8 @@ anvil-loom: anvil-loom-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-loom: no affected packages; skipping' exit 0 diff --git a/crates/cargo-anvil/templates/justfiles/anvil/checks/miri.just b/crates/cargo-anvil/templates/justfiles/anvil/checks/miri.just index 72090a0c2..5ec10f934 100644 --- a/crates/cargo-anvil/templates/justfiles/anvil/checks/miri.just +++ b/crates/cargo-anvil/templates/justfiles/anvil/checks/miri.just @@ -90,7 +90,8 @@ _anvil-miri-test profile package="" test_filter="" example="": anvil-impact } $include = (& "{{ just_executable() }}" _anvil-impact-include affected) - if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if (-not $package -and $include -eq '--skip') { Write-Host "$recipeName`: no affected packages; skipping" exit 0 diff --git a/crates/cargo-anvil/templates/justfiles/anvil/checks/mutants-diff.just b/crates/cargo-anvil/templates/justfiles/anvil/checks/mutants-diff.just index e4efc812f..f4d6d31e0 100644 --- a/crates/cargo-anvil/templates/justfiles/anvil/checks/mutants-diff.just +++ b/crates/cargo-anvil/templates/justfiles/anvil/checks/mutants-diff.just @@ -21,6 +21,8 @@ anvil-mutants-diff: anvil-mutants-diff-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($IsWindows -and ($env:PROCESSOR_ARCHITECTURE -eq 'ARM64' -or $env:PROCESSOR_ARCHITEW6432 -eq 'ARM64')) { Write-Host 'anvil-mutants-diff: anvil-mutants-diff-validate-prereqs aarch64-pc-windows-msvc -- cargo-mutants does not build here (winapi); skipping' exit 0 diff --git a/crates/cargo-anvil/templates/justfiles/anvil/checks/semver-check.just b/crates/cargo-anvil/templates/justfiles/anvil/checks/semver-check.just index 5dbd16128..deb72eca2 100644 --- a/crates/cargo-anvil/templates/justfiles/anvil/checks/semver-check.just +++ b/crates/cargo-anvil/templates/justfiles/anvil/checks/semver-check.just @@ -51,6 +51,8 @@ anvil-semver-check: anvil-semver-check-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $stableArgs = {{_anvil_stable_toolchain_args}} $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } $commentFile = 'target/anvil/comments/semver.md' if ($include -eq '--skip') { Write-Host 'anvil-semver-check: no affected packages; skipping' diff --git a/crates/cargo-anvil/templates/justfiles/anvil/checks/udeps.just b/crates/cargo-anvil/templates/justfiles/anvil/checks/udeps.just index 83770a4c9..a94ff77ac 100644 --- a/crates/cargo-anvil/templates/justfiles/anvil/checks/udeps.just +++ b/crates/cargo-anvil/templates/justfiles/anvil/checks/udeps.just @@ -29,6 +29,8 @@ anvil-udeps: anvil-udeps-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include required) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-udeps: no affected packages; skipping'; exit 0 } $pkg = @(if ($include) { -split $include } else { '--workspace' }) # Pass 1: default targets (lib + bins) -- surfaces [dependencies] that diff --git a/crates/cargo-anvil/templates/justfiles/anvil/helpers.just b/crates/cargo-anvil/templates/justfiles/anvil/helpers.just index ebfbc07dc..1a1f6b0cd 100644 --- a/crates/cargo-anvil/templates/justfiles/anvil/helpers.just +++ b/crates/cargo-anvil/templates/justfiles/anvil/helpers.just @@ -57,10 +57,24 @@ # like-named crate is also a transitive dependency. # * "--skip" - the tier is empty for this change (recipe exits 0). # -# This keeps the per-recipe boilerplate to a single capture, a one-line skip -# guard, and the cargo invocation. Modified-tier recipes never forward -# `$include`; they only check the skip sentinel and then let their command -# determine its normal input domain. +# Those three values are only meaningful when the resolver succeeded, so every +# caller captures its exit code and propagates it before reading `$include`: +# +# $impactExit = $LASTEXITCODE +# if ($impactExit -ne 0) { exit $impactExit } +# +# `$ErrorActionPreference = 'Stop'` does not fail a recipe on a native +# command's nonzero exit, so without that guard a failed lookup (a missing +# cache under ANVIL_IMPACT=consume, an unreadable include file, an +# unrecognized ANVIL_IMPACT value) would leave `$include` empty and the check +# would silently widen to its unscoped default and pass. The same reason +# anvil-impact fails loudly in consume mode applies to reading the cache it +# asserts: a broken scope must stop the check, not quietly rescope it. +# +# This keeps the per-recipe boilerplate to a single capture, the exit-code +# guard, a one-line skip guard, and the cargo invocation. Modified-tier +# recipes never forward `$include`; they only check the skip sentinel and then +# let their command determine its normal input domain. # # Every recipe whose body uses multi-line conditionals or array-splat # splicing is annotated with [script("pwsh", "-NoProfile")]. pwsh is preinstalled on diff --git a/crates/cargo-anvil/tests/recipe_contracts.rs b/crates/cargo-anvil/tests/recipe_contracts.rs index d87229ef8..176dfb320 100644 --- a/crates/cargo-anvil/tests/recipe_contracts.rs +++ b/crates/cargo-anvil/tests/recipe_contracts.rs @@ -404,7 +404,7 @@ fn write(path: &Path, contents: &str) { /// Seed the impact cache that scoped check recipes read via /// `_anvil-impact-include`, standing in for a completed `anvil-impact` run. -/// Without a cache file the recipes fall back to their tier default +/// Outside consume mode, a missing cache falls back to the tier default /// (`--workspace` for the affected tier), so tests that exercise a scoped run /// must plant the include file the recipe consumes. fn seed_include(root: &Path, tier: &str, spec: &str) { @@ -587,6 +587,42 @@ fn assert_failed(output: &Output, context: &str) { ); } +#[test] +fn scoped_check_propagates_missing_consumed_impact_cache() { + if !tools_available() { + return; + } + let tmp = fixture( + &[("fmt.just", FMT), ("impact.just", IMPACT)], + &[ + "anvil-component-nightly-rustfmt-validate-prereqs", + "anvil-component-nightly-rustfmt-install", + "anvil-tool-cargo-each-validate-prereqs", + "anvil-tool-cargo-each-install installer", + "anvil-impact", + ], + ); + let log = tmp.path().join("cargo.log"); + let output = run_just( + tmp.path(), + &["anvil-fmt"], + &[("ANVIL_IMPACT", OsStr::new("consume")), ("FAKE_CARGO_LOG", log.as_os_str())], + ); + + assert_failed(&output, "missing consumed impact cache"); + // anvil-impact is stubbed here, so the only component that can report a + // missing include file is the resolver called from inside anvil-fmt -- + // which proves the recipe body ran and propagated, rather than just + // failing to load the fixture or tripping the dependency's own guard. + let stderr = String::from_utf8_lossy(&output.stderr); + assert!( + stderr.contains("include_modified.txt"), + "the scoped check must surface the resolver's own cache-missing error\nstderr:\n{stderr}" + ); + assert_eq!(output.status.code(), Some(1)); + assert!(!log.exists(), "the scoped command must not run after impact scope resolution fails"); +} + fn assert_miri_cargo_calls(cargo_calls: &str) { assert!( cargo_calls.contains("+nightly-test metadata --no-deps --format-version 1"), diff --git a/crates/cargo-anvil/tests/snapshots/snapshots__ado_backend.snap b/crates/cargo-anvil/tests/snapshots/snapshots__ado_backend.snap index 75056f9ce..e525f8f11 100644 --- a/crates/cargo-anvil/tests/snapshots/snapshots__ado_backend.snap +++ b/crates/cargo-anvil/tests/snapshots/snapshots__ado_backend.snap @@ -1936,6 +1936,8 @@ anvil-audit-validate-prereqs: anvil-tool-cargo-audit-validate-prereqs anvil-bench: anvil-bench-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-bench: no affected packages; skipping'; exit 0 } & cargo {{_anvil_stable_toolchain_args}} bench @(if ($include) { -split $include } else { '--workspace' }) --all-features --no-run if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } @@ -1987,6 +1989,8 @@ anvil-bench-validate-prereqs: anvil-tool-rustc-validate-prereqs anvil-bolero: anvil-bolero-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if (-not $IsLinux) { Write-Host 'anvil-bolero: non-Linux host -- skipping (cargo-bolero/libfuzzer is Linux-only; harnesses still run under anvil-llvm-cov)' exit 0 @@ -2087,6 +2091,8 @@ anvil-bolero-validate-prereqs: anvil-toolchain-nightly-validate-prereqs anvil-to anvil-careful: anvil-careful-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-careful: no affected packages; skipping'; exit 0 } $pkg = @(if ($include) { -split $include } else { '--workspace' }) # Guard against cargo-careful's stale-sysroot hazard. cargo-careful @@ -2171,6 +2177,8 @@ anvil-careful-validate-prereqs: anvil-component-nightly-rust-src-validate-prereq anvil-cargo-hack: anvil-cargo-hack-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include required) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-cargo-hack: no affected packages; skipping'; exit 0 } & cargo {{_anvil_stable_toolchain_args}} hack @(if ($include) { -split $include } else { '--workspace' }) --feature-powerset --depth 2 check if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } @@ -2206,6 +2214,8 @@ anvil-cargo-hack-validate-prereqs: anvil-tool-cargo-hack-validate-prereqs anvil-cargo-sort: anvil-cargo-sort-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include modified) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-cargo-sort: no modified packages; skipping'; exit 0 } & cargo {{_anvil_stable_toolchain_args}} sort --workspace --grouped --check --check-format if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } @@ -2239,6 +2249,8 @@ anvil-cargo-sort-validate-prereqs: anvil-tool-cargo-sort-validate-prereqs anvil-clippy: anvil-clippy-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-clippy: no affected packages; skipping'; exit 0 } & cargo {{_anvil_stable_toolchain_args}} clippy @(if ($include) { -split $include } else { '--workspace' }) --all-targets --all-features --locked "--" '-D' 'warnings' if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } @@ -2298,6 +2310,8 @@ anvil-deny-validate-prereqs: anvil-tool-cargo-deny-validate-prereqs anvil-doc-build open="false": anvil-doc-build-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include required) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-doc-build: no affected packages; skipping'; exit 0 } $env:RUSTDOCFLAGS = '-D warnings' $pkg = @(if ($include) { -split $include } else { '--workspace' }) @@ -2337,6 +2351,8 @@ anvil-doc-build-validate-prereqs: anvil-tool-rustc-validate-prereqs anvil-doc-test: anvil-doc-test-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-doc-test: no affected packages; skipping'; exit 0 } $pkg = @(if ($include) { -split $include } else { '--workspace' }) & cargo {{_anvil_stable_toolchain_args}} test --doc @pkg --all-features --locked @@ -2368,6 +2384,8 @@ anvil-doc-test-validate-prereqs: anvil-tool-rustc-validate-prereqs anvil-ensure-no-cyclic-deps: anvil-ensure-no-cyclic-deps-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include modified) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-ensure-no-cyclic-deps: no modified packages; skipping'; exit 0 } & cargo {{_anvil_stable_toolchain_args}} ensure-no-cyclic-deps if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } @@ -2396,6 +2414,8 @@ anvil-ensure-no-cyclic-deps-validate-prereqs: anvil-tool-cargo-ensure-no-cyclic- anvil-ensure-no-default-features: anvil-ensure-no-default-features-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include modified) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-ensure-no-default-features: no modified packages; skipping'; exit 0 } & cargo {{_anvil_stable_toolchain_args}} ensure-no-default-features if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } @@ -2437,6 +2457,8 @@ anvil-examples run="false" package="" example="" timeout="30": anvil-examples-va exit 2 } $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if (-not $package -and $include -eq '--skip') { Write-Host 'anvil-examples: no affected packages; skipping'; exit 0 } $pkgArgs = @(if ($package) { '--package'; $package } elseif ($include) { -split $include } else { '--workspace' }) $exampleArgs = @(if ($example) { '--example'; $example } else { '--examples' }) @@ -2570,6 +2592,8 @@ anvil-examples-validate-prereqs: anvil-tool-rustc-validate-prereqs anvil-external-types: anvil-external-types-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-external-types: no affected packages; skipping' exit 0 @@ -2669,6 +2693,8 @@ anvil-external-types-validate-prereqs: anvil-toolchain-nightly-external-types-va anvil-fmt fix="false": anvil-fmt-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include modified) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-fmt: no modified packages; skipping'; exit 0 } cargo '+{{ rust_nightly }}' each --workspace --keep-going '--' cargo '+{{ rust_nightly }}' fmt --manifest-path '{manifest}' {{ if fix == "true" { "" } else { "--check" } }} if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } @@ -2712,6 +2738,8 @@ anvil-fmt-validate-prereqs: anvil-component-nightly-rustfmt-validate-prereqs anv anvil-license-headers: anvil-license-headers-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include modified) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-license-headers: no modified packages; skipping'; exit 0 } & cargo {{_anvil_stable_toolchain_args}} heather if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } @@ -2745,6 +2773,8 @@ anvil-license-headers-validate-prereqs: anvil-tool-cargo-heather-validate-prereq anvil-llvm-cov: anvil-llvm-cov-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-llvm-cov: no affected packages; skipping' exit 0 @@ -3036,6 +3066,8 @@ anvil-llvm-cov-validate-prereqs: anvil-component-nightly-llvm-tools-validate-pre anvil-loom: anvil-loom-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-loom: no affected packages; skipping' exit 0 @@ -3288,7 +3320,8 @@ _anvil-miri-test profile package="" test_filter="" example="": anvil-impact } $include = (& "{{ just_executable() }}" _anvil-impact-include affected) - if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if (-not $package -and $include -eq '--skip') { Write-Host "$recipeName`: no affected packages; skipping" exit 0 @@ -3673,6 +3706,8 @@ anvil-msrv-test-validate-prereqs: anvil-mutants-diff: anvil-mutants-diff-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($IsWindows -and ($env:PROCESSOR_ARCHITECTURE -eq 'ARM64' -or $env:PROCESSOR_ARCHITEW6432 -eq 'ARM64')) { Write-Host 'anvil-mutants-diff: anvil-mutants-diff-validate-prereqs aarch64-pc-windows-msvc -- cargo-mutants does not build here (winapi); skipping' exit 0 @@ -4015,6 +4050,8 @@ anvil-semver-check: anvil-semver-check-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $stableArgs = {{_anvil_stable_toolchain_args}} $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } $commentFile = 'target/anvil/comments/semver.md' if ($include -eq '--skip') { Write-Host 'anvil-semver-check: no affected packages; skipping' @@ -4303,6 +4340,8 @@ anvil-spellcheck-validate-prereqs: anvil-tool-cargo-spellcheck-validate-prereqs anvil-udeps: anvil-udeps-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include required) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-udeps: no affected packages; skipping'; exit 0 } $pkg = @(if ($include) { -split $include } else { '--workspace' }) # Pass 1: default targets (lib + bins) -- surfaces [dependencies] that @@ -6026,10 +6065,24 @@ anvil-scheduled-test-validate-prereqs: \ # like-named crate is also a transitive dependency. # * "--skip" - the tier is empty for this change (recipe exits 0). # -# This keeps the per-recipe boilerplate to a single capture, a one-line skip -# guard, and the cargo invocation. Modified-tier recipes never forward -# `$include`; they only check the skip sentinel and then let their command -# determine its normal input domain. +# Those three values are only meaningful when the resolver succeeded, so every +# caller captures its exit code and propagates it before reading `$include`: +# +# $impactExit = $LASTEXITCODE +# if ($impactExit -ne 0) { exit $impactExit } +# +# `$ErrorActionPreference = 'Stop'` does not fail a recipe on a native +# command's nonzero exit, so without that guard a failed lookup (a missing +# cache under ANVIL_IMPACT=consume, an unreadable include file, an +# unrecognized ANVIL_IMPACT value) would leave `$include` empty and the check +# would silently widen to its unscoped default and pass. The same reason +# anvil-impact fails loudly in consume mode applies to reading the cache it +# asserts: a broken scope must stop the check, not quietly rescope it. +# +# This keeps the per-recipe boilerplate to a single capture, the exit-code +# guard, a one-line skip guard, and the cargo invocation. Modified-tier +# recipes never forward `$include`; they only check the skip sentinel and then +# let their command determine its normal input domain. # # Every recipe whose body uses multi-line conditionals or array-splat # splicing is annotated with [script("pwsh", "-NoProfile")]. pwsh is preinstalled on diff --git a/crates/cargo-anvil/tests/snapshots/snapshots__github_backend.snap b/crates/cargo-anvil/tests/snapshots/snapshots__github_backend.snap index 29ecea517..1b48dc0eb 100644 --- a/crates/cargo-anvil/tests/snapshots/snapshots__github_backend.snap +++ b/crates/cargo-anvil/tests/snapshots/snapshots__github_backend.snap @@ -2049,6 +2049,8 @@ anvil-audit-validate-prereqs: anvil-tool-cargo-audit-validate-prereqs anvil-bench: anvil-bench-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-bench: no affected packages; skipping'; exit 0 } & cargo {{_anvil_stable_toolchain_args}} bench @(if ($include) { -split $include } else { '--workspace' }) --all-features --no-run if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } @@ -2100,6 +2102,8 @@ anvil-bench-validate-prereqs: anvil-tool-rustc-validate-prereqs anvil-bolero: anvil-bolero-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if (-not $IsLinux) { Write-Host 'anvil-bolero: non-Linux host -- skipping (cargo-bolero/libfuzzer is Linux-only; harnesses still run under anvil-llvm-cov)' exit 0 @@ -2200,6 +2204,8 @@ anvil-bolero-validate-prereqs: anvil-toolchain-nightly-validate-prereqs anvil-to anvil-careful: anvil-careful-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-careful: no affected packages; skipping'; exit 0 } $pkg = @(if ($include) { -split $include } else { '--workspace' }) # Guard against cargo-careful's stale-sysroot hazard. cargo-careful @@ -2284,6 +2290,8 @@ anvil-careful-validate-prereqs: anvil-component-nightly-rust-src-validate-prereq anvil-cargo-hack: anvil-cargo-hack-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include required) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-cargo-hack: no affected packages; skipping'; exit 0 } & cargo {{_anvil_stable_toolchain_args}} hack @(if ($include) { -split $include } else { '--workspace' }) --feature-powerset --depth 2 check if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } @@ -2319,6 +2327,8 @@ anvil-cargo-hack-validate-prereqs: anvil-tool-cargo-hack-validate-prereqs anvil-cargo-sort: anvil-cargo-sort-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include modified) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-cargo-sort: no modified packages; skipping'; exit 0 } & cargo {{_anvil_stable_toolchain_args}} sort --workspace --grouped --check --check-format if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } @@ -2352,6 +2362,8 @@ anvil-cargo-sort-validate-prereqs: anvil-tool-cargo-sort-validate-prereqs anvil-clippy: anvil-clippy-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-clippy: no affected packages; skipping'; exit 0 } & cargo {{_anvil_stable_toolchain_args}} clippy @(if ($include) { -split $include } else { '--workspace' }) --all-targets --all-features --locked "--" '-D' 'warnings' if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } @@ -2411,6 +2423,8 @@ anvil-deny-validate-prereqs: anvil-tool-cargo-deny-validate-prereqs anvil-doc-build open="false": anvil-doc-build-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include required) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-doc-build: no affected packages; skipping'; exit 0 } $env:RUSTDOCFLAGS = '-D warnings' $pkg = @(if ($include) { -split $include } else { '--workspace' }) @@ -2450,6 +2464,8 @@ anvil-doc-build-validate-prereqs: anvil-tool-rustc-validate-prereqs anvil-doc-test: anvil-doc-test-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-doc-test: no affected packages; skipping'; exit 0 } $pkg = @(if ($include) { -split $include } else { '--workspace' }) & cargo {{_anvil_stable_toolchain_args}} test --doc @pkg --all-features --locked @@ -2481,6 +2497,8 @@ anvil-doc-test-validate-prereqs: anvil-tool-rustc-validate-prereqs anvil-ensure-no-cyclic-deps: anvil-ensure-no-cyclic-deps-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include modified) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-ensure-no-cyclic-deps: no modified packages; skipping'; exit 0 } & cargo {{_anvil_stable_toolchain_args}} ensure-no-cyclic-deps if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } @@ -2509,6 +2527,8 @@ anvil-ensure-no-cyclic-deps-validate-prereqs: anvil-tool-cargo-ensure-no-cyclic- anvil-ensure-no-default-features: anvil-ensure-no-default-features-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include modified) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-ensure-no-default-features: no modified packages; skipping'; exit 0 } & cargo {{_anvil_stable_toolchain_args}} ensure-no-default-features if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } @@ -2550,6 +2570,8 @@ anvil-examples run="false" package="" example="" timeout="30": anvil-examples-va exit 2 } $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if (-not $package -and $include -eq '--skip') { Write-Host 'anvil-examples: no affected packages; skipping'; exit 0 } $pkgArgs = @(if ($package) { '--package'; $package } elseif ($include) { -split $include } else { '--workspace' }) $exampleArgs = @(if ($example) { '--example'; $example } else { '--examples' }) @@ -2683,6 +2705,8 @@ anvil-examples-validate-prereqs: anvil-tool-rustc-validate-prereqs anvil-external-types: anvil-external-types-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-external-types: no affected packages; skipping' exit 0 @@ -2782,6 +2806,8 @@ anvil-external-types-validate-prereqs: anvil-toolchain-nightly-external-types-va anvil-fmt fix="false": anvil-fmt-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include modified) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-fmt: no modified packages; skipping'; exit 0 } cargo '+{{ rust_nightly }}' each --workspace --keep-going '--' cargo '+{{ rust_nightly }}' fmt --manifest-path '{manifest}' {{ if fix == "true" { "" } else { "--check" } }} if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } @@ -2825,6 +2851,8 @@ anvil-fmt-validate-prereqs: anvil-component-nightly-rustfmt-validate-prereqs anv anvil-license-headers: anvil-license-headers-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include modified) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-license-headers: no modified packages; skipping'; exit 0 } & cargo {{_anvil_stable_toolchain_args}} heather if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } @@ -2858,6 +2886,8 @@ anvil-license-headers-validate-prereqs: anvil-tool-cargo-heather-validate-prereq anvil-llvm-cov: anvil-llvm-cov-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-llvm-cov: no affected packages; skipping' exit 0 @@ -3149,6 +3179,8 @@ anvil-llvm-cov-validate-prereqs: anvil-component-nightly-llvm-tools-validate-pre anvil-loom: anvil-loom-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-loom: no affected packages; skipping' exit 0 @@ -3401,7 +3433,8 @@ _anvil-miri-test profile package="" test_filter="" example="": anvil-impact } $include = (& "{{ just_executable() }}" _anvil-impact-include affected) - if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if (-not $package -and $include -eq '--skip') { Write-Host "$recipeName`: no affected packages; skipping" exit 0 @@ -3786,6 +3819,8 @@ anvil-msrv-test-validate-prereqs: anvil-mutants-diff: anvil-mutants-diff-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($IsWindows -and ($env:PROCESSOR_ARCHITECTURE -eq 'ARM64' -or $env:PROCESSOR_ARCHITEW6432 -eq 'ARM64')) { Write-Host 'anvil-mutants-diff: anvil-mutants-diff-validate-prereqs aarch64-pc-windows-msvc -- cargo-mutants does not build here (winapi); skipping' exit 0 @@ -4128,6 +4163,8 @@ anvil-semver-check: anvil-semver-check-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $stableArgs = {{_anvil_stable_toolchain_args}} $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } $commentFile = 'target/anvil/comments/semver.md' if ($include -eq '--skip') { Write-Host 'anvil-semver-check: no affected packages; skipping' @@ -4416,6 +4453,8 @@ anvil-spellcheck-validate-prereqs: anvil-tool-cargo-spellcheck-validate-prereqs anvil-udeps: anvil-udeps-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include required) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-udeps: no affected packages; skipping'; exit 0 } $pkg = @(if ($include) { -split $include } else { '--workspace' }) # Pass 1: default targets (lib + bins) -- surfaces [dependencies] that @@ -6139,10 +6178,24 @@ anvil-scheduled-test-validate-prereqs: \ # like-named crate is also a transitive dependency. # * "--skip" - the tier is empty for this change (recipe exits 0). # -# This keeps the per-recipe boilerplate to a single capture, a one-line skip -# guard, and the cargo invocation. Modified-tier recipes never forward -# `$include`; they only check the skip sentinel and then let their command -# determine its normal input domain. +# Those three values are only meaningful when the resolver succeeded, so every +# caller captures its exit code and propagates it before reading `$include`: +# +# $impactExit = $LASTEXITCODE +# if ($impactExit -ne 0) { exit $impactExit } +# +# `$ErrorActionPreference = 'Stop'` does not fail a recipe on a native +# command's nonzero exit, so without that guard a failed lookup (a missing +# cache under ANVIL_IMPACT=consume, an unreadable include file, an +# unrecognized ANVIL_IMPACT value) would leave `$include` empty and the check +# would silently widen to its unscoped default and pass. The same reason +# anvil-impact fails loudly in consume mode applies to reading the cache it +# asserts: a broken scope must stop the check, not quietly rescope it. +# +# This keeps the per-recipe boilerplate to a single capture, the exit-code +# guard, a one-line skip guard, and the cargo invocation. Modified-tier +# recipes never forward `$include`; they only check the skip sentinel and then +# let their command determine its normal input domain. # # Every recipe whose body uses multi-line conditionals or array-splat # splicing is annotated with [script("pwsh", "-NoProfile")]. pwsh is preinstalled on diff --git a/crates/cargo-anvil/tests/snapshots/snapshots__local_only.snap b/crates/cargo-anvil/tests/snapshots/snapshots__local_only.snap index 27a43ff49..cf721233a 100644 --- a/crates/cargo-anvil/tests/snapshots/snapshots__local_only.snap +++ b/crates/cargo-anvil/tests/snapshots/snapshots__local_only.snap @@ -733,6 +733,8 @@ anvil-audit-validate-prereqs: anvil-tool-cargo-audit-validate-prereqs anvil-bench: anvil-bench-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-bench: no affected packages; skipping'; exit 0 } & cargo {{_anvil_stable_toolchain_args}} bench @(if ($include) { -split $include } else { '--workspace' }) --all-features --no-run if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } @@ -784,6 +786,8 @@ anvil-bench-validate-prereqs: anvil-tool-rustc-validate-prereqs anvil-bolero: anvil-bolero-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if (-not $IsLinux) { Write-Host 'anvil-bolero: non-Linux host -- skipping (cargo-bolero/libfuzzer is Linux-only; harnesses still run under anvil-llvm-cov)' exit 0 @@ -884,6 +888,8 @@ anvil-bolero-validate-prereqs: anvil-toolchain-nightly-validate-prereqs anvil-to anvil-careful: anvil-careful-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-careful: no affected packages; skipping'; exit 0 } $pkg = @(if ($include) { -split $include } else { '--workspace' }) # Guard against cargo-careful's stale-sysroot hazard. cargo-careful @@ -968,6 +974,8 @@ anvil-careful-validate-prereqs: anvil-component-nightly-rust-src-validate-prereq anvil-cargo-hack: anvil-cargo-hack-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include required) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-cargo-hack: no affected packages; skipping'; exit 0 } & cargo {{_anvil_stable_toolchain_args}} hack @(if ($include) { -split $include } else { '--workspace' }) --feature-powerset --depth 2 check if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } @@ -1003,6 +1011,8 @@ anvil-cargo-hack-validate-prereqs: anvil-tool-cargo-hack-validate-prereqs anvil-cargo-sort: anvil-cargo-sort-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include modified) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-cargo-sort: no modified packages; skipping'; exit 0 } & cargo {{_anvil_stable_toolchain_args}} sort --workspace --grouped --check --check-format if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } @@ -1036,6 +1046,8 @@ anvil-cargo-sort-validate-prereqs: anvil-tool-cargo-sort-validate-prereqs anvil-clippy: anvil-clippy-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-clippy: no affected packages; skipping'; exit 0 } & cargo {{_anvil_stable_toolchain_args}} clippy @(if ($include) { -split $include } else { '--workspace' }) --all-targets --all-features --locked "--" '-D' 'warnings' if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } @@ -1095,6 +1107,8 @@ anvil-deny-validate-prereqs: anvil-tool-cargo-deny-validate-prereqs anvil-doc-build open="false": anvil-doc-build-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include required) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-doc-build: no affected packages; skipping'; exit 0 } $env:RUSTDOCFLAGS = '-D warnings' $pkg = @(if ($include) { -split $include } else { '--workspace' }) @@ -1134,6 +1148,8 @@ anvil-doc-build-validate-prereqs: anvil-tool-rustc-validate-prereqs anvil-doc-test: anvil-doc-test-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-doc-test: no affected packages; skipping'; exit 0 } $pkg = @(if ($include) { -split $include } else { '--workspace' }) & cargo {{_anvil_stable_toolchain_args}} test --doc @pkg --all-features --locked @@ -1165,6 +1181,8 @@ anvil-doc-test-validate-prereqs: anvil-tool-rustc-validate-prereqs anvil-ensure-no-cyclic-deps: anvil-ensure-no-cyclic-deps-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include modified) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-ensure-no-cyclic-deps: no modified packages; skipping'; exit 0 } & cargo {{_anvil_stable_toolchain_args}} ensure-no-cyclic-deps if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } @@ -1193,6 +1211,8 @@ anvil-ensure-no-cyclic-deps-validate-prereqs: anvil-tool-cargo-ensure-no-cyclic- anvil-ensure-no-default-features: anvil-ensure-no-default-features-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include modified) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-ensure-no-default-features: no modified packages; skipping'; exit 0 } & cargo {{_anvil_stable_toolchain_args}} ensure-no-default-features if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } @@ -1234,6 +1254,8 @@ anvil-examples run="false" package="" example="" timeout="30": anvil-examples-va exit 2 } $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if (-not $package -and $include -eq '--skip') { Write-Host 'anvil-examples: no affected packages; skipping'; exit 0 } $pkgArgs = @(if ($package) { '--package'; $package } elseif ($include) { -split $include } else { '--workspace' }) $exampleArgs = @(if ($example) { '--example'; $example } else { '--examples' }) @@ -1367,6 +1389,8 @@ anvil-examples-validate-prereqs: anvil-tool-rustc-validate-prereqs anvil-external-types: anvil-external-types-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-external-types: no affected packages; skipping' exit 0 @@ -1466,6 +1490,8 @@ anvil-external-types-validate-prereqs: anvil-toolchain-nightly-external-types-va anvil-fmt fix="false": anvil-fmt-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include modified) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-fmt: no modified packages; skipping'; exit 0 } cargo '+{{ rust_nightly }}' each --workspace --keep-going '--' cargo '+{{ rust_nightly }}' fmt --manifest-path '{manifest}' {{ if fix == "true" { "" } else { "--check" } }} if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } @@ -1509,6 +1535,8 @@ anvil-fmt-validate-prereqs: anvil-component-nightly-rustfmt-validate-prereqs anv anvil-license-headers: anvil-license-headers-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include modified) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-license-headers: no modified packages; skipping'; exit 0 } & cargo {{_anvil_stable_toolchain_args}} heather if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } @@ -1542,6 +1570,8 @@ anvil-license-headers-validate-prereqs: anvil-tool-cargo-heather-validate-prereq anvil-llvm-cov: anvil-llvm-cov-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-llvm-cov: no affected packages; skipping' exit 0 @@ -1833,6 +1863,8 @@ anvil-llvm-cov-validate-prereqs: anvil-component-nightly-llvm-tools-validate-pre anvil-loom: anvil-loom-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-loom: no affected packages; skipping' exit 0 @@ -2085,7 +2117,8 @@ _anvil-miri-test profile package="" test_filter="" example="": anvil-impact } $include = (& "{{ just_executable() }}" _anvil-impact-include affected) - if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if (-not $package -and $include -eq '--skip') { Write-Host "$recipeName`: no affected packages; skipping" exit 0 @@ -2470,6 +2503,8 @@ anvil-msrv-test-validate-prereqs: anvil-mutants-diff: anvil-mutants-diff-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($IsWindows -and ($env:PROCESSOR_ARCHITECTURE -eq 'ARM64' -or $env:PROCESSOR_ARCHITEW6432 -eq 'ARM64')) { Write-Host 'anvil-mutants-diff: anvil-mutants-diff-validate-prereqs aarch64-pc-windows-msvc -- cargo-mutants does not build here (winapi); skipping' exit 0 @@ -2812,6 +2847,8 @@ anvil-semver-check: anvil-semver-check-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $stableArgs = {{_anvil_stable_toolchain_args}} $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } $commentFile = 'target/anvil/comments/semver.md' if ($include -eq '--skip') { Write-Host 'anvil-semver-check: no affected packages; skipping' @@ -3100,6 +3137,8 @@ anvil-spellcheck-validate-prereqs: anvil-tool-cargo-spellcheck-validate-prereqs anvil-udeps: anvil-udeps-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include required) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-udeps: no affected packages; skipping'; exit 0 } $pkg = @(if ($include) { -split $include } else { '--workspace' }) # Pass 1: default targets (lib + bins) -- surfaces [dependencies] that @@ -4823,10 +4862,24 @@ anvil-scheduled-test-validate-prereqs: \ # like-named crate is also a transitive dependency. # * "--skip" - the tier is empty for this change (recipe exits 0). # -# This keeps the per-recipe boilerplate to a single capture, a one-line skip -# guard, and the cargo invocation. Modified-tier recipes never forward -# `$include`; they only check the skip sentinel and then let their command -# determine its normal input domain. +# Those three values are only meaningful when the resolver succeeded, so every +# caller captures its exit code and propagates it before reading `$include`: +# +# $impactExit = $LASTEXITCODE +# if ($impactExit -ne 0) { exit $impactExit } +# +# `$ErrorActionPreference = 'Stop'` does not fail a recipe on a native +# command's nonzero exit, so without that guard a failed lookup (a missing +# cache under ANVIL_IMPACT=consume, an unreadable include file, an +# unrecognized ANVIL_IMPACT value) would leave `$include` empty and the check +# would silently widen to its unscoped default and pass. The same reason +# anvil-impact fails loudly in consume mode applies to reading the cache it +# asserts: a broken scope must stop the check, not quietly rescope it. +# +# This keeps the per-recipe boilerplate to a single capture, the exit-code +# guard, a one-line skip guard, and the cargo invocation. Modified-tier +# recipes never forward `$include`; they only check the skip sentinel and then +# let their command determine its normal input domain. # # Every recipe whose body uses multi-line conditionals or array-splat # splicing is annotated with [script("pwsh", "-NoProfile")]. pwsh is preinstalled on diff --git a/justfiles/anvil/checks/bench.just b/justfiles/anvil/checks/bench.just index b26880a81..074caf3cb 100644 --- a/justfiles/anvil/checks/bench.just +++ b/justfiles/anvil/checks/bench.just @@ -13,6 +13,8 @@ anvil-bench: anvil-bench-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-bench: no affected packages; skipping'; exit 0 } & cargo {{_anvil_stable_toolchain_args}} bench @(if ($include) { -split $include } else { '--workspace' }) --all-features --no-run if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } diff --git a/justfiles/anvil/checks/bolero.just b/justfiles/anvil/checks/bolero.just index 344fabdcc..e9590ac30 100644 --- a/justfiles/anvil/checks/bolero.just +++ b/justfiles/anvil/checks/bolero.just @@ -33,6 +33,8 @@ anvil-bolero: anvil-bolero-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if (-not $IsLinux) { Write-Host 'anvil-bolero: non-Linux host -- skipping (cargo-bolero/libfuzzer is Linux-only; harnesses still run under anvil-llvm-cov)' exit 0 diff --git a/justfiles/anvil/checks/careful.just b/justfiles/anvil/checks/careful.just index c937a5dc2..be342ffc2 100644 --- a/justfiles/anvil/checks/careful.just +++ b/justfiles/anvil/checks/careful.just @@ -11,6 +11,8 @@ anvil-careful: anvil-careful-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-careful: no affected packages; skipping'; exit 0 } $pkg = @(if ($include) { -split $include } else { '--workspace' }) # Guard against cargo-careful's stale-sysroot hazard. cargo-careful diff --git a/justfiles/anvil/checks/cargo-hack.just b/justfiles/anvil/checks/cargo-hack.just index 0377fb4da..994e80799 100644 --- a/justfiles/anvil/checks/cargo-hack.just +++ b/justfiles/anvil/checks/cargo-hack.just @@ -15,6 +15,8 @@ anvil-cargo-hack: anvil-cargo-hack-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include required) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-cargo-hack: no affected packages; skipping'; exit 0 } & cargo {{_anvil_stable_toolchain_args}} hack @(if ($include) { -split $include } else { '--workspace' }) --feature-powerset --depth 2 check if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } diff --git a/justfiles/anvil/checks/cargo-sort.just b/justfiles/anvil/checks/cargo-sort.just index 9744892f0..ac9053e0d 100644 --- a/justfiles/anvil/checks/cargo-sort.just +++ b/justfiles/anvil/checks/cargo-sort.just @@ -20,6 +20,8 @@ anvil-cargo-sort: anvil-cargo-sort-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include modified) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-cargo-sort: no modified packages; skipping'; exit 0 } & cargo {{_anvil_stable_toolchain_args}} sort --workspace --grouped --check --check-format if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } diff --git a/justfiles/anvil/checks/clippy.just b/justfiles/anvil/checks/clippy.just index a0354f2c4..223f0750c 100644 --- a/justfiles/anvil/checks/clippy.just +++ b/justfiles/anvil/checks/clippy.just @@ -18,6 +18,8 @@ anvil-clippy: anvil-clippy-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-clippy: no affected packages; skipping'; exit 0 } & cargo {{_anvil_stable_toolchain_args}} clippy @(if ($include) { -split $include } else { '--workspace' }) --all-targets --all-features --locked "--" '-D' 'warnings' if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } diff --git a/justfiles/anvil/checks/doc-build.just b/justfiles/anvil/checks/doc-build.just index ab0095978..117179966 100644 --- a/justfiles/anvil/checks/doc-build.just +++ b/justfiles/anvil/checks/doc-build.just @@ -16,6 +16,8 @@ anvil-doc-build open="false": anvil-doc-build-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include required) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-doc-build: no affected packages; skipping'; exit 0 } $env:RUSTDOCFLAGS = '-D warnings' $pkg = @(if ($include) { -split $include } else { '--workspace' }) diff --git a/justfiles/anvil/checks/doc-test.just b/justfiles/anvil/checks/doc-test.just index ca6ade8c9..190e8b8d5 100644 --- a/justfiles/anvil/checks/doc-test.just +++ b/justfiles/anvil/checks/doc-test.just @@ -18,6 +18,8 @@ anvil-doc-test: anvil-doc-test-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-doc-test: no affected packages; skipping'; exit 0 } $pkg = @(if ($include) { -split $include } else { '--workspace' }) & cargo {{_anvil_stable_toolchain_args}} test --doc @pkg --all-features --locked diff --git a/justfiles/anvil/checks/ensure-no-cyclic-deps.just b/justfiles/anvil/checks/ensure-no-cyclic-deps.just index 57f360170..34bac20b0 100644 --- a/justfiles/anvil/checks/ensure-no-cyclic-deps.just +++ b/justfiles/anvil/checks/ensure-no-cyclic-deps.just @@ -13,6 +13,8 @@ anvil-ensure-no-cyclic-deps: anvil-ensure-no-cyclic-deps-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include modified) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-ensure-no-cyclic-deps: no modified packages; skipping'; exit 0 } & cargo {{_anvil_stable_toolchain_args}} ensure-no-cyclic-deps if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } diff --git a/justfiles/anvil/checks/ensure-no-default-features.just b/justfiles/anvil/checks/ensure-no-default-features.just index cee4d24d5..0b2bddace 100644 --- a/justfiles/anvil/checks/ensure-no-default-features.just +++ b/justfiles/anvil/checks/ensure-no-default-features.just @@ -13,6 +13,8 @@ anvil-ensure-no-default-features: anvil-ensure-no-default-features-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include modified) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-ensure-no-default-features: no modified packages; skipping'; exit 0 } & cargo {{_anvil_stable_toolchain_args}} ensure-no-default-features if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } diff --git a/justfiles/anvil/checks/examples.just b/justfiles/anvil/checks/examples.just index efdbe6a91..b38d87733 100644 --- a/justfiles/anvil/checks/examples.just +++ b/justfiles/anvil/checks/examples.just @@ -26,6 +26,8 @@ anvil-examples run="false" package="" example="" timeout="30": anvil-examples-va exit 2 } $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if (-not $package -and $include -eq '--skip') { Write-Host 'anvil-examples: no affected packages; skipping'; exit 0 } $pkgArgs = @(if ($package) { '--package'; $package } elseif ($include) { -split $include } else { '--workspace' }) $exampleArgs = @(if ($example) { '--example'; $example } else { '--examples' }) diff --git a/justfiles/anvil/checks/external-types.just b/justfiles/anvil/checks/external-types.just index d659a3d4d..f25f11ff2 100644 --- a/justfiles/anvil/checks/external-types.just +++ b/justfiles/anvil/checks/external-types.just @@ -24,6 +24,8 @@ anvil-external-types: anvil-external-types-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-external-types: no affected packages; skipping' exit 0 diff --git a/justfiles/anvil/checks/fmt.just b/justfiles/anvil/checks/fmt.just index 299cb33ac..e4eb7ff41 100644 --- a/justfiles/anvil/checks/fmt.just +++ b/justfiles/anvil/checks/fmt.just @@ -26,6 +26,8 @@ anvil-fmt fix="false": anvil-fmt-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include modified) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-fmt: no modified packages; skipping'; exit 0 } cargo '+{{ rust_nightly }}' each --workspace --keep-going '--' cargo '+{{ rust_nightly }}' fmt --manifest-path '{manifest}' {{ if fix == "true" { "" } else { "--check" } }} if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } diff --git a/justfiles/anvil/checks/license-headers.just b/justfiles/anvil/checks/license-headers.just index fb794c32e..7839b444b 100644 --- a/justfiles/anvil/checks/license-headers.just +++ b/justfiles/anvil/checks/license-headers.just @@ -13,6 +13,8 @@ anvil-license-headers: anvil-license-headers-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include modified) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-license-headers: no modified packages; skipping'; exit 0 } & cargo {{_anvil_stable_toolchain_args}} heather if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } diff --git a/justfiles/anvil/checks/llvm-cov.just b/justfiles/anvil/checks/llvm-cov.just index a4531ecb3..5b75bdb0a 100644 --- a/justfiles/anvil/checks/llvm-cov.just +++ b/justfiles/anvil/checks/llvm-cov.just @@ -18,6 +18,8 @@ anvil-llvm-cov: anvil-llvm-cov-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-llvm-cov: no affected packages; skipping' exit 0 diff --git a/justfiles/anvil/checks/loom.just b/justfiles/anvil/checks/loom.just index 4703a71f3..8acc6994e 100644 --- a/justfiles/anvil/checks/loom.just +++ b/justfiles/anvil/checks/loom.just @@ -53,6 +53,8 @@ anvil-loom: anvil-loom-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-loom: no affected packages; skipping' exit 0 diff --git a/justfiles/anvil/checks/miri.just b/justfiles/anvil/checks/miri.just index 72090a0c2..5ec10f934 100644 --- a/justfiles/anvil/checks/miri.just +++ b/justfiles/anvil/checks/miri.just @@ -90,7 +90,8 @@ _anvil-miri-test profile package="" test_filter="" example="": anvil-impact } $include = (& "{{ just_executable() }}" _anvil-impact-include affected) - if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if (-not $package -and $include -eq '--skip') { Write-Host "$recipeName`: no affected packages; skipping" exit 0 diff --git a/justfiles/anvil/checks/mutants-diff.just b/justfiles/anvil/checks/mutants-diff.just index e4efc812f..f4d6d31e0 100644 --- a/justfiles/anvil/checks/mutants-diff.just +++ b/justfiles/anvil/checks/mutants-diff.just @@ -21,6 +21,8 @@ anvil-mutants-diff: anvil-mutants-diff-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($IsWindows -and ($env:PROCESSOR_ARCHITECTURE -eq 'ARM64' -or $env:PROCESSOR_ARCHITEW6432 -eq 'ARM64')) { Write-Host 'anvil-mutants-diff: anvil-mutants-diff-validate-prereqs aarch64-pc-windows-msvc -- cargo-mutants does not build here (winapi); skipping' exit 0 diff --git a/justfiles/anvil/checks/semver-check.just b/justfiles/anvil/checks/semver-check.just index 5dbd16128..deb72eca2 100644 --- a/justfiles/anvil/checks/semver-check.just +++ b/justfiles/anvil/checks/semver-check.just @@ -51,6 +51,8 @@ anvil-semver-check: anvil-semver-check-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $stableArgs = {{_anvil_stable_toolchain_args}} $include = (& "{{ just_executable() }}" _anvil-impact-include affected) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } $commentFile = 'target/anvil/comments/semver.md' if ($include -eq '--skip') { Write-Host 'anvil-semver-check: no affected packages; skipping' diff --git a/justfiles/anvil/checks/udeps.just b/justfiles/anvil/checks/udeps.just index 83770a4c9..a94ff77ac 100644 --- a/justfiles/anvil/checks/udeps.just +++ b/justfiles/anvil/checks/udeps.just @@ -29,6 +29,8 @@ anvil-udeps: anvil-udeps-validate-prereqs anvil-impact $ErrorActionPreference = 'Stop' $include = (& "{{ just_executable() }}" _anvil-impact-include required) + $impactExit = $LASTEXITCODE + if ($impactExit -ne 0) { exit $impactExit } if ($include -eq '--skip') { Write-Host 'anvil-udeps: no affected packages; skipping'; exit 0 } $pkg = @(if ($include) { -split $include } else { '--workspace' }) # Pass 1: default targets (lib + bins) -- surfaces [dependencies] that diff --git a/justfiles/anvil/helpers.just b/justfiles/anvil/helpers.just index ebfbc07dc..1a1f6b0cd 100644 --- a/justfiles/anvil/helpers.just +++ b/justfiles/anvil/helpers.just @@ -57,10 +57,24 @@ # like-named crate is also a transitive dependency. # * "--skip" - the tier is empty for this change (recipe exits 0). # -# This keeps the per-recipe boilerplate to a single capture, a one-line skip -# guard, and the cargo invocation. Modified-tier recipes never forward -# `$include`; they only check the skip sentinel and then let their command -# determine its normal input domain. +# Those three values are only meaningful when the resolver succeeded, so every +# caller captures its exit code and propagates it before reading `$include`: +# +# $impactExit = $LASTEXITCODE +# if ($impactExit -ne 0) { exit $impactExit } +# +# `$ErrorActionPreference = 'Stop'` does not fail a recipe on a native +# command's nonzero exit, so without that guard a failed lookup (a missing +# cache under ANVIL_IMPACT=consume, an unreadable include file, an +# unrecognized ANVIL_IMPACT value) would leave `$include` empty and the check +# would silently widen to its unscoped default and pass. The same reason +# anvil-impact fails loudly in consume mode applies to reading the cache it +# asserts: a broken scope must stop the check, not quietly rescope it. +# +# This keeps the per-recipe boilerplate to a single capture, the exit-code +# guard, a one-line skip guard, and the cargo invocation. Modified-tier +# recipes never forward `$include`; they only check the skip sentinel and then +# let their command determine its normal input domain. # # Every recipe whose body uses multi-line conditionals or array-splat # splicing is annotated with [script("pwsh", "-NoProfile")]. pwsh is preinstalled on