Skip to content

build: adopt cargo-anvil check catalog (github backend)#534

Draft
martin-kolinek wants to merge 18 commits into
mainfrom
unified-builds-3
Draft

build: adopt cargo-anvil check catalog (github backend)#534
martin-kolinek wants to merge 18 commits into
mainfrom
unified-builds-3

Conversation

@martin-kolinek

Copy link
Copy Markdown
Collaborator

Summary

Adopt cargo-anvil (unified-build check catalog, GitHub backend) into oxidizer, reconciling pre-existing tool configs and closing the gaps that surfaced on Windows local runs. Draft for review while the remaining slow-tier validation completes.

What's included

  • Structural adoption of cargo-anvil (github backend), reconciling pre-existing clippy/rustfmt/spellcheck/deny configs into anvil-managed regions; cargo-sort applied; internal crates opted into permissive external-types; general nightly bumped to 2026-05-30.
  • Loom detection via the new convention: multitude declares a loom marker feature + [[test]] required-features = ["loom"]; anvil runs only loom targets with --all-features --test-threads=1 under --cfg loom (all 20 tests pass). Restores loom coverage the old loom_*.rs glob had silently dropped.
  • Per-config coverage exports to fix the Windows llvm-cov export 32 KB command-line overflow (os error 206): one self-contained run per feature config, merged at the lcov level by cargo-coverage-gate 0.2.0; Codecov uploads both per-config lcovs.
  • Coverage-gate reconciliation so the per-package gate matches Codecov intent: testing_aids + ohno/test_util.rs use coverage(off) (replacing codecov.yml ignores; macro logic moved to a #[track_caller] helper); cachet's untestable no-logs telemetry no-ops use coverage(off); no-data crates (automation, fundle, testing_aids) opt out via min-lines-percent = 0. Gate now reports "all packages meet their threshold".

Validation

Fast tier green. Slow tier: doc-test/examples/miri/careful green; loom green; coverage gate green. bolero self-skips on Windows (Linux-only); mutants not yet run.

Notes

  • ohno gains a doc-hidden assert_error_message_impl helper (additive, non-breaking) backing the existing macro.
  • coverage measurement validated on Windows; CI runs coverage on Linux.

@codecov

codecov Bot commented Jun 29, 2026

Copy link
Copy Markdown

Codecov Report

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

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #534   +/-   ##
=======================================
  Coverage   100.0%   100.0%           
=======================================
  Files         354      353    -1     
  Lines       26897    26885   -12     
=======================================
- Hits        26897    26885   -12     
Flag Coverage Δ
linux 100.0% <100.0%> (?)
linux-arm 100.0% <100.0%> (?)
windows 100.0% <100.0%> (?)

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 Adopt cargo-anvil check catalog (github backend) build: adopt cargo-anvil check catalog (github backend) Jun 29, 2026
@martin-kolinek martin-kolinek force-pushed the unified-builds-3 branch 3 times, most recently from b68c23e to 8942a2f Compare June 30, 2026 16:20
martin-kolinek and others added 18 commits July 1, 2026 12:12
Run `cargo anvil` (github backend) to adopt the unified-build check catalog:
generate justfiles/anvil/ + .github/ (actions + workflows) trees, .anvil.lock,
and the rustfmt/spellcheck/clippy/deny/.delta config hosts.

Reconcile oxidizer-github's pre-existing hand-rolled lint setup:
- Root Cargo.toml: replace the manual [workspace.lints.rust/clippy/rustdoc]
  tables with anvil's managed catalog region; preserve the repo's unique lint
  deltas (missing_docs, unreachable_pub, clippy.panic,
  clippy.empty_structs_with_brackets) as dotted-key continuations outside the
  sentinels. Take ownership of the region's unexpected_cfgs check-cfg to add
  cfg(utc_backend), which this workspace requires (documented anvil mechanism).
- 38 crates: drop the manual `[lints] workspace=true` now provided by anvil's
  managed region (removes duplicate [lints] tables that broke TOML parsing).

Validated: cargo metadata parses; `cargo anvil --dry-run` clean (the
workspace-lints region is intentionally adopter-owned).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
oxidizer-github already had curated clippy.toml / rustfmt.toml / spellcheck.toml
/ deny.toml, which collided with anvil's injected regions:
- clippy.toml: drop 6 manual keys now owned by anvil's region (exact dupes).
- rustfmt.toml: drop the redundant standalone max_width (region provides it).
- spellcheck.toml: drop the manual block; anvil's region is a superset
  (adds tokenization_splitchars).
- deny.toml: merge. Investigation showed anvil's catalog config breaks two things
  here: it rejects CDLA-Permissive-2.0 (Mozilla CA data via webpki-root-certs,
  explicitly allowed by this repo) and its bans wildcards="deny" fails ~14 internal
  crates that use wildcard versions. Keep the repo's curated [licenses] (incl.
  BSL-1.0/CDLA-Permissive-2.0, confidence 0.8) and [advisories].ignore; add anvil's
  [sources] and advisory yanked/unmaintained; set [bans] wildcards="warn".

Verified: cargo deny check passes (advisories/bans/licenses/sources all ok);
fmt --check clean. workspace-lints and deny regions are intentionally adopter-owned.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Resolve the single clippy::let_and_return the anvil catalog surfaces; the repo
already enforced the rest of the catalog lints.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sort dependency tables and apply cargo-sort formatting across the workspace to
satisfy anvil-cargo-sort. cargo-sort relocates the adopter-owned
[workspace.lints] table (it moves it ahead of [profile.*] and controls the
sentinel placement); the lints remain fully functional and the region was
already adopter-owned. Per-crate [lints] regions are format-stable.

Note: anvil's --check-format cargo-sort check is not fully compatible with the
sentinel-delimited workspace-lints region (upstream issue); accepting
cargo-sort's layout keeps the check green while preserving all lints.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…xt-types pin)

Regenerate using the anvil-multiple-regions cargo-anvil build:
- deny.toml now uses 4 granular managed regions (advisories/licenses/bans/
  sources) instead of one. Keep `sources` anvil-managed; take ownership of
  advisories (repo RUSTSEC ignores), licenses (repo allowlist incl. BSL-1.0 /
  CDLA-Permissive-2.0, confidence 0.8), and bans (wildcards="warn").
- versions.just: external-types pin bumped to nightly-2026-03-20 /
  cargo-check-external-types 0.5.0 (MSRV 1.93 support).
- tools.just / checks.just: pick up msrustup-safe component detection and
  impact/libclang fixes from the branch.
- .gitattributes: anvil-managed region added.

Verified: cargo deny check passes (advisories/bans/licenses/sources all ok).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
anvil-external-types checks every library crate, but automation and
testing_aids (publish=false, in automation::INTERNAL_CRATES) had no
[package.metadata.cargo_check_external_types] opt-in, so cargo-check-external-types
denied their legitimate internal use of ohno/serde/opentelemetry/tracing.

Opt both into the check permissively with allowed_external_types = ["*"], matching
their internal, non-public-API status. anvil-external-types now passes.

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

Regenerate from anvil-multiple-regions, which bumps rust_nightly
nightly-2026-02-10 -> nightly-2026-05-30 (matching the repo's constants.env
RUST_NIGHTLY).

The bump alone does not fix anvil-llvm-cov: coverage_attribute is still
unstable on rustc 1.98. The real cause is a latent gate inconsistency in the
automation crate:
  #![cfg_attr(all(coverage_nightly, test), feature(coverage_attribute))]  // test only
  #![cfg_attr(coverage_nightly, coverage(off))]                           // all builds
Under --workspace coverage, automation's plain (non-test) lib is built with
--cfg coverage_nightly, so coverage(off) applies without the feature gate ->
E0658. The repo's own `-p automation` coverage only builds the test target, so
it never hit this.

Gate the feature on coverage_nightly alone, matching bytesbuf/ohno/multitude
which also apply crate-level coverage(off). automation now compiles under
coverage.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Re-regenerate the anvil catalog against the latest anvil-multiple-regions,
which detects loom tests authoritatively via a `[[test]]` target declaring
`required-features = ["loom"]` (cargo metadata), runs only those targets under
`--cfg loom` with `--all-features --test-threads=1`, and fails loud if a crate
declares loom support but exposes no such target.

Update crates/multitude to match the new convention:
- add an empty `loom` marker feature (selection only; the build is still
  driven by `--cfg loom` via the existing `target.'cfg(loom)'` tables)
- declare `[[test]] name = "loom" path = "tests/loom.rs"
  required-features = ["loom"]`

This restores real loom coverage (the previous `just loom` glob no longer
matched the consolidated tests/loom.rs and silently no-op'd in CI). All 20
loom tests pass under `just anvil-loom`.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Re-regenerate against the latest anvil-multiple-regions, which fixes the
Windows `llvm-cov export` command-line overflow (os error 206) by exporting
coverage per feature config instead of from one merged report:

- anvil-llvm-cov now runs `--all-features` and `--no-default-features` as
  separate self-contained llvm-cov runs (clean between), each exporting only
  its own config's objects (~half) -> stays under the 32 KB Windows cmdline
  limit. The two per-config lcovs are reconciled downstream.
- cargo-coverage-gate pin bumped 0.1.0 -> 0.2.0, which accepts multiple
  `--lcov` files and merges them at the line level (the gate).
- Codecov uploads now reference the two per-config lcov files
  (lcov-all-features.info, lcov-no-default.info); Codecov coalesces them.
- impact action.yml picks up upstream baseline-resolution fixes (shared
  base-ref resolver, active-toolchain override, first-adoption fallback).

Verified on Windows: `just anvil-llvm-cov` now completes with zero os-error-206
(both configs export lcov + cobertura). Remaining gate threshold failures
(testing_aids, ohno, cachet) are pre-existing per-package coverage gaps newly
surfaced by the gate, not overflow.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
cargo-coverage-gate (the per-package line-coverage gate anvil runs) evaluates
raw lcov and does not read codecov.yml, so files the repo excludes from Codecov
via `ignore` globs were still gated and failed. Translate the two ignores into
mechanisms the gate honors:

- testing_aids: add crate-level `#![cfg_attr(coverage_nightly, coverage(off))]`
  (+ feature gate). It is an internal (publish=false) test-helper crate whose
  utilities are exercised opportunistically by other crates' tests, so
  per-package coverage is not meaningful. Now reports NO DATA -> OK.

- ohno/src/test_util.rs: the existing module-level coverage(off) was inert for
  `assert_error_message!` because a macro body is re-instrumented at each
  expansion site and attributed back to this file. Move the assertion logic
  into `assert_error_message_impl` (a real `#[doc(hidden)] pub fn`); the macro
  now expands to a single call to it. The function is compiled once in ohno
  where coverage(off) applies, so test_util.rs no longer contributes
  instrumented lines. ohno goes 99.3% -> 100%. The function is `#[track_caller]`
  so assertion failures still report at the caller's line (a plain function
  would point into test_util.rs). Macro behavior is unchanged (verified: ohno +
  http_extensions tests, incl. the should_panic, doctest, and panic location).

- codecov.yml: drop both now-redundant `ignore` entries (the lines no longer
  appear in the lcov at all, so Codecov and the gate agree).

NOTE: ohno is published (crates.io v0.3.6); this adds a doc-hidden public helper
(additive, non-breaking) to back the macro.

Remaining: cachet 99.7% (no-default-features-only telemetry fallback arms) is a
genuine gap, not a codecov ignore -- handled separately.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
`record_{debug,info,error}_with_duration` each contain a
`#[cfg(not(any(feature = "logs", test)))]` no-op block that only exists to
silence unused-variable warnings when logging is compiled out. Under
`--no-default-features` these blocks compile only into non-test builds (notably
cachet's examples) and are never executed, so they surfaced as 9 uncovered
lines under anvil's nextest-based coverage (which instruments example binaries;
oxidizer's libtest `cargo llvm-cov` does not, which is why Codecov shows the
file 100%).

Add `#[cfg_attr(coverage_nightly, coverage(off))]` to the three functions (and
enable `feature(coverage_attribute)` at the crate root) so these untestable
discards are not instrumented. cachet goes 99.7% -> 100%.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
cargo-coverage-gate maps any gated package that produces NO coverage data to
Verdict::ConfigError (exit 2) unless it is explicitly opted out via
`min-lines-percent = 0`. automation (script-only, entirely coverage(off)),
fundle (macro re-exports + marker structs + trait defs, no executable bodies),
and testing_aids (whole crate coverage(off)) all legitimately produce no data,
so the full-workspace gate failed even though every measured package passed.

Add `[package.metadata.coverage-gate] min-lines-percent = 0` to the three so
the gate treats their NO DATA as OK. This is also the marker anvil-llvm-cov's
impact-scoping uses to drop opted-out packages from the measured set.

With this, `cargo coverage-gate` over the per-config lcovs reports "all packages
meet their threshold" (exit 0).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Regenerate against the latest anvil-multiple-regions, which version-qualifies
the impact `--package` specs (`name@version`) to fix the workspace-vs-dependency
ambiguity. The affected-package list previously emitted bare names, so
`cargo <cmd> -p ohno` failed with "specification `ohno` is ambiguous" whenever a
published copy of a workspace crate is also pulled in transitively -- here
`ohno@0.3.5` via thread_aware [dev-dep] -> many_cpus -> many_cpus_impl ->
cpulist. `--package ohno@0.3.8` resolves uniquely to the workspace member.

Recipes that take bare package selectors (e.g. cargo-coverage-gate) strip the
`@version` suffix. Also picks up the upstream msrustup-swappable toolchain
recipe changes. Adopter-owned regions (workspace-lints, deny-*) preserved.

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

anyhow < 1.0.103 has unsound Error::downcast_mut() (UB after Error::context); patched in 1.0.103. Flagged HIGH RISK by anvil-aprz.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pulls the upstream fix (a5d8e0f) that moves impact spec formatting into a shared pwsh recipe (_anvil-impact-format). The previous bash version mis-parsed the version map because IFS=\$'\t' coalesces the empty-lib '\t\t' field, zeroing every package's version and emitting bare -p specs (so cargo -p ohno stayed ambiguous). The pwsh recipe uses ConvertFrom-Json and now emits version-qualified specs (e.g. --package ohno@0.3.8), verified locally.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The macro->function refactor (assert_error_message! -> assert_error_message_impl)
exposed the accepted-continuation || chain as a cargo-mutants target that
macro_rules! bodies had shielded. cargo-mutants reported a surviving ||->&&
mutant on the chain.

Add four direct tests against assert_error_message_impl, each making exactly one
branch of the || chain decisive (the others false), so flipping any || to &&
breaks an assertion. Verified: cargo mutants --package ohno --file
crates/ohno/src/test_util.rs now catches all 5 mutants (was 1 missed).

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

The per-package coverage gate flagged fundle_macros (66.7%) and fundle_macros_impl
(99.8%) under impact-scoped cargo llvm-cov nextest, while a --workspace run
reported 100%. Root cause: proc-macro coverage is captured at compile time of the
expanding consumer, and which integration tests run under impact-scoped nextest is
non-deterministic (two identical full-scope runs executed fundle's integration
tests 14 times vs 0 times), so proc-macro line coverage flickers run-to-run.

- fundle_macros: its three #[proc_macro_attribute] entry points (bundle/deps/
  newtype) are thin shims that delegate to fundle_macros_impl and cannot be
  exercised by a runtime test; their only coverage is non-deterministic
  compile-time expansion. Mark them #[cfg_attr(coverage_nightly, coverage(off))]
  so they are excluded from instrumentation, and opt the crate out of the
  per-package line gate via min-lines-percent=0 (same convention as the fundle
  facade crate).
- fundle_macros_impl: add direct lib unit tests for the named-fields / tuple-struct
  / field-count error branches (bundle/deps/newtype) that were previously reached
  only by the non-deterministic trybuild compile-fail tests. Lib unit tests run
  deterministically, so these branches are now reliably covered.
- fundle: add a runtime test of #[fundle::newtype] exercising the generated
  From/Deref/DerefMut, which the trybuild compile-test does not verify at runtime.

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

Pull latest cargo-anvil (fc9160a) and regenerate. Key upstream fixes:
- anvil-miri now runs via libtest (cargo miri test --all-features --tests)
  instead of cargo miri nextest run. Under miri, nextest's process-per-test
  model multiplies the (expensive) per-process interpreter startup; libtest
  runs all tests of a binary in one miri process. This was the main PR
  wall-time regression vs the repo's existing extended-analysis miri job
  (which already used cargo miri test): ~80min -> expected ~42min on Windows.
- anvil-careful now tracks the careful-sysroot build identity (nightly rustc
  -vV + cargo-careful version) in target/anvil/careful-sysroot.id and runs
  cargo clean when it changes, fixing the stale-sysroot E0463/metadata errors
  (cargo can't see the in-place sysroot rebuild behind the stable cache path).

Also adds the anvil-managed [lints] region to the new crate plurality
(removing its manual [lints] workspace=true to avoid a duplicate-key error).

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.

1 participant