Skip to content

feat: Introduce cargo-anvil for unified Rust build/CI scaffolding - #33

Merged
martin-kolinek merged 23 commits into
mainfrom
unified-builds-impl
Jun 16, 2026
Merged

feat: Introduce cargo-anvil for unified Rust build/CI scaffolding#33
martin-kolinek merged 23 commits into
mainfrom
unified-builds-impl

Conversation

@martin-kolinek

@martin-kolinek martin-kolinek commented May 27, 2026

Copy link
Copy Markdown
Collaborator

Introduces cargo-anvil: a cargo subcommand that ships and maintains an opinionated, unified build/CI scaffolding for Rust workspaces. One cargo anvil invocation emits a complete, reviewable tree of GitHub Actions workflows, Azure DevOps pipelines, justfiles/anvil/ recipes, and managed regions in Cargo.toml / deny.toml / rustfmt.toml / .delta.toml / clippy.toml / spellcheck.toml. Subsequent runs keep the tree in sync as the tool's catalog evolves, while preserving any local customizations the adopter has made.

Why this exists

Every Rust repo in the oxidizer / oxidizer-github / ox-tools family hand-rolls its own CI on the same opinionated checks (fmt, clippy, deny, audit, miri, careful, mutants, llvm-cov, cargo-hack, semver-check, doc, aprz, …). The cost of keeping those copies in sync — toolchain bumps, lint updates, new advisory feeds, cross-OS matrix changes — is paid per-repo, by hand, every time. cargo-anvil centralizes the catalog in one place and renders it into the per-repo CI surface.

What an adopter gets

After one cargo anvil, the repo contains:

  • .github/workflows/anvil-pr.yml + anvil-pr-impl.yml and composite actions under .github/actions/anvil-* for the PR tier (anvil-pr-fast, anvil-pr-test, anvil-pr-mutants, anvil-pr-runtime-analysis).
  • .github/workflows/anvil-scheduled.yml + anvil-scheduled-impl.yml and matching actions for the scheduled (nightly) tier.
  • An ADO mirror under .pipelines/ (when --backend ado is selected or autodetected).
  • justfiles/anvil/ recipe tree (mod.just, checks.just, groups.just, tiers.just, tools.just, versions.just). Local just anvil-pr-fast, just anvil-clippy, etc. reproduce CI without any cargo-anvil binary present.
  • Layered tool-setup recipes: each tool gets anvil-tool-<name>-install / -validate-prereqs, each check anvil-<check>-setup / -validate-prereqs, each group/tier the same pattern, all composed up to a global anvil-setup. CI jobs install only what they need.
  • Managed regions spliced into Cargo.toml (opinionated workspace + per-crate lints), rustfmt.toml, deny.toml, clippy.toml, spellcheck.toml, .delta.toml, .cargo-heather.toml. Each region is delimited by # managed by cargo anvil / # end managed by cargo anvil.
  • A sidecar .anvil.lock manifest tracking per-file and per-region checksums for the three-checksum update algorithm.

Design highlights

  • Three impact tiers (modified, affected, required) backed by cargo-delta. Each check is tagged with the tier it scopes to; CI wiring emits one include-list env var per tier with a --skip sentinel for empty tiers. The required tier is included for tools whose correctness resolves through the dep graph (cargo doc, cargo hack, cargo udeps); the unscoped bucket is reserved for Cargo.lock- and PR-context-only checks (deny, audit, aprz, pr-title).
  • Three update decisions (Write, LeaveAlone, Propose) keyed on a three-way comparison of (original-template, current-template, live-file) checksums, all tracked in the lock manifest. A user edit inside a managed region is preserved when the template hasn't moved; a template bump becomes a .anvil-proposed sidecar with a one-line summary, so customizations are never silently overwritten.
  • Backend autodetection from origin's URL; explicit --backend github / --backend ado override; --no-backends for local-only.
  • Cross-OS matrix defaults matching the surveyed-repo evidence. GitHub default: Linux/Windows × x86_64/aarch64 (4 legs, using GH's hosted ARM runners). ADO default: Linux + Windows x86_64 (ADO has no hosted ARM agents). Compile-sensitive checks matrix; text/metadata checks ride along on the same matrix to keep group definitions stable.
  • Caching (GitHub actions/cache, ADO Cache@2) keyed on OS + arch + rustc version + lockfile-family hashes + versions.just hash, so toolchain bumps and catalog updates invalidate cleanly.
  • Self-validation gate via .github/workflows/regenerate-check.yml — the one hand-written workflow that builds cargo-anvil from the PR's branch, runs cargo anvil --dry-run, and fails the PR if the in-tree state drifts from what the templates would render.

Verification

  • 199 unit tests in crates/cargo-anvil/src/ (run/plan/decision/region/emit/workspace/manifest/checksum).
  • 4 schema tests in tests/schemas.rs (every emitted TOML parseable; every emitted workflow valid against actionlint; ADO YAML indentation consistent; every emitted recipe just-parseable).
  • 3 snapshot tests in tests/snapshots.rs covering the three representative backend combinations (local-only, GitHub-backend, ADO-backend) — full byte-exact emitted tree.
  • 4 fixture tests in tests/update.rs covering single-crate (no [workspace]), opt-outs (emptied managed region preserved), customized (user edit inside a managed region preserved), migration (pre-existing hand-written Justfile / deny.toml / [profile.release] survive splicing).
  • Dogfooded against ox-tools itself: this PR's workspace is the first adopter, so the full PR tier (anvil-pr-fast, anvil-pr-test, anvil-pr-mutants, anvil-pr-runtime-analysis) plus the scheduled tier runs against the very cargo-anvil build under review on every push.
  • cargo-delta CLI and JSON shape verified by running the actual binary against this workspace; the impact-step shell scripts encode the real two-snapshot --baseline / --current flow.

Total: 210 tests green locally + 4 PR-tier groups × 4 OS legs + impact + scheduled-runtime tier all green in CI.

Documentation

Lives under crates/cargo-anvil/docs/:

  • design/design.md — top-level design and CLI shape.
  • design/local.md — the just recipe tree, impact env vars, daily-driver flow.
  • design/github.md — owned reusable workflows, per-group composite actions, impact scoping.
  • design/ado.md — owned stages templates, per-group step templates, 1ESPT composition guidance.
  • design/checks.md — the opinionated catalog and per-group OS scope matrix.
  • design/updates.md — the three-checksum state machine validated by fixture tests.
  • verification.md — continuous-validation strategy (dogfooding + snapshot + fixtures + schema).
  • implementation-plans/0000.md — initial implementation plan (history).
  • implementation-plans/0001.md — design-vs-implementation reconciliation plan executed in this PR.

Naming

The crate, binary, recipe namespace, lockfile, env vars, and managed-region markers all use anvil (no ox- prefix). This keeps the tool open-sourceable outside the ox- brand. The forge metaphor (anvil = stable opinionated surface for forging per-repo build tooling) is mentioned in design/design.md once and intentionally absent from user-facing surfaces.

Follow-ups (not in this PR)

  • Publish cargo-anvil to crates.io once a few in-tree adopters have shaken it out.
  • 1ESPT-compliant ADO composition examples for repos that need them — cargo-anvil emits composable stages, not a 1ESPT extender; adopters compose the stages template into their compliance pipeline.

@martin-kolinek martin-kolinek changed the title cargo-ox-check: plan 0001 implementation + dogfood-ready iteration Introduce cargo-ox-check: unified Rust build/CI scaffolding May 27, 2026
@martin-kolinek
martin-kolinek force-pushed the unified-builds-impl branch 7 times, most recently from 7be21da to 5243f05 Compare June 1, 2026 17:26
@codecov-commenter

Codecov Comments Bot (codecov-commenter) commented Jun 1, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.56465% with 130 lines in your changes missing coverage. Please review.
✅ Project coverage is 95.4%. Comparing base (9c14fe3) to head (01c92a8).

Files with missing lines Patch % Lines
crates/cargo-anvil/src/run.rs 91.8% 35 Missing ⚠️
crates/cargo-anvil/src/backend.rs 78.6% 32 Missing ⚠️
crates/cargo-anvil/src/plan.rs 96.1% 18 Missing ⚠️
crates/cargo-anvil/src/main.rs 0.0% 17 Missing ⚠️
crates/cargo-anvil/src/region.rs 97.8% 6 Missing ⚠️
crates/cargo-anvil/src/workspace.rs 97.7% 5 Missing ⚠️
crates/cargo-anvil/src/emit/github.rs 97.4% 4 Missing ⚠️
crates/cargo-anvil/src/emit/ado.rs 97.8% 3 Missing ⚠️
crates/cargo-anvil/src/emit/cargo_toml.rs 98.1% 3 Missing ⚠️
crates/cargo-anvil/src/emit/shared_configs.rs 97.4% 2 Missing ⚠️
... and 4 more
Additional details and impacted files
@@           Coverage Diff           @@
##            main     #33     +/-   ##
=======================================
+ Coverage   93.2%   95.4%   +2.1%     
=======================================
  Files         28      48     +20     
  Lines       1262    5029   +3767     
=======================================
+ Hits        1177    4799   +3622     
- Misses        85     230    +145     
Flag Coverage Δ
linux 95.4% <95.5%> (?)
linux-arm 95.4% <95.5%> (?)
windows 59.4% <ø> (?)

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.

@martin-kolinek martin-kolinek changed the title Introduce cargo-ox-check: unified Rust build/CI scaffolding feat: Introduce cargo-ox-check for unified Rust build/CI scaffolding Jun 1, 2026
@martin-kolinek
martin-kolinek changed the base branch from unified-builds to main June 4, 2026 11:32
@github-actions

github-actions Bot commented Jun 5, 2026

Copy link
Copy Markdown

⚠️ Breaking Changes Detected

error: failed to retrieve local crate data from git revision

Caused by:
    0: failed to retrieve manifest file from git revision source
    1: possibly due to errors: [
         failed when reading /home/runner/work/ox-tools/ox-tools/target/semver-checks/git-origin_main/3b8d0ca833db6717e02d157ce4f59f148fa8d958/scripts/crate-template/Cargo.toml: TOML parse error at line 9, column 26
         |
       9 | keywords = ["oxidizer", {{CRATE_KEYWORDS}}]
         |                          ^
       missing key for inline table element, expected key
       : TOML parse error at line 9, column 26
         |
       9 | keywords = ["oxidizer", {{CRATE_KEYWORDS}}]
         |                          ^
       missing key for inline table element, expected key
       ,
         failed to parse /home/runner/work/ox-tools/ox-tools/target/semver-checks/git-origin_main/3b8d0ca833db6717e02d157ce4f59f148fa8d958/crates/cargo_ensure_no_cyclic_deps/tests/fixtures/without_cycle/Cargo.toml: no `package` table,
         failed to parse /home/runner/work/ox-tools/ox-tools/target/semver-checks/git-origin_main/3b8d0ca833db6717e02d157ce4f59f148fa8d958/crates/cargo_ensure_no_cyclic_deps/tests/fixtures/with_dev_cycle/Cargo.toml: no `package` table,
         failed to parse /home/runner/work/ox-tools/ox-tools/target/semver-checks/git-origin_main/3b8d0ca833db6717e02d157ce4f59f148fa8d958/Cargo.toml: no `package` table,
         failed to parse /home/runner/work/ox-tools/ox-tools/target/semver-checks/git-origin_main/3b8d0ca833db6717e02d157ce4f59f148fa8d958/crates/cargo_ensure_no_cyclic_deps/tests/fixtures/with_cycle/Cargo.toml: no `package` table,
         failed to parse /home/runner/work/ox-tools/ox-tools/target/semver-checks/git-origin_main/3b8d0ca833db6717e02d157ce4f59f148fa8d958/crates/cargo_ensure_no_cyclic_deps/tests/fixtures/with_self_dev_dep/Cargo.toml: no `package` table,
       ]
    2: package `cargo-anvil` not found in /home/runner/work/ox-tools/ox-tools/target/semver-checks/git-origin_main/3b8d0ca833db6717e02d157ce4f59f148fa8d958

Stack backtrace:
   0: anyhow::error::<impl anyhow::Error>::msg
   1: cargo_semver_checks::rustdoc_gen::RustdocFromProjectRoot::get_crate_source
   2: cargo_semver_checks::rustdoc_gen::StatefulRustdocGenerator<cargo_semver_checks::rustdoc_gen::CoupledState>::prepare_generator
   3: cargo_semver_checks::Check::check_release::{{closure}}
   4: cargo_semver_checks::Check::check_release
   5: cargo_semver_checks::exit_on_error
   6: cargo_semver_checks::main
   7: std::sys::backtrace::__rust_begin_short_backtrace
   8: main

If the breaking changes are intentional then everything is fine - this message is merely informative.

Remember to apply a version number bump with the correct severity when publishing a version with breaking changes (1.x.x -> 2.x.x or 0.1.x -> 0.2.x).

@martin-kolinek
martin-kolinek force-pushed the unified-builds-impl branch 5 times, most recently from 36840e6 to d87a60e Compare June 10, 2026 09:43
Comment thread crates/cargo-ox-check-update/docs/design/design.md Outdated
Comment thread crates/cargo-anvil/docs/design/design.md
Comment thread crates/cargo-anvil/docs/design/checks.md Outdated
@github-actions

github-actions Bot commented Jun 10, 2026

Copy link
Copy Markdown

⚠️ Potential breaking changes detected

cargo semver-checks flagged the following on this PR. This is informational -- breaking changes between commits are expected; the major-version bump happens at release time, not on every PR.

cargo-heather

    Building cargo-heather v0.2.1 (current)
       Built [   5.803s] (current)
     Parsing cargo-heather v0.2.1 (current)
      Parsed [   0.002s] (current)
    Building cargo-heather v0.2.1 (baseline)
       Built [   5.953s] (baseline)
     Parsing cargo-heather v0.2.1 (baseline)
      Parsed [   0.002s] (baseline)
    Checking cargo-heather v0.2.1 -> v0.2.1 (no change; assume minor)
     Checked [   0.013s] 196 checks: 195 pass, 1 fail, 0 warn, 49 skip

--- failure method_parameter_count_changed: pub method parameter count changed ---

Description:
A publicly-visible method now takes a different number of parameters, not counting the receiver (self) parameter.
        ref: https://doc.rust-lang.org/cargo/reference/semver.html#fn-change-arity
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.46.0/src/lints/method_parameter_count_changed.ron

Failed in:
  cargo_heather::CommentStyle::format_header now takes 2 parameters instead of 1, in /home/runner/work/ox-tools/ox-tools/crates/cargo-heather/src/comment.rs:129

     Summary semver requires new major version: 1 major and 0 minor checks failed
    Finished [  12.189s] cargo-heather

@martin-kolinek martin-kolinek changed the title feat: Introduce cargo-ox-check for unified Rust build/CI scaffolding feat: Introduce cargo-anvil for unified Rust build/CI scaffolding Jun 12, 2026
martin-kolinek added a commit that referenced this pull request Jun 15, 2026
Addresses Sander's review feedback on PR #33: 'CI' is a working

methodology, not a synonym for automation pipelines. Adopt

'cloud workflows' (neutral across GH Actions and ADO pipelines)

throughout cargo-anvil docs, source comments, template comments,

and Cargo.toml description/keywords.

Scope: cargo-anvil only. No code identifiers changed. Pre-existing

ox-tools repo docs (top-level README.md, DEVELOPMENT.md) and other

crates' READMEs left alone since they predate cargo-anvil and were

not part of Sander's review.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
martin-kolinek and others added 6 commits June 15, 2026 14:18
Top-level design document covering the why (unified, opinionated CI/build
scaffolding for Rust repos), principles (recipe-first; cargo-as-fallback;
owned-with-customization), and tier/group structure. Companion documents
split out checks.md (catalog), local.md (justfile layout), updates.md
(drift detection), github.md / ado.md (per-backend wiring), and
verification.md (continuous-validation strategy).

Includes the initial pipeline design, the --backend flag shape (repeatable
not enum), the rename from cargo-ox-ci to cargo-ox-check, and
implementation-plans/0000.md laying out the 22-step MVP path.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
22 implementation steps from plan 0000:

  1.  CLI surface with no-op `update` subcommand
  2.  Backend autodetection from `origin` URL
  3.  Workspace discovery and member enumeration
  4.  `.ox-check.lock` manifest read/write
  5.  SHA-256 helpers + three-checksum (T/L/D) decision algorithm
  6.  Managed-region parser and writer
  7.  Plan, proposed-file emission, dry-run summary
  8.  tools.just emitter and owned-file driver
  9.  checks.just emitter (per-check recipes)
  10. groups.just + tiers.just emitters
  11. Justfile imports managed region
  12. Cargo.toml workspace + member [lints] regions
  13. deny.toml, rustfmt.toml, .delta.toml regions
  14. End-to-end local-only update wiring
  15-17. GitHub composite actions, reusable workflows, root workflows
  18-20. ADO step templates, stages templates, root pipelines
  21. Schema-validation tests (actionlint, taplo, just-parse)
  22. regenerate-check workflow for dogfooding

Plus initial README and crates.io polish.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Switch from anyhow to ohno::AppError throughout cargo-ox-check
- Drop DecisionInputs::emptied and Decision::Skipped
- Drop spurious ox-check-managed sentinels from owned .just templates
- Move per-group GH action / ADO step YAML bodies into template files
  (single source for the per-group template body, substituted at emit)
- Single-import Justfile pattern: only mod.just visible to users, all
  ox-check recipes reached transitively; alias in mod.just
- Drop ADO YAML `pr:` trigger (Azure Repos uses branch policies)
- Include master alongside main in scheduled-tier schedule
- insta snapshot tests covering local, github, and ado emitted trees
- Codecov upload (GH) and PublishCodeCoverageResults@2 (ADO) wired in

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
P0 docs sweep, then:
  P1 (C2-C5): drop ox-check-tools-install-missing from the design;
             Propose decision bumps manifest L to the new template;
             purge stale manifest entries on update; categorized dry-run
             summary (Will create / Will update / Will propose / etc.)
  P2 (C6-C8): tool-version policy -- per-tool minimums + version-aware
             require check + rustc/pwsh gate
  P3 (C9-C11): three-tier impact scoping (modified / affected / required)
             via cargo-delta, with per-tier env vars consumed by recipes
  P4 (C12-C14): llvm-cov HTML report + missing lints; BASE_REF master
             fallback; per-recipe _ox-check-require fan-out
  P5 (C15-C16): GitHub actions/cache in setup composite; ADO Cache@2
             in setup step
  P6 (C17-C18): fixture scenarios under tests/fixtures/; rewrite
             verification.md fixture/schema sections

Also: required-tier env var introduced and used by checks classified as
required (workspace-wide tools that consume the dep closure). Compile-
sensitive groups (pr-fast, pr-test, pr-runtime-analysis, advisories)
expanded to cross-OS. ARM64 legs added to GitHub default matrix.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Pre-dogfood fixes: bash shell default, arch in cache key
- cargo-delta CLI / JSON shape adjustments after surveying real tool versions
- Per-recipe bash shebang annotation; drop intrusive `set shell`
- Initial dogfood pass on this repo + follow-up lint/rustfmt resolution
- Switch recipe interpreter from bash shebang to [script("pwsh")]
- Normalize line endings before hashing; lowercase justfile path migration
- Cohabit-outside-region pattern for ox-tools workspace lint extras
- Bump rustfmt max_width to 140
- File/region removal: handle catalog drops and disabled backends
- setup-action: install libclang on Linux; install rustfmt/clippy components
- InSync refreshes manifest L so stale-L from older binary versions self-heals
- Fix cargo-mutants --in-diff usage; tolerate per-tool install failures
- Matrix-input plumbing: hardcoded OS matrices, per-leg runner labels only
  (rejected fromJSON(inputs.X) form -- silent empty-matrix failure mode)
- ADO backend: per-job wrapper template (steps/job.yml) for 1ESPT
  extensibility -- adopters edit the wrapper to plug in templateContext
  blocks; the dirty-file flow then preserves their edits

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Two Linux-only CI failures (PowerShell array-unwrap, cargo-delta name
  format mismatch for hyphenated packages)
- setup-action: save cache even on job failure (deprecated save-always
  replacement)
- Optional cargo-binstall installer with per-tool cargo-install fallback
- License header exclusions for vendored files
- Per-job cache keys + drop --workspace from more recipe tools
- Skip re-install when tool already present at sufficient version
- Justfile case fixes: lowercase justfile -> canonical "Justfile" with
  case-aware migration
- Cache cargo's installed-tools registry (.crates.toml) too
- doc2readme: add --workspace + regenerate READMEs
- Spellcheck infrastructure: preprocess .spelling -> .dic, pass
  `--cfg spellcheck.toml`, skip llvm-cov on Windows ARM, mkdir
  target/coverage
- Dictionary buildup: technical jargon, lowercase variants for
  case-sensitive merge, intra-doc link backticking

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
martin-kolinek and others added 15 commits June 15, 2026 14:18
- Trigger CI with conventional-commits PR title check
- ox-check-deny: dedupe [licenses] + bump unmaintained syntax
- ox-check-udeps: drop unused deps surfaced by cargo-udeps
- ox-check-semver-check: filter bin-only crates from --package; tolerate
  unpublished + bin->lib baselines
- ox-check-external-types: per-manifest invocation (no --package);
  invoke via cargo +nightly; tolerate rustdoc JSON schema drift
- ox-check-aprz: use 'deps' subcommand, not 'check'
- setup: install nightly toolchain explicitly
- Test fixes for ox-check mutation-testing coverage
- Iterate group placement: try moving external-types and udeps to
  scheduled-advisories for speed, then revert udeps/semver-check to
  pr-fast (acceptable PR-tier cost)
- GitHub: pass GITHUB_TOKEN to per-group composite action for cargo-aprz

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…zation

- Pin nightly toolchains via versions.just; drop schema-mismatch tolerance
  (the pin makes drift impossible)
- Ship default spellcheck.toml as a managed-region file
- Switch ox-check-fmt to pinned nightly rustfmt with unstable opinions;
  bulk-format the codebase with that pin
- Tighten ox-check lint catalog: Bucket A folding + contested-lint
  removals; drop empty_structs_with_brackets cohabit lint
- Add rust.unexpected_cfgs to lint catalog (coverage cfgs)
- Ship managed-region clippy.toml; attribute wildcard_imports bug
  workaround
- Bump rust_nightly pin to nightly-2026-02-10 to match oxidizer

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Strip pre-existing spellcheck.toml content that collides with managed region
- Fix stale snapshots + spellcheck typos from recent commits
- ADO impact.yml needs setup.yml to bootstrap cargo
- Cross-distro libclang install in setup.yml
- Env-var form for impact baseline ref selection
- Skip LFS smudge in impact baseline worktree
- Validate impact package names; drop unknowns
- Manifest mutation tests (double-newline guard, loop-bound progress)
- Unify semver-check workspace path with per-package tolerance
- Add ox-check-system-deps-check: probe for system-level libs (libclang)
  with per-OS install hints
- Rename "nightly" tier to "scheduled" (PR #25 review feedback)
- Per-OS impact stages to fix cfg-conditional dep gaps (impact-linux +
  impact-windows; each downstream leg consumes its OS's impact set)
- Local-vs-CI parity for readme-check + impact validation

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- cargo-heather: clarify ox-gen-readme opt-out reason; generate README via
  doc2readme so the README stays in sync with the crate-level rustdoc
- Recipe-level cleanup: TODO on every OX_CHECK_INCLUDE recipe to factor
  out the preamble; inline `?? --workspace` splat; prune TODOs that turn
  out to be misplaced
- Advisory PR comments for non-blocking findings (semver-check). Recipes
  exit 0 and write a markdown body to target/ox-check/comments/<NAME>.md
  when there are findings, remove the file when clean. GH wiring uses
  marocchino/sticky-pull-request-comment; ADO wiring uses a pwsh step
  against the ADO REST API. Skipped silently on builds without
  Contribute-to-PRs permissions
- Extend spellcheck tokenization_splitchars to cover typographic
  punctuation (em-dash, en-dash, arrows) so prose with em-dashes
  tokenises correctly across cargo-spellcheck versions

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Close two methodology gaps vs legacy CI (udeps --all-targets behaviour,
  external-types pinned-nightly enables PR-tier inclusion)
- Rename pr-mutants -> pr-slow; add miri + careful to PR tier
- Unified ox-check-setup recipe; install rustup components alongside
  cargo tools
- Pin exact tool versions on install; accept newer if already present
- Address real bugs surfaced by ox-check (walkdir unused, external-types
  API leaks: clap_builder::Error in cli, ohno::AppError in
  automation::run_cargo)
- Bump cargo-heather pin (0.1.0 -> 0.2.1) since 0.1.0 fails to build from
  source on current Rust
- miri exclusions for FS-heavy tests; pass `--no-tests=pass` to miri
  nextest only (not llvm-cov)
- Add mermaid diagrams to checks.md / github.md / ado.md

PR-slow CI structure:
- Merge pr-test into pr-slow with three sub-recipes (slow1=tests+coverage,
  slow2=miri+careful, slow3=mutants)
- Split pr-slow into three parallel CI jobs/stages (was: one sequential
  job per OS leg). Per-leg wall-clock drops from sum(slow1,slow2,slow3)
  to max(slow1,slow2,slow3). Umbrella `ox-check-pr-slow` just recipe
  preserved for local convenience

CI diagram iteration: per-pipeline diagrams (PR + scheduled per backend),
LR layout, single vertical column for jobs, multiple subgraph/column/ELK
experiments converging on plain flowchart LR without invisible chains.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Diagram polish: - Spell out matrix legs (linux, windows, linux-arm, windows-arm) instead   of "4-leg matrix"; drop tool lists from job labels (recipe column is   source of truth) - Promote coverage upload to first-class node (codecov action / ADO   PublishCodeCoverageResults@2) - Coverage upload: every leg except windows-arm (not linux-only).   Single-leg coverage misses OS/arch-gated code; codecov coalesces   multiple uploads against the same commit - Per-caller setup nodes so the ox-check-setup invocation is visible in   every per-group composite/step template chain - Drop self-skip notes on pr-mutants node labels; drop verbose legends and   prose paragraphs; simplify external-action labels - Add cargo delta as recipe node under impact; GH ox-check-impact now   goes through ox-check-setup so cargo-delta picks up the catalog-pinned   version (matches ADO behaviour)  Naming reorg: - Rename pr-slow{1,2,3} to pr-{test,runtime-analysis,mutants}; rename   ox-check-mutants check to ox-check-mutants-diff (parallels existing   ox-check-mutants-full) - Rename cargo-ox-check -> cargo-ox-check-update; flatten the CLI (drop   the redundant `update` subcommand since update is the only action) - Rename crate dir crates/cargo_ox_check_update -> crates/cargo-ox-check-update   to match the hyphen convention used by cargo-coverage-gate and   cargo-heather  checks.md diagram: break out per-check nodes into a third layer, surface the ox-check alias, add ox-check- prefix to group labels, surface the pr-slow umbrella as a sibling of pr-fast.  Other: - Drop misleading "oxidizer-github also uploads from a single canonical   leg" comments (oxidizer-github actually uploads from three legs); the   one-leg shape was an ox-check design simplification, then reverted - Drop orphan job.yml node from ADO PR diagram (its dotted "wraps via"   edges were removed earlier and the node had no remaining edges) - Tighten diagram spacing via flowchart init config (nodeSpacing,   rankSpacing, padding); bump font size on GH/ADO diagrams - Normalize crates/automation/Cargo.toml ox-check-lints managed region   (drop trailing blanks that had caused a silent opt-out)  Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the monolithic ox-check-setup + tool-minimums.txt /

rustup-components.txt data files with a per-tool, per-component,

per-toolchain install + validate-prereqs recipe layer in tools.just,

composed into per-check, per-group, per-tier, and global setup recipes.

All version pins now live as plain just variables in versions.just

(single source of truth, no sidecar data files).

CI: GH composite ox-check-setup and ADO setup.yml take a 'group' input

/ parameter; per-group composites pass their own group name so a

pr-fast leg never installs cargo-mutants. Special value 'none' skips

tool install entirely (used by impact, which only needs cargo-delta).

Naming: ox-check-tool-<bin>-install, ox-check-component-<tc>-<c>-install,

ox-check-toolchain-<sym>-install at the atomic layer;

ox-check-<check|group|tier>-setup at composition layers.

Mirror ox-check-<x>-validate-prereqs recipes at every layer.

Each check recipe depends on its matching -validate-prereqs (no more

_ox-check-require). Failure policy: no tolerance -- a failed install

fails the recipe (cargo-mutants ARM-Windows self-skip lives in the

install body).

Docs: local.md, github.md, ado.md, design.md updated to match.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
RUSTSEC-2026-0035 (pingora-cache) uses CVSS 4.0; cargo-audit 0.21.0

fails to parse the advisory database with 'unsupported CVSS version: 4.0'.

0.22.2 (released 2026-06-05) adds CVSS 4.0 support.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ox-check-system-deps-check only looked under /usr/lib/x86_64-linux-gnu

for libclang. On ubuntu-24.04-arm runners the matching path is

/usr/lib/aarch64-linux-gnu/, so the probe reported libclang missing

even after apt-get install libclang-dev had put it there.

Add aarch64-linux-gnu to the explicit-probe list and to the

version-suffix glob fallback.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
cargo-spellcheck 0.15.1 no longer builds from source on Rust 1.93 (the
locked ra_ap_stdx triggers E0804 'cannot add auto trait Send to dyn
bound via pointer cast'), which broke the linux-arm pr-fast leg where
binstall has no ARM prebuilt and falls back to source. Bump to 0.15.7;
the em-dash word-boundary regression that motivated the original 0.15.1
pin is already mitigated by the typographic-punctuation entries we
appended to tokenization_splitchars in spellcheck.toml's managed region
(Phase 7.5).

Separately, six tests in cargo-coverage-gate's workspace module stacked
two #[cfg_attr(miri, ignore = ...)] attributes per test (one citing
filesystem use, one citing cargo metadata subprocess). Newer rustc
(nightly used by extended-analysis) treats the second ignore as
redundant and emits 'unused attribute' under -D warnings, failing the
legacy extended-analysis workflow. Collapsed each pair into a single
attribute with a combined reason.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Same pattern as the workspace.rs fix: from_path_reads_from_disk had two
stacked #[cfg_attr(miri, ignore = ...)] attributes (lines 166 and 168
straddling the #[test] attribute). Newer rustc emits 'unused attribute'
on the second one. Collapsed into one with a combined reason.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Full rebrand of the unified-builds tool from cargo-ox-check-update /
ox-check-* to cargo-anvil / anvil-*. Scope:

- Crate / binary: crates/cargo-ox-check-update -> crates/cargo-anvil;
  Cargo name 'cargo-ox-check-update' -> 'cargo-anvil'; clap bin_name
  'cargo ox-check-update' -> 'cargo anvil'. The subcommand token cargo
  injects is now 'anvil' and parse_from_cargo_args strips that.
- Just recipe namespace: every ox-check-* recipe (groups, tiers,
  per-check, per-tool *-install / *-setup / *-validate-prereqs) renamed
  to anvil-*. 'just anvil' aliases anvil-pr.
- Directories: justfiles/ox-check/ -> justfiles/anvil/ (both workspace
  and templates); .github/actions/ox-check-* -> .github/actions/anvil-*;
  .github/workflows/ox-check-pr*.yml + ox-check-scheduled*.yml renamed
  to anvil-pr*.yml + anvil-scheduled*.yml.
- Lockfile: .ox-check.lock -> .anvil.lock.
- Env vars: OX_CHECK_* -> ANVIL_*; ADO pipeline variable
  ox_check_rustc_version -> anvil_rustc_version.
- Managed-region marker strings: '# managed by ox-check' -> '# managed
  by cargo anvil' (visible to every consuming repo on next regen).
- Docs / README: design/, implementation-plans/, verification.md, all
  templates, snapshots regenerated.

Verified locally: cargo check + 199 lib tests + 4 schema tests + 3
snapshot tests pass; 'cargo anvil' is idempotent on this workspace
(31 items, 0 changes); 'just format' clean. The tests/update.rs
integration binary triggers a Windows local-only UAC heuristic on
the developer machine (filename starts with 'update'); CI runs it
fine. Skipping pr-fast locally because cargo-aprz network appraisal
runs >30min; CI is faster.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The rename to cargo-anvil is a deliberate brand choice (vs. the prior
descriptive cargo-ox-check-update name). Add a one-paragraph note at
the top of design.md explaining the forge metaphor, isolated so it
doesn't leak into the user-facing surfaces.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Install a prebuilt cargo-binstall binary up front via the official

action so the bootstrap branch in _install-tool (which runs

'cargo install --locked cargo-binstall' from source, ~4 min per

cold-cache job) becomes a no-op on GH. Also opportunistically use

binstall for 'just' itself with cargo-install fallback.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Addresses Sander's review feedback on PR #33: 'CI' is a working

methodology, not a synonym for automation pipelines. Adopt

'cloud workflows' (neutral across GH Actions and ADO pipelines)

throughout cargo-anvil docs, source comments, template comments,

and Cargo.toml description/keywords.

Scope: cargo-anvil only. No code identifiers changed. Pre-existing

ox-tools repo docs (top-level README.md, DEVELOPMENT.md) and other

crates' READMEs left alone since they predate cargo-anvil and were

not part of Sander's review.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
martin-kolinek and others added 2 commits June 15, 2026 14:35
Two follow-ups after rebasing on origin/main:

1. Regenerate crates/cargo-anvil/README.md — the prior CI -> cloud

   workflows rename in 71ad75c edited lib.rs doc-comments but didn't

   re-run doc2readme, so the README drifted.

2. Opt cargo-heather out of anvil-readme-check via

   [package.metadata.ox-gen-readme] disable = true. cargo-heather

   ships a hand-curated README with its own logo and layout and is

   already excluded from the legacy 'just readme'/'readme-check'

   recipes via hardcoded '--ignore cargo-heather'. anvil-readme-check

   supports the per-crate opt-out documented at checks.just:215.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Reverts the [package.metadata.ox-gen-readme] disable opt-out added

in 8639a43 (incorrect: 7a3dcd4 on main intentionally moved

cargo-heather to the shared doc2readme pipeline). The real root

cause was stale docs.rs URLs in README.md: PR #40 bumped the crate

to v0.3.0 but didn't regenerate the README. main's legacy

'just readme-check' skips cargo-heather via hardcoded

--ignore cargo-heather so the drift went undetected;

anvil-readme-check (which has no per-crate skip-list) correctly

flagged it.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@martin-kolinek
martin-kolinek marked this pull request as ready for review June 15, 2026 14:40
Copilot AI review requested due to automatic review settings June 15, 2026 14:40

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.

Copilot wasn't able to review this pull request because it exceeds the maximum number of lines (20,000). Try reducing the number of changed lines and requesting a review from Copilot again.

@martin-kolinek
martin-kolinek enabled auto-merge (squash) June 15, 2026 16:16

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Awww, I like the new name :)

@martin-kolinek
martin-kolinek merged commit e5d5a3d into main Jun 16, 2026
44 checks passed
@martin-kolinek
martin-kolinek deleted the unified-builds-impl branch June 16, 2026 08:28
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.

5 participants