Skip to content

fix(cargo-anvil): make anvil-fmt actually run the pinned nightly rustfmt - #159

Open
Evgenii (Vaiz) wants to merge 1 commit into
mainfrom
u/vaiz/2026/09/04/anvil-fmt-nightly
Open

fix(cargo-anvil): make anvil-fmt actually run the pinned nightly rustfmt#159
Evgenii (Vaiz) wants to merge 1 commit into
mainfrom
u/vaiz/2026/09/04/anvil-fmt-nightly

Conversation

@Vaiz

@Vaiz Evgenii (Vaiz) commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

🤖 Clawpilot here! Posted automatically by Clawpilot (an AI agent), not by a human. Please verify before acting.

The bug

anvil-fmt was a green check that enforced nothing.

cargo fmt only dispatches: cargo-fmt runs rustfmt as a child process through the rustup shim, and that shim reads RUSTUP_TOOLCHAIN — which rustup exports into every child of a +-selected cargo and does not rewrite when a nested cargo carries its own +. So in

cargo {{_anvil_stable_toolchain_args}} each ... '--' cargo '+{{ rust_nightly }}' fmt ... --check

the inner +nightly selected a nightly cargo, but the rustfmt underneath it still resolved through the outer stable selection. Stable rustfmt does not fail on unstable options — it downgrades each to a warning and exits 0, so every unstable rustfmt.toml rule silently stopped being enforced while the check kept passing.

An ambient RUSTUP_TOOLCHAIN (which anvil's own cloud setup exports) breaks it the same way, so dropping the stable pin alone is not a fix.

The fix

-    cargo {{_anvil_stable_toolchain_args}} each --workspace --keep-going '--' cargo '+{{ rust_nightly }}' fmt --manifest-path '{manifest}' --check
+    $env:RUSTUP_TOOLCHAIN = '{{ rust_nightly }}'
+    cargo each --workspace --keep-going '--' cargo fmt --manifest-path '{manifest}' --check

RUSTUP_TOOLCHAIN covers every descendant, which is what a dispatcher needs; the assignment is scoped to this recipe's process. The wrapper loses its stable pin deliberately — that pin is the thing that leaks, and cargo each only enumerates members, it compiles nothing.

anvil-fmt was the only recipe nesting cargos this way. udeps, miri, careful, external-types, bolero and llvm-cov are cargo subcommands dispatched by the already-selected nightly cargo, so they resolve correctly even under a hostile ambient RUSTUP_TOOLCHAIN; rustfmt is the outlier because it is a separate rustup component re-resolved from PATH.

Tests

fmt_runs_the_pinned_nightly_over_workspace_members (the existing delegation test, extended) hands the recipe a hostile ambient RUSTUP_TOOLCHAIN=test-stable and asserts every cargo it spawns still sees the pinned nightly. Verified load-bearing against both broken forms: the original recipe, and the partial fix that only removes the stable pin.

checks_do_not_invoke_an_implicit_default_cargo now accepts an assignment-scoped $env:RUSTUP_TOOLCHAIN = '{{ … }}' as an explicit selection, tracked per recipe and only when it interpolates a catalog variable.

Verification

  • cargo run -p cargo-anvil -- anvil, snapshots regenerated, cargo fmt --check / clippy -D warnings / cargo test -p cargo-anvil all green.
  • End-to-end in microsoft/oxidizer: just anvil-fmt went from 2306 unstable-feature warnings (exit 0) to zero.

Downstream

microsoft/oxidizer#728 carries a repository-customized fmt.just solely because of this bug; once oxidizer picks up the new cargo-anvil, that customization can be dropped.

@codecov-commenter

Codecov Comments Bot (codecov-commenter) commented Sep 4, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.5%. Comparing base (f3c65bb) to head (4ca357a).

❌ Your project status has failed because the head coverage (97.5%) is below the target coverage (100.0%). You can increase the head coverage or adjust the target coverage.

Additional details and impacted files
@@          Coverage Diff          @@
##            main    #159   +/-   ##
=====================================
  Coverage   97.5%   97.5%           
=====================================
  Files        299     299           
  Lines      67766   67766           
=====================================
+ Hits       66127   66132    +5     
+ Misses      1639    1634    -5     
Flag Coverage Δ
linux 97.5% <ø> (?)
linux-arm 97.5% <ø> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The change corrects toolchain selection for cargo fmt with a targeted regression test and consistent regenerated artifacts, with no remaining issues found in the reviewed diffs.

Pull request overview

This PR fixes anvil-fmt so it consistently runs the pinned nightly rustfmt by selecting the nightly toolchain via RUSTUP_TOOLCHAIN, ensuring unstable rustfmt.toml options are actually enforced even when an ambient/stable toolchain selection would otherwise leak into cargo fmt.

Changes:

  • Update the anvil-fmt check to set $env:RUSTUP_TOOLCHAIN to the pinned nightly and run cargo fmt without relying on nested +toolchain.
  • Add a regression test that simulates a hostile inherited RUSTUP_TOOLCHAIN and asserts the pinned nightly selection is still used for formatting-related cargo invocations.
  • Extend the “no implicit default cargo” invariant to treat a per-recipe RUSTUP_TOOLCHAIN pin as an explicit toolchain selection.
File summaries
File Description
justfiles/anvil/checks/fmt.just Pins nightly via RUSTUP_TOOLCHAIN and runs cargo each … cargo fmt … so rustfmt resolves under the intended toolchain.
crates/cargo-anvil/templates/justfiles/anvil/checks/fmt.just Template source updated to generate the fixed anvil-fmt recipe.
crates/cargo-anvil/tests/recipe_contracts.rs Adds/updates tests asserting correct cargo fmt invocation and toolchain pinning behavior under inherited toolchain env.
crates/cargo-anvil/src/anvil/artifacts/justfile.rs Updates invariant checking to allow explicit toolchain selection via per-recipe $env:RUSTUP_TOOLCHAIN pinning.
crates/cargo-anvil/tests/snapshots/snapshots__local_only.snap Snapshot update reflecting the generated fmt.just changes.
crates/cargo-anvil/tests/snapshots/snapshots__github_backend.snap Snapshot update reflecting the generated fmt.just changes.
crates/cargo-anvil/tests/snapshots/snapshots__ado_backend.snap Snapshot update reflecting the generated fmt.just changes.
.anvil.lock Updates checksums to match the regenerated artifacts/templates.
Review details
  • Files reviewed: 7/8 changed files
  • Comments generated: 0
  • Review effort level: Lite

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@Vaiz
Evgenii (Vaiz) force-pushed the u/vaiz/2026/09/04/anvil-fmt-nightly branch from 25ce18f to 966f52c Compare September 4, 2026 11:06
@Vaiz
Evgenii (Vaiz) marked this pull request as ready for review September 4, 2026 11:33
Copilot AI review requested due to automatic review settings September 4, 2026 11:33

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The change directly addresses the described toolchain-leak failure mode and adds targeted contract coverage to prevent regressions under hostile ambient RUSTUP_TOOLCHAIN.

Review details
  • Files reviewed: 7/8 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

`cargo fmt` only dispatches: cargo-fmt runs `rustfmt` as a child process
through the rustup shim, and that shim reads `RUSTUP_TOOLCHAIN`, which
rustup exports into every child of a `+`-selected cargo and does not
rewrite when a nested cargo carries its own `+`. So the inner
`+{{ rust_nightly }}` selected a nightly cargo, but the `rustfmt`
underneath it still resolved through the outer stable selection. Stable
rustfmt does not fail on unstable options -- it downgrades each to a
warning and exits 0, so every unstable `rustfmt.toml` rule silently
stopped being enforced while the check kept passing.

Set `RUSTUP_TOOLCHAIN` for the recipe instead, which covers every
descendant. The wrapper loses its stable pin deliberately: that pin is
the thing that leaks, and `cargo each` only enumerates members, it
compiles nothing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings September 4, 2026 14:17
@Vaiz
Evgenii (Vaiz) force-pushed the u/vaiz/2026/09/04/anvil-fmt-nightly branch from 966f52c to 4ca357a Compare September 4, 2026 14:17

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The change is narrowly scoped, matches the stated failure mode, and includes targeted tests to prevent regressions under hostile RUSTUP_TOOLCHAIN environments.

Review details
  • Files reviewed: 7/8 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants