Skip to content

ci(anvil): update to cargo-anvil 0.6.0 to fix semver-check - #718

Draft
Evgenii (Vaiz) wants to merge 5 commits into
mainfrom
u/vaiz/2026/09/01/anvil-0-5-0
Draft

ci(anvil): update to cargo-anvil 0.6.0 to fix semver-check#718
Evgenii (Vaiz) wants to merge 5 commits into
mainfrom
u/vaiz/2026/09/01/anvil-0-5-0

Conversation

@Vaiz

@Vaiz Evgenii (Vaiz) commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

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

Fixes AB#7802687.

Problem

anvil-semver-check has been non-functional repo-wide. Every library crate aborts with:

error: unsupported rustdoc format v60 for file: .../target/doc/anyspawn_azure.json
(supported formats are v55, v56, v57)

The recipe installs the pin from justfiles/anvil/versions.just, which was cargo-semver-checks 0.46.0 (rustdoc JSON v55/v56/v57). The recipe does not pin a toolchain for doc building, so it uses the runner default stable — now Rust 1.98, whose rustdoc emits v60. The tool aborts while parsing the JSON it just produced.

Because the recipe is advisory, the crash was never surfaced as a failure. Each tool error was collected as a finding and posted as a sticky ":warning: Potential breaking changes detected" comment listing ~21 crates. Net effect: SemVer checking has been off across the workspace since the runner rolled to 1.98, and every PR carried a misleading breaking-change warning. Last observed on #715 (2026-08-31), also on #712 and #704.

#709 fixed this by editing the pin directly and was closed unmerged, because versions.just is anvil-generated and a repository-local edit stops it receiving automatic updates. This PR takes the sanctioned route instead.

Change

Regenerate the anvil-managed tree with cargo anvil from cargo-anvil 0.6.0 (2026-09-02; previously 0.3.0, #596, 2026-07-24). The 0.6.0 catalog pins cargo-semver-checks 0.50.0, which reads rustdoc JSON v57, v60 and v61.

constants.env is not anvil-managed and is bumped by hand from 0.48.0 to 0.50.0, so the main.yml semver job and scripts/release-packages.ps1 install the same version the recipes do. That path pins RUST_LATEST=1.96.1, which emits v57, so it was never broken — but 0.48.0 reads only v55/v56/v57, so a developer running release-packages.ps1 on current stable would hit the identical error locally.

Compatibility

rustdoc JSON format_version against the toolchains in play:

toolchain rustdoc JSON read by 0.50.0
1.95 (RUST_MSRV) v57 yes
1.96.1 (RUST_LATEST) v57 yes
1.98 (runner stable) v60 yes
nightly-2026-05-30 v57 yes

0.50.0 drops v55 and v56, both of which correspond to a rustc below the MSRV. Its install MSRV is Rust 1.93, below this repo's 1.95.

What else the 0.6.0 catalog brings

  • Impact analysis moves to an artifact. The impact-linux / impact-windows jobs no longer export include_modified / include_affected / include_required as job outputs threaded into every group. They upload an anvil-impact-<os> artifact; each group job downloads it into target/anvil/impact and passes impact_mode: consume, so scoped checks read the same cache a local run reads. The impact jobs also drop lfs: true — they only touch paths and dependency metadata. The Codecov upload now gates on hashFiles(...) instead of a job output.
  • New MSRV check and group. justfiles/anvil/checks/msrv-test.just plus a pr-msrv group, wired into anvil-pr-impl.yml across the four legs.
  • Container artifacts regenerated. The .anvil/container script set (Containerfile, entrypoint.sh, image-id.*, run-in-container.*, README.md) is replaced by a single generated Dockerfile + .dockerignore. justfiles/anvil/runner.just and the anvil-runner region in the root justfile are removed, and container.just becomes an import? so the documented without_artifact opt-out cannot break Justfile parsing.
  • Tool pins bumped: cargo-deny 0.19.8 → 0.20.2, cargo-doc2readme 0.7.2 → 0.7.3, cargo-llvm-cov 0.8.7 → 0.9.0, cargo-mutants 27.0.0 → 27.1.0, cargo-nextest 0.9.137 → 0.9.143, plus a new cargo-each pin.
  • Exit-code split retained from 0.5.0. semver-check.just still separates exit 100 (completed check, deny-level findings) from exit 101 (check could not complete), rendering "Potential breaking changes" and "Inconclusive comparisons" under separate headings — so a broken tool no longer presents as breaking-change output. That was the third follow-up on fix(ci): bump cargo-semver-checks to 0.50.0 for rustdoc JSON v60 #709 and the third item on the work item.

Two things that needed manual resolution

Duplicate [lints] table. cargo-anvil appends an anvil-lints managed region without detecting an existing unmanaged [lints] table. 20 crates had one, so regeneration produced two [lints] tables in each and cargo rejected the workspace outright. The pre-existing block is removed in favour of the managed region; both are workspace = true, so this is not a lint behaviour change. Worth reporting upstream against microsoft/ox-tools.

anvil-delta region. The managed region no longer carries [delta] root-files = ["Cargo.lock", "Cargo.toml", "rust-toolchain.toml"], and cargo-anvil reports that it left the region empty because this repo defines top-level trip_wire_patterns. Those patterns already list all three files, so impact scoping is unchanged.

Not adopted

Six recipes carry deliberate repository-specific edits: fmt, bolero, and the four miri recipes — including miri's ~200-line parallel artifact runner (_anvil-miri-test, #706). cargo-anvil declined to overwrite them and wrote .anvil-proposed sidecars, which are not committed. They keep the [script("pwsh", "-NoProfile")] interpreter flag cherry-picked from the 0.5.0 sidecars, so all 53 script blocks in the tree are consistent and none load the invoking user's PowerShell profile.

The proposed bolero.just still rewrites target discovery — per-package cargo bolero list calls, discovery failures raised as errors rather than swallowed as "no targets", and --release profiles. Those look substantive rather than cosmetic and should be decided by someone who knows the bolero setup, not inside a CI-infra PR.

Verification

  • cargo anvil --dry-run after the change reports 133 unchanged items and zero pending writes, so the tree is fully in sync with the 0.6.0 catalog.
  • cargo metadata --no-deps --locked succeeds.
  • just --list parses the regenerated recipe tree.
  • Rebased onto main at c6addf0, which picked up the new thread_aware_core crate; it already carries the managed lints region and needed no change.
  • No .rs files are touched, so formatting and lint output cannot change.

The workflow restructure — the impact artifact hand-off and the new pr-msrv group — can only really be validated by CI on this PR.


CI status notes (appended automatically by Clawpilot, an AI agent — please verify)

Fixed: anvil-readme-check on all four Fast Checks runners

This was not a cargo-anvil defect. The recipe reported error: recipe _anvil-readme failed with exit code 1 immediately after an INFO: Readme is up to date line, which looked like a success message paired with a non-zero exit, but the recipe iterates every eligible crate and exits 1 at the end if any crate failed. The "up to date" line was simply the last crate in the loop. Three earlier crates had genuinely failed:

Crate Error
seatbelt Readme links to incompatible version of dependency 'tower-service'
http_extensions Readme links to incompatible version of dependency 'http-body'
rest_over_grpc Readme links to incompatible version of dependency 'tower-service'

Root cause: cargo-anvil 0.6.0 bumps the pinned cargo-doc2readme from 0.7.2 to 0.7.3, and 0.7.3 builds docs.rs URLs from the dependency's package name rather than its lib name. The three crates above are the only ones in the workspace that link a dependency whose package name and lib name differ, so only their checked-in READMEs went stale. Regenerating them with the pinned 0.7.3 and the workspace crates/README.j2 template is the fix; the diff is limited to those docs.rs URLs and the embedded dependency-info blob. just anvil-readme-check now passes over the whole workspace locally.

Open, upstream: anvil-msrv-test runs benchmark harnesses

PR Job / Check Group: MSRV Tests fails on linux and linux-arm with:

--> Error in ae_basic_operations_cg: Failed to run benchmarks: No such file or directory (os error 2).
Is gungraun-runner installed and gungraun-runner in your $PATH?
error: test failed, to rerun pass `-p arty_executor --bench ae_basic_operations_cg`
error: recipe `anvil-msrv-test` failed with exit code 1

justfiles/anvil/checks/msrv-test.just and justfiles/anvil/groups/pr-msrv.just are both added by this PR — the MSRV group is new in cargo-anvil 0.6.0. The generated recipe runs cargo +$msrv test @pkg --all-targets --all-features --locked, and --all-targets includes --benches, so cargo test builds and executes every bench target. This workspace has Callgrind benches built with harness = false that hand their work to a separate gungraun-runner binary, which the anvil-pr-msrv-setup dependency chain does not install (it installs only cargo-delta and the MSRV toolchain). No other anvil check hits this, because the repository's actual test path is anvil-llvm-cov, which uses cargo nextest run and therefore does not execute bench harnesses.

This looks like an upstream cargo-anvil defect rather than something to patch here. An MSRV check exists to prove the workspace still compiles and its tests pass under the declared minimum toolchain; executing benchmark harnesses is not part of that, and it drags in profiler prerequisites (here gungraun-runner plus Valgrind) that the group never declares. Suggested upstream fix, in preference order:

  1. Drop bench targets from the MSRV run — use explicit --lib --bins --tests instead of --all-targets.
  2. Or keep --all-targets but add --no-run for the bench portion, so bench targets are compiled (which is the MSRV-relevant signal) but never executed.

Deliberately not doing here, per this PR's scope: adding a gungraun-runner install to the generated pr-msrv setup, or excluding arty_executor locally. Both would be repository-specific edits to cargo-anvil's generated output, which is the failure mode this adoption is meant to avoid.

Note that this PR is an unusually harsh test of the new group: it touches every crate's Cargo.toml to introduce the anvil-managed [lints] region, so impact analysis marks the entire workspace affected and the MSRV group runs everything, benches included. On an ordinary PR the failure would appear only when a bench-carrying crate is in the affected set.

Open: six customized recipes lost their impact scoping

ANVIL_INCLUDE_AFFECTED / ANVIL_INCLUDE_MODIFIED are no longer exported by anything under .github/**, but six repository-customized recipes still read them: miri.just, miri-tree-borrows.just, miri-strict-provenance.just, miri-race-coverage.just, bolero.just and fmt.just. Those recipes therefore fall back to --workspace, and anvil-fmt never sees its --skip short-circuit. cargo-anvil 0.6.0 detected the drift and wrote a .anvil-proposed sibling for each. Adopting those proposals is the correct resolution, but it is not mechanical — the customized miri.just is 256 lines against a 57-line proposal — so it needs a human decision about which repository-specific logic moves upstream. Tracked in the discussion thread on this PR.

Follow-on: constants.env had to follow anvil's cargo-doc2readme pin

Regenerating the READMEs in ebf327ac turned anvil-readme-check green on all four Fast Checks runners but broke static-analysis on all four runners of the pre-anvil main workflow, with the mirror image of the original error:

ERROR: Readme links to incompatible version of dependency `tower_service'

Note the underscore. .github/workflows/main.yml installs cargo-doc2readme from CARGO_DOC2README_VERSION in constants.env, which still said 0.7.2, while justfiles/anvil/versions.just pins 0.7.3. The two workflows were therefore checking the same checked-in READMEs with two generators that disagree about whether a docs.rs URL uses the dependency's lib name (tower_service, 0.7.2) or its package name (tower-service, 0.7.3). Only one of the two can be right for a single file, and it has to be the version anvil pins.

Fixed in b6fff7ea by bumping CARGO_DOC2README_VERSION to 0.7.3. This is the same move this PR already makes for CARGO_SEMVER_CHECKS_VERSION: when anvil bumps a tool pin, the hand-written constants file has to follow so the non-anvil consumers stay in step.

For the record, constants.env and justfiles/anvil/versions.just still disagree on five other tools — cargo-deny (0.19.8 vs 0.20.2), cargo-llvm-cov (0.8.7 vs 0.9.0), cargo-mutants (27.0.0 vs 27.1.0), cargo-nextest (0.9.137 vs 0.9.143) and cargo-spellcheck (0.15.1 vs 0.15.7). Nothing currently fails on any of them, so they are deliberately left alone here rather than bumped speculatively; they are worth reconciling when the pre-anvil main workflow is retired.

Copilot AI lite review requested due to automatic review settings September 1, 2026 09:57

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Updates the repository’s Anvil-managed CI/automation surface by regenerating workflows, composite actions, and just recipes using cargo-anvil 0.5.0, aligning tool pins (notably cargo-semver-checks) to restore functional semver checking on current stable Rust toolchains and improve reporting for inconclusive comparisons.

Changes:

  • Regenerated Anvil-managed justfiles/anvil/** recipes (tiers, tools, checks) and bumped catalog tool pins (including cargo-semver-checks 0.49.0).
  • Restructured GitHub Actions wiring to use a shared anvil-run-group action + supplemental status reporting, and updated workflow/job permission boundaries.
  • Added a managed local container runner (.anvil/container/**) and ANVIL_RUNNER routing for tier entry points; normalized Cargo.toml [lints] blocks into managed regions.

Reviewed changes

Copilot reviewed 95 out of 96 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
justfiles/anvil/versions.just Updates Anvil tool version pins (incl. cargo-semver-checks).
justfiles/anvil/tools.just Updates installer/prereq logic (pwsh -NoProfile, source prereqs).
justfiles/anvil/tiers.just Routes tier entrypoints through the runner dispatcher.
justfiles/anvil/runner.just Adds runner dispatch (native vs container) for tier commands.
justfiles/anvil/mod.just Imports new container/runner entrypoints into Anvil tree.
justfiles/anvil/helpers.just Hardens impact formatting/metadata parsing and error handling.
justfiles/anvil/groups/scheduled-test.just Regen header/structure for scheduled test group.
justfiles/anvil/groups/scheduled-runtime-analysis.just Regen header/structure for scheduled runtime-analysis group.
justfiles/anvil/groups/scheduled-exhaustive.just Regen header/structure for scheduled exhaustive group.
justfiles/anvil/groups/scheduled-advisories.just Regen header/structure for scheduled advisories group.
justfiles/anvil/groups/pr-test.just Regen header/structure for PR test group.
justfiles/anvil/groups/pr-slow.just Regen header/structure for PR slow group.
justfiles/anvil/groups/pr-runtime-analysis.just Regen header/structure for PR runtime-analysis group.
justfiles/anvil/groups/pr-mutants.just Regen header/structure for PR mutants group.
justfiles/anvil/groups/pr-fast.just Adjusts pr-fast membership (drops anvil-aprz from fast group).
justfiles/anvil/container.just Adds anvil-container recipe entrypoint for host/container runs.
justfiles/anvil/checks/udeps.just Updates script shell flags and regen header.
justfiles/anvil/checks/spellcheck.just Updates spellcheck setup flow (defers source deps check to installer).
justfiles/anvil/checks/semver-check.just Uses new cargo-semver-checks exit-code semantics and improved reporting.
justfiles/anvil/checks/readme-check.just Hardened metadata parsing and impact package validation.
justfiles/anvil/checks/pr-title.just Expands title validation diagnostics; treats empty PR_TITLE as skip.
justfiles/anvil/checks/mutants-full.just Adds explicit Windows ARM skip behavior for mutants-full.
justfiles/anvil/checks/mutants-diff.just Updates script shell flags and regen header.
justfiles/anvil/checks/loom.just Updates script shell flags and regen header.
justfiles/anvil/checks/llvm-cov.just Refines coverage opt-out handling and windows-arm behavior.
justfiles/anvil/checks/license-headers.just Updates script shell flags and regen header.
justfiles/anvil/checks/external-types.just Hardened metadata parsing; improves failure diagnostics.
justfiles/anvil/checks/examples.just Updates script shell flags and regen header.
justfiles/anvil/checks/ensure-no-default-features.just Updates script shell flags and regen header.
justfiles/anvil/checks/ensure-no-cyclic-deps.just Updates script shell flags and regen header.
justfiles/anvil/checks/doc-test.just Updates script shell flags and regen header.
justfiles/anvil/checks/doc-build.just Updates script shell flags and regen header.
justfiles/anvil/checks/deny.just Updates script shell flags and regen header.
justfiles/anvil/checks/clippy.just Updates script shell flags and regen header.
justfiles/anvil/checks/cargo-sort.just Updates script shell flags and clarifies formatting rationale.
justfiles/anvil/checks/cargo-hack.just Updates script shell flags and regen header.
justfiles/anvil/checks/careful.just Changes sysroot identity tracking to hash the resolved executable.
justfiles/anvil/checks/bench.just Updates script shell flags and comment wording.
justfiles/anvil/checks/audit.just Updates script shell flags and regen header.
justfiles/anvil/checks/aprz.just Updates token acquisition behavior (container-aware) and adds re-run guard.
justfile Adds anvil_runner variable for tier routing (ANVIL_RUNNER).
crates/routerama/Cargo.toml Moves [lints] into an Anvil-managed region.
crates/routerama_macros/Cargo.toml Marks [lints] as Anvil-managed.
crates/routerama_build/Cargo.toml Moves [lints] into an Anvil-managed region.
crates/rest_over_grpc/Cargo.toml Marks [lints] as Anvil-managed.
crates/rest_over_grpc_tests/Cargo.toml Moves [lints] into an Anvil-managed region.
crates/rest_over_grpc_examples/Cargo.toml Moves [lints] into an Anvil-managed region.
crates/rallocator/Cargo.toml Moves [lints] into an Anvil-managed region.
crates/rallocator_wire/Cargo.toml Marks [lints] as Anvil-managed.
crates/rallocator_telemetry/Cargo.toml Marks [lints] as Anvil-managed.
crates/rallocator_cli/Cargo.toml Marks [lints] as Anvil-managed.
crates/observed/Cargo.toml Moves [lints] into an Anvil-managed region.
crates/observed_utils/Cargo.toml Marks [lints] as Anvil-managed.
crates/observed_testing/Cargo.toml Marks [lints] as Anvil-managed.
crates/observed_macros/Cargo.toml Marks [lints] as Anvil-managed.
crates/observed_macros_impl/Cargo.toml Marks [lints] as Anvil-managed.
crates/internity/Cargo.toml Marks [lints] as Anvil-managed.
crates/internity_macros/Cargo.toml Marks [lints] as Anvil-managed.
crates/fetch_winhttp/Cargo.toml Marks [lints] as Anvil-managed.
crates/fetch_winhttp_impl/Cargo.toml Marks [lints] as Anvil-managed.
crates/allocation_hints/Cargo.toml Marks [lints] as Anvil-managed.
constants.env Bumps CARGO_SEMVER_CHECKS_VERSION to 0.49.0 for non-Anvil consumers.
.github/workflows/anvil-scheduled.yml Adjusts permissions to allow issue publishing via called workflow.
.github/workflows/anvil-scheduled-impl.yml Switches to anvil-run-group; adds publish-failure issue publisher job.
.github/workflows/anvil-pr.yml Splits PR vs merge-queue callers; tightens permissions; adds inputs.
.github/workflows/anvil-pr-impl.yml Adds base ref input handling; uses anvil-run-group; adds status publishing hooks.
.github/actions/anvil-setup/just-problem-matcher.json Adds a Just problem matcher for clearer failing-recipe annotations.
.github/actions/anvil-setup/action.yml Adds matcher registration; validates group input; adjusts shell invocation.
.github/actions/anvil-scheduled-test/action.yml Removed in favor of shared anvil-run-group.
.github/actions/anvil-scheduled-runtime-analysis/action.yml Removed in favor of shared anvil-run-group.
.github/actions/anvil-scheduled-exhaustive/action.yml Removed in favor of shared anvil-run-group.
.github/actions/anvil-scheduled-advisories/action.yml Removed in favor of shared anvil-run-group.
.github/actions/anvil-pr-test/action.yml Removed in favor of shared anvil-run-group.
.github/actions/anvil-pr-runtime-analysis/action.yml Removed in favor of shared anvil-run-group.
.github/actions/anvil-pr-mutants/action.yml Removed in favor of shared anvil-run-group.
.github/actions/anvil-pr-fast/action.yml Removed in favor of shared anvil-run-group.
.github/actions/anvil-impact/action.yml Adds explicit .delta.toml usage and documentation clarifications.
.github/actions/anvil-run-group/action.yml New shared action to run a group + capture failing recipe output.
.github/actions/anvil-report-status/action.yml New action to publish/clear supplemental commit statuses per group/runner.
.gitattributes Forces LF endings for *.sh scripts.
.delta.toml Removes managed [delta] root-files block; leaves managed region empty.
.anvil/container/run-in-container.sh Adds managed container driver (bash) for local reproducible runs.
.anvil/container/run-in-container.ps1 Adds managed container driver (pwsh) for Windows local runs via WSL docker.
.anvil/container/README.md Adds managed documentation for local container execution.
.anvil/container/image-id.sh Adds managed image identity computation (bash).
.anvil/container/image-id.ps1 Adds managed image identity computation (pwsh).
.anvil/container/entrypoint.sh Adds managed container entrypoint for per-user Cargo home wiring.
.anvil/container/Containerfile.dockerignore Adds allowlist-based build-context filtering for container image builds.
.anvil/container/Containerfile Adds managed image build definition for anvil container environment.
.anvil.lock Updates Anvil lockfile to cargo-anvil 0.5.0 and new managed file set.
Suppressed comments (1)

.github/workflows/anvil-pr-impl.yml:158

  • Same concern as above: this step is tag-pinned and the comment asserts immutability that Git tags don’t guarantee. Prefer pinning to the release commit SHA to prevent unexpected upstream changes.
        uses: marocchino/sticky-pull-request-comment@v3.0.5 # immutable release, the tag cannot be moved

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

Comment thread .github/workflows/anvil-pr-impl.yml
Comment thread .github/workflows/anvil-scheduled-impl.yml
Comment thread .github/workflows/anvil-pr-impl.yml
Comment thread .github/actions/anvil-setup/action.yml
Copilot AI review requested due to automatic review settings September 1, 2026 10:01

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 95 out of 96 changed files in this pull request and generated no new comments.

Suppressed comments (5)

.github/workflows/anvil-pr-impl.yml:153

  • This uses: ...@v3.0.5 line claims the tag is immutable (“cannot be moved”), but Git tags can be retargeted. If the intent is supply-chain immutability, pin the action to a commit SHA (as this repo does for many other actions) and keep the version in the comment.
      - name: Upsert anvil-semver advisory
        if: always() && github.event_name == 'pull_request' && matrix.os == 'linux' && github.event.pull_request.head.repo.full_name == github.repository && hashFiles('target/anvil/comments/semver.md') != ''
        uses: marocchino/sticky-pull-request-comment@v3.0.5 # immutable release, the tag cannot be moved
        with:

.github/workflows/anvil-pr-impl.yml:159

  • Same issue as above: release tags (like v3.0.5) are not guaranteed immutable and can be moved. If you’re relying on immutability, pin to a commit SHA and keep the human-readable version in the comment.
      - name: Clear anvil-semver advisory
        if: always() && github.event_name == 'pull_request' && matrix.os == 'linux' && github.event.pull_request.head.repo.full_name == github.repository && hashFiles('target/anvil/comments/semver.md') == ''
        uses: marocchino/sticky-pull-request-comment@v3.0.5 # immutable release, the tag cannot be moved
        with:

.github/workflows/anvil-pr-impl.yml:207

  • This switches Codecov Action from a commit-SHA pin to a moving v7.0.0 tag while also claiming it’s immutable. If the goal is a reproducible/supply-chain-safe workflow, prefer pinning to a specific commit SHA and keep v7.0.0 as a comment.
        uses: codecov/codecov-action@v7.0.0 # immutable release, the tag cannot be moved

.github/workflows/anvil-scheduled-impl.yml:67

  • Same supply-chain concern here: @v7.0.0 is a mutable ref (tags can be moved). If you want immutability, pin to the commit SHA and keep the version as a comment.
        uses: codecov/codecov-action@v7.0.0 # immutable release, the tag cannot be moved

.github/actions/anvil-setup/action.yml:149

  • This swaps a commit-SHA pin for a v1.21.0 tag and asserts the tag can’t be moved. Git tags can be retargeted; if the intent is reproducible/supply-chain-safe installs, pin to the action commit SHA and keep the v1.21.0 label as a comment.
    - name: Install cargo-binstall
      uses: cargo-bins/cargo-binstall@v1.21.0 # immutable release, the tag cannot be moved

@codecov

codecov Bot commented Sep 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.0%. Comparing base (c6addf0) to head (b6fff7e).

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #718   +/-   ##
=======================================
  Coverage   100.0%   100.0%           
=======================================
  Files         587      587           
  Lines       63002    63002           
=======================================
  Hits        63002    63002           
Flag Coverage Δ
linux 99.9% <ø> (-0.1%) ⬇️
linux-arm 100.0% <ø> (ø)
scheduled ?
windows 99.9% <ø> (?)

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.

Evgenii (Vaiz) added a commit to microsoft/ox-tools that referenced this pull request Sep 1, 2026
anvil pins third-party actions two ways: by tag where the publisher has
enabled GitHub immutable releases, and by commit SHA everywhere else with
the version in a trailing comment. The rule was applied consistently but
written down nowhere, so from the generated output alone the tag pins look
like an inconsistency rather than a decision.

That gap has a measurable cost. On microsoft/oxidizer#718 an automated
reviewer flagged the tag pins four times in one review, once per pinned
action, each time recommending a revert to SHA pinning on the grounds that
Git tags are movable. The general rule is correct; it just does not apply
to a tag held by an immutable release. Without the policy recorded
anywhere, the same objection recurs on every adopting repo.

Add an "Action pinning" subsection to the GitHub backend design under
Security, covering what an immutable release actually guarantees, why that
makes a tag an acceptable pin, and the fact that immutability is a property
of one published release rather than a standing guarantee about the
publisher -- so a bump must re-verify it and fall back to a SHA when the
check fails.

Documentation only; no template, workflow or behaviour changes. The five
tag-pinned actions were re-checked while writing this and all report
`immutable: true`: codecov/codecov-action v7.0.0,
marocchino/sticky-pull-request-comment v3.0.5, cargo-bins/cargo-binstall
v1.21.0, taiki-e/install-action v2.81.8 and github/codeql-action v4.37.7.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Evgenii (Vaiz) and others added 3 commits September 3, 2026 06:08
Regenerates the anvil-managed check tree from cargo-anvil 0.5.0 (was
0.3.0) and bumps CARGO_SEMVER_CHECKS_VERSION to match.

anvil-semver-check has been non-functional repo-wide. The recipe
installs the pin from justfiles/anvil/versions.just, which was
cargo-semver-checks 0.46.0 (rustdoc JSON v55/v56/v57), while the
recipe builds docs with the runner default stable toolchain, now Rust
1.98, whose rustdoc emits v60. Every library crate aborted with
"unsupported rustdoc format v60", and because the recipe is advisory
each tool crash was collected as a finding and posted as a sticky
"Potential breaking changes detected" comment. The 0.5.0 catalog pins
0.49.0, which reads v56/v57/v60.

The 0.5.0 semver-check recipe also separates the two outcomes: exit
100 (real deny-level findings) renders under "Potential breaking
changes", exit 101 (an unbuildable or missing baseline) under
"Inconclusive comparisons", so a broken tool no longer presents as
breaking-change output.

constants.env is not anvil-managed and is bumped by hand from 0.48.0
to 0.49.0 so the main.yml semver job and scripts/release-packages.ps1
use the same version the recipes install.

Two things needed manual resolution:

* cargo-anvil 0.5.0 appends an anvil-lints managed region without
  detecting an existing unmanaged [lints] table, producing a duplicate
  table in the 20 crates that had one. cargo rejects that outright
  ("duplicate key ... [lints]"), so the pre-existing block is removed
  in favour of the managed region. Both are `workspace = true`, so
  this is not a lint behaviour change.
* The anvil-delta managed region no longer carries `[delta]
  root-files`. This repo's own top-level trip_wire_patterns already
  lists Cargo.lock, Cargo.toml and rust-toolchain.toml, so impact
  scoping is unchanged.

Six recipes carry deliberate repository-specific edits (fmt, bolero
and the four miri recipes, including miri's parallel artifact runner).
cargo-anvil left them alone and wrote .anvil-proposed sidecars; those
are not adopted here and are not committed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The 0.5.0 catalog runs every pwsh recipe as [script("pwsh",
"-NoProfile")]. The six recipes carrying repository-specific edits --
fmt, bolero, and the four miri recipes -- were left untouched by the
regeneration, so they kept the old [script("pwsh")] form. That left 46
recipes loading no profile and 7 script blocks loading the invoking
user's PowerShell profile.

A profile can change PATH, aliases, $ErrorActionPreference and output
encoding, so the inconsistency means these six checks can behave
differently from every other check on the same machine, and
differently from CI. It also costs profile load time on each
invocation.

This takes only the interpreter flag from the proposed templates. The
recipe bodies stay as they are: miri's parallel artifact runner is
deliberate (#706), and fmt and bolero carry their own repository
customizations.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
cargo-anvil 0.6.0 (2026-09-02) supersedes the 0.5.0 regeneration this
branch started from. Its catalog pins cargo-semver-checks 0.50.0, which
reads rustdoc JSON v57, v60 and v61 - covering the MSRV (1.95), the
pinned RUST_LATEST (1.96.1) and the runner default stable (1.98) that
broke the check in the first place.

The 0.6.0 catalog also:

* Replaces the job-output impact plumbing with an `anvil-impact`
  artifact that each group job downloads into target/anvil/impact, so
  scoped checks read the same cache as a local run.
* Adds an MSRV test check and a pr-msrv group.
* Replaces the .anvil/container script set with a generated Dockerfile
  and drops runner.just and the anvil-runner region.
* Bumps cargo-deny 0.20.2, cargo-doc2readme 0.7.3, cargo-llvm-cov
  0.9.0, cargo-mutants 27.1.0 and cargo-nextest 0.9.143.

constants.env is not anvil-managed; CARGO_SEMVER_CHECKS_VERSION is
bumped by hand to 0.50.0 so the workflow job and release-packages.ps1
install what the recipes install.

The six customized recipes (fmt, bolero, four miri) were skipped again
and keep the -NoProfile interpreter flag cherry-picked earlier.

`cargo anvil --dry-run` reports zero pending writes, cargo metadata
succeeds and just --list parses the tree.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings September 3, 2026 05:11
@Vaiz
Evgenii (Vaiz) force-pushed the u/vaiz/2026/09/01/anvil-0-5-0 branch from a434f92 to 703e662 Compare September 3, 2026 05:11
@Vaiz Evgenii (Vaiz) changed the title ci(anvil): update to cargo-anvil 0.5.0 to fix semver-check ci(anvil): update to cargo-anvil 0.6.0 to fix semver-check Sep 3, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Several repository-specific Just recipes still rely on ANVIL_INCLUDE_* env vars, but the regenerated CI now scopes via the downloaded target/anvil/impact cache, risking unintended full-workspace runs and loss of impact scoping.

Review details

Suppressed comments (3)

justfiles/anvil/checks/miri.just:33

  • _anvil-miri-test still depends on ANVIL_INCLUDE_AFFECTED for scoping, but the regenerated workflows now use the downloaded target/anvil/impact cache (and set ANVIL_IMPACT=consume) instead of exporting ANVIL_INCLUDE_*. With the env var unset, this will default to --workspace, undermining PR-tier impact scoping and potentially making runtime-analysis runs much larger than intended. Consider depending on anvil-impact and, when ANVIL_INCLUDE_AFFECTED isn’t already provided, populating it from _anvil-impact-include affected.
    justfiles/anvil/checks/bolero.just:43
  • anvil-bolero still relies on ANVIL_INCLUDE_AFFECTED for impact scoping, but the updated workflows now scope via the downloaded target/anvil/impact cache and do not populate ANVIL_INCLUDE_*. With ANVIL_INCLUDE_AFFECTED unset, this falls back to --workspace and can unintentionally turn the PR-tier bolero run into a full-workspace run. Prefer depending on anvil-impact and deriving the include list via _anvil-impact-include affected.
    justfiles/anvil/checks/fmt.just:21
  • This recipe still gates on ANVIL_INCLUDE_MODIFIED == "--skip", but the regenerated CI flow now scopes via the target/anvil/impact cache + ANVIL_IMPACT and does not set ANVIL_INCLUDE_*. As a result, anvil-fmt will never observe the skip sentinel and will always run. Consider depending on anvil-impact and reading the scope via _anvil-impact-include modified to preserve the modified-tier short-circuit.
  • Files reviewed: 90/91 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@Vaiz
Evgenii (Vaiz) marked this pull request as draft September 3, 2026 05:34
@Vaiz Evgenii (Vaiz) reopened this Sep 3, 2026
cargo-anvil 0.6.0 bumps the pinned cargo-doc2readme from 0.7.2 to 0.7.3,
which fixes the docs.rs link builder to use the dependency's package name
instead of its lib name. Three crates linked dependencies whose package
name and lib name differ, so their checked-in READMEs no longer matched
what the pinned generator produces and `anvil-readme-check` failed on all
four Fast Checks runners:

  seatbelt        tower_service -> tower-service
  rest_over_grpc  tower_service -> tower-service
  http_extensions http_body     -> http-body

Regenerated with the pinned 0.7.3 and the workspace crates/README.j2
template; the only changes are those docs.rs URLs and the embedded
dependency-info blob.
@Vaiz

Copy link
Copy Markdown
Contributor Author

Answering the "Needs a closer look" review body from 2026-09-03 (the three suppressed comments on justfiles/anvil/checks/miri.just, bolero.just and fmt.just). The finding is correct, and it is a real regression in this PR rather than a false positive, so thank you for it.

Verified against the tree at 703e6628: ANVIL_INCLUDE_AFFECTED / ANVIL_INCLUDE_MODIFIED are now referenced in exactly six files, all of them repository-customized recipes under justfiles/anvil/checks/miri.just, miri-tree-borrows.just, miri-strict-provenance.just, miri-race-coverage.just, bolero.just and fmt.just. Nothing under .github/** exports either variable any more; the regenerated workflows scope through the downloaded target/anvil/impact cache with ANVIL_IMPACT=consume. So the consequence you describe is exactly right: anvil-fmt can no longer observe the --skip sentinel and always runs, and anvil-bolero / the anvil-miri* recipes fall back to --workspace instead of the affected subset.

The cause is that these six recipes are repository-customized forks. They carry the older # Managed by cargo-anvil. Update the corresponding template ... unless the change is repository-specific. header rather than the # GENERATED BY cargo-anvil. DO NOT EDIT DIRECTLY. header the regenerated files carry, so cargo-anvil 0.6.0 refused to overwrite them and instead emitted .anvil-proposed siblings for each. Those proposals are precisely the shape you suggest — for example the proposed fmt.just replaces the env-var guard with $include = (& just _anvil-impact-include modified) and takes a dependency on anvil-impact, which is the change you recommended.

Resolution direction: the right fix is to adopt cargo-anvil's proposals and drop the local forks, because this PR's whole purpose is to consume anvil's generated output rather than maintain repository-specific variants of it. I am not doing that in this commit, because it is not mechanical: the customized miri.just is 256 lines against a 57-line proposal, so adopting it wholesale would discard a large amount of repository-specific miri logic that has to be re-homed upstream or deliberately re-applied. That is a decision for the repository owner, and it is now flagged for them.

Two related notes for anyone reading this thread. First, the anvil-readme-check failure on all four Fast Checks runners was unrelated and is fixed in ebf327ac. Second, anvil-msrv-test — a check group new in cargo-anvil 0.6.0 — is failing for an upstream reason described in the pull request description; it is not addressed here.

`constants.env` feeds the pre-anvil `main` workflow, whose `static-analysis`
job installs cargo-doc2readme and runs its own README check. cargo-anvil 0.6.0
pins 0.7.3 in `justfiles/anvil/versions.just`, but `constants.env` still said
0.7.2, so the two workflows disagreed about the generator for the same
checked-in READMEs.

That drift became visible once the READMEs were regenerated in ebf327a:
`anvil-readme-check` went green under 0.7.3 while `static-analysis` failed on
all four runners under 0.7.2 with the mirror-image error

    ERROR: Readme links to incompatible version of dependency `tower_service'

(0.7.2 wants the lib name `tower_service`, 0.7.3 the package name
`tower-service`). Only one of the two can be right for a single checked-in
README, and it has to be the version anvil pins.

This mirrors what the PR already does for CARGO_SEMVER_CHECKS_VERSION: when
anvil bumps a tool pin, the hand-written constants file has to follow so the
non-anvil consumers stay in step. Only the doc2readme pin is bumped here --
cargo-deny, cargo-llvm-cov, cargo-mutants, cargo-nextest and cargo-spellcheck
also drift between the two files but nothing currently fails on them, so they
are left alone rather than bumped speculatively.
Evgenii (Vaiz) added a commit to microsoft/ox-tools that referenced this pull request Sep 3, 2026
`anvil-msrv-test` ran `cargo test --all-targets`. That flag expands to
`--lib --bins --tests --benches --examples`, so the recipe built AND
EXECUTED every bench target as part of the minimum-version check.

A bench declared `harness = false` delegates its run to a separate driver
binary -- criterion's, or a profiler runner such as `gungraun-runner`
driving Valgrind. `anvil-msrv-test-setup` installs only `cargo-delta` and
the MSRV toolchain, so that driver is never present and the group fails on
a prerequisite it does not declare:

    --> Error in ae_basic_operations_cg: Failed to run benchmarks:
        No such file or directory (os error 2).
        Is gungraun-runner installed and gungraun-runner in your $PATH?
    error: recipe `anvil-msrv-test` failed with exit code 1

That failure carries no minimum-version signal. An MSRV check exists to
prove the affected packages still compile and their tests still pass under
the declared minimum compiler; running benchmark harnesses is not part of
that, and it breaks the check for any adopter with a `harness = false`
bench. Observed on microsoft/oxidizer#718 (linux and linux-arm legs).

Use `--tests` instead. Chosen over the literally-equivalent
`--lib --bins --tests` because `--lib` errors with "no library targets
found" on a bin-only affected package under impact scoping -- the same
reason `anvil-miri` already selects `--tests`, documented in `miri.just`.
`--tests` selects exactly the targets carrying `test = true`: lib unit
tests, bin unit tests and integration tests.

A compile-only `--all-targets --no-run` pass was considered and rejected.
Benches and examples are dev-only targets that no consumer compiles, so
they do not constrain the crate's real MSRV, and the repository already
covers "do they still build" without running them: `anvil-bench` is
`cargo bench --no-run` and `anvil-examples` is `cargo build --examples`.
Adding the pass would double MSRV build time on the PR critical path for
a signal already owned elsewhere. This change makes `msrv-test` consistent
with that existing repository-wide policy.

No doctest coverage is lost: `--all-targets` suppresses doctests as well,
and `anvil-doc-test` owns them.

`msrv-test.just` was the only template with `cargo test --all-targets`.
`clippy.just` and `udeps.just` also pass `--all-targets`, but to `cargo
clippy` and `cargo udeps`, which compile without executing; both are left
as they are. `anvil-llvm-cov` and the other nextest paths are untouched.

The `impact.rs` contract test pinned the old argv; it now pins the new one
and additionally asserts that neither `--all-targets` nor `--benches` ever
reaches the MSRV invocations, so a regression fails loudly rather than
silently re-enabling bench execution.

Design doc, README and the mirrored lib.rs table updated to match; the
three emitted-tree snapshots re-recorded and reviewed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Evgenii (Vaiz) added a commit to microsoft/ox-tools that referenced this pull request Sep 3, 2026
The generated workflows carried this comment at every tag-pinned action:

    uses: codecov/codecov-action@v7.0.0 # immutable release, the tag cannot be moved

The claim is true for these specific pins -- GitHub's immutable-releases
feature does lock the tag to a commit, and the REST API reports
`"immutable": true` for each of them. But the sentence asserts a property
of the TAG, and read on its own it looks like a general claim that Git
tags are stable, which is false and is exactly the belief SHA-pinning
exists to defend against.

Read that way it is also self-undermining: if tags could not be moved,
the SHA pins on `actions/checkout` and `actions/download-artifact` in the
same file would be pointless. On microsoft/oxidizer#718 this produced four
separate automated-reviewer findings that a human had to rebut one at a
time.

Reword to name the mechanism the pin depends on instead:

    # pinned by tag: this release is an immutable release (GitHub locks the tag to a commit)

Same length class, one line, and it no longer generalises: it says why
THIS pin is safe rather than what tags are. Applied to all five sites --
`cargo-bins/cargo-binstall` in setup-action.yml, `codecov/codecov-action`
in both pr- and scheduled-impl-workflow.yml, and both
`marocchino/sticky-pull-request-comment` uses in pr-impl-workflow.yml.

`docs/design/github.md` reproduces both the comment and a sample step, so
both copies are updated to keep the doc and the templates from drifting,
plus a sentence recording why the comment names a mechanism rather than
asserting tag stability. The "Action pinning" section already explained
that immutability is per-release and must be re-confirmed on a version
bump; added one sentence stating that `actions/checkout` and
`actions/download-artifact` stay SHA-pinned because immutable releases are
opt-in per publisher and theirs have not enabled it -- not because those
actions are less trusted.

Behaviour is unchanged: comments only, same action versions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Evgenii (Vaiz) added a commit to microsoft/ox-tools that referenced this pull request Sep 3, 2026
`anvil-msrv-test` ran `cargo test --all-targets`. That flag expands to `--lib --bins --tests --benches --examples`, so the recipe built AND EXECUTED every bench target as part of the minimum-version check.

A bench declared `harness = false` delegates its run to a separate driver binary -- criterion's, or a profiler runner such as `gungraun-runner` driving Valgrind. `anvil-msrv-test-setup` installs only `cargo-delta` and the MSRV toolchain, so that driver is never present and the group fails on a prerequisite it does not declare:

    --> Error in ae_basic_operations_cg: Failed to run benchmarks:
        No such file or directory (os error 2).
        Is gungraun-runner installed and gungraun-runner in your $PATH?
    error: recipe `anvil-msrv-test` failed with exit code 1

That failure carries no minimum-version signal. An MSRV check exists to prove the affected packages still compile and their tests still pass under the declared minimum compiler; running benchmark harnesses is not part of that, and it breaks the check for any adopter with a `harness = false` bench. Observed on microsoft/oxidizer#718 (linux and linux-arm legs).

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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