Skip to content

feat(cargo-anvil)!: identify the ADO stage on every job wrapper invocation - #154

Open
Evgenii (Vaiz) wants to merge 1 commit into
mainfrom
u/vaiz/2026/09/03/anvil-job-stage-parameter
Open

feat(cargo-anvil)!: identify the ADO stage on every job wrapper invocation#154
Evgenii (Vaiz) wants to merge 1 commit into
mainfrom
u/vaiz/2026/09/03/anvil-job-stage-parameter

Conversation

@Vaiz

Copy link
Copy Markdown
Contributor

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

Replaces #85, which sat 37 commits behind main in exactly the files it edited (pr-stages.yml +80/-78, ado.rs +115/-32, ado.md +242/-130) and would have failed its own test on rebase, because a new pr_msrv stage landed meanwhile. Re-derived from current main, with martin-kolinek's four review threads from that PR folded in rather than retrofitted.

Problem

steps/job.yml is the extensibility point adopters on 1ESPT / SubstratePT / M365PT take ownership of, but it cannot tell which stage it is rendering. Two escape hatches do not work:

  • per-OS job names repeat — linux / windows in every pr-* and scheduled-* stage, compute_linux / compute_windows in impact — so name does not identify the stage;
  • templateContext: is consumed at template-expansion time, so a runtime condition on System.StageName is not available there either.

The motivating case is Component Governance. A pr_mutants job compiles inside cargo-mutants' scratch copy of the tree, so its cargo SBOM dies with that copy and CG falls back to parsing Cargo.lock, reporting optional backends the build never selects. A wrapper that sets failOnAlert: true has to exempt that one stage, and today has no way to name it. The impact jobs are the same non-compiling shape.

Change

Add an optional stage parameter to the steps/job.yml contract and pass it from all 20 invocations:

- template: steps/job.yml
  parameters:
    name: linux
    stage: pr_mutants
    pool: ${{ parameters.linuxPool }}

The value is the ADO stage identifier exactly as written after stage:, which also matches System.StageName. The default wrapper ignores it; it exists purely so an owned wrapper can discriminate. It defaults to '', so a caller that passes no stage still renders.

Named stage, not group (thread r3841475638): group already means "anvil group recipe name" across this repo — anvil-run-group, anvil-setup, steps/setup.yml — and impact is not an anvil check group. The stage identifier is what the value actually is, so the collision disappears instead of needing a disclaimer.

Effects

  • steps/job.yml accepts stage; the default wrapper's rendered output is otherwise unchanged.
  • All 20 steps/job.yml invocations across both stages templates pass a stage — 10 stages × 2 per-OS jobs.
  • New test stages_identify_the_stage_for_every_job derives the expected stages from GROUPS rather than restating them (thread r3841475966). A group added to GROUPS but never threaded into a stages template now fails the test — the regression the old hardcoded list could not catch. Verified by temporarily dropping one stage: line: the test fails naming the exact stage and template.
  • job_wrapper_declares_expected_contract also pins name: stage.
  • The five stale mirrors of the old name/pool/steps/artifacts contract are reconciled (thread r3841475483), including the §4.2 sample, which previously called steps/job.yml with no stage while the new test requires one. The "stages update without merging" promise is now qualified: it holds for the wrapper body, not for a contract that grows.
  • Marked breaking (thread r3841475789). squash_merge_commit_message is COMMIT_MESSAGES, so the BREAKING CHANGE: trailer reaches the changelog; CHANGELOG.md is written at bump time and is deliberately not hand-edited here.
  • The ado_backend snapshot is regenerated, never hand-edited.

Adopter action required. ADO rejects a parameter the target template does not declare. A repository that already owns steps/job.yml must take the proposed wrapper update in the same change as the regenerated pr.yml / scheduled.yml, or its pipeline fails to compile.

Validation

cargo test -p cargo-anvil (386 unit + all integration suites), cargo clippy -p cargo-anvil --all-targets -D warnings, cargo fmt, and just anvil-spellcheck — all green.

Copilot AI lite review requested due to automatic review settings September 3, 2026 10:38

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 stage contract expansion is consistently applied across templates and is backed by a targeted regression test plus synchronized docs/snapshots.

Pull request overview

This PR extends the Azure DevOps job wrapper contract in cargo-anvil so every steps/job.yml invocation is told which ADO stage it belongs to, enabling downstream-owned wrappers (e.g., 1ESPT/SubstratePT/M365PT) to apply stage-specific behavior despite per-OS job name reuse.

Changes:

  • Add an optional stage parameter to templates/ado/steps/job.yml (default '') and pass it from all PR and scheduled stage job wrapper invocations.
  • Add a regression test that derives the expected ADO stage identifiers from GROUPS (plus the literal impact) and asserts every steps/job.yml invocation passes stage.
  • Update the ADO design doc and regenerate the ado_backend snapshot to reflect the expanded wrapper contract and new stage threading.
File summaries
File Description
crates/cargo-anvil/templates/ado/steps/job.yml Expands the job wrapper parameter contract with optional stage (default '') and documents its purpose for owned wrappers.
crates/cargo-anvil/templates/ado/pr-stages.yml Threads stage: <stage_id> through every PR-tier steps/job.yml invocation (including impact).
crates/cargo-anvil/templates/ado/scheduled-stages.yml Threads stage: <stage_id> through every scheduled-tier steps/job.yml invocation.
crates/cargo-anvil/src/anvil/artifacts/ado.rs Updates contract-pinning test and adds a new test ensuring every job wrapper invocation passes stage, derived from GROUPS.
crates/cargo-anvil/docs/design/ado.md Updates design documentation to reflect the stage parameter and the adopter implication of contract growth.
crates/cargo-anvil/tests/snapshots/snapshots__ado_backend.snap Regenerates the backend snapshot to include the new stage parameter usage and updated wrapper contract text.
Review details
  • Files reviewed: 6/6 changed files
  • Comments generated: 0
  • Review effort level: Lite

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

@codecov-commenter

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

Copy link
Copy Markdown

Codecov Report

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

❌ 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    #154   +/-   ##
=====================================
  Coverage   97.5%   97.5%           
=====================================
  Files        299     299           
  Lines      67766   67766           
=====================================
+ Hits       66125   66126    +1     
+ Misses      1641    1640    -1     
Flag Coverage Δ
linux 100.0% <ø> (+2.4%) ⬆️
linux-arm 100.0% <ø> (+2.4%) ⬆️
scheduled ?
windows 100.0% <ø> (+2.2%) ⬆️

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.

…ation

`steps/job.yml` is the extensibility point adopters take ownership of,
but it cannot tell which stage it is rendering: per-OS job names repeat
across stages, and `templateContext:` is consumed at template-expansion
time, so a runtime condition on `System.StageName` is unavailable there.

Add an optional `stage` parameter to the `steps/job.yml` contract and
pass it from all 20 invocations across both stages templates. The value
is the ADO stage identifier exactly as written after `stage:`, matching
`System.StageName`. The default wrapper ignores it; it exists so an
owned wrapper can discriminate. It defaults to `''`, so a caller that
passes no stage still renders.

BREAKING CHANGE: `steps/job.yml` now accepts a `stage` parameter and all
generated stage templates pass it. ADO rejects a parameter the target
template does not declare, so a repository that owns `steps/job.yml`
must take the wrapper update in the same change as the regenerated
`pr.yml` / `scheduled.yml`, or its pipeline fails to compile.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Vaiz
Evgenii (Vaiz) force-pushed the u/vaiz/2026/09/03/anvil-job-stage-parameter branch from 69a24bd to b0d6db4 Compare September 4, 2026 08:18
Copilot AI review requested due to automatic review settings September 4, 2026 08:18

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 wrapper contract change is consistently propagated through templates, enforced by targeted tests, and reflected in the design documentation and regenerated snapshot.

Review details
  • Files reviewed: 6/7 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.

4 participants