From 311f6f2ae5bbb8d4552cb229b1cfcc8cf57a4a71 Mon Sep 17 00:00:00 2001 From: "rearden-grok[bot]" <317016512+rearden-grok[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 19:07:01 -0700 Subject: [PATCH] ci: shard PR cargo-mutants next to coverage A ~300-mutant in-diff at -j 2 needs about 50 minutes, and the single mutants-pr job was canceled at 30. Four shards start after fmt, clippy, and test, in parallel with coverage, and stay advisory. Weekly sweep is unchanged. --- .github/workflows/ci.yml | 103 +++++++++++++++++++++++++++++++++- .github/workflows/mutants.yml | 31 +--------- CHANGELOG.md | 18 ++++-- TESTING.md | 2 +- 4 files changed, 115 insertions(+), 39 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 543d3d950..e607f1a7d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,14 +24,15 @@ env: # every GitHub Actions “stable” bump. Bump intentionally with the toolchain. # # Each gate is its own check. Short ubuntu gates share one runner so -# concurrent-job slots stay free for clippy, test, and future cargo-mutants -# shards (mutants.yml): +# concurrent-job slots stay free for clippy, test, coverage, and the PR +# mutants shards: # fmt → deny → ast-grep → nixos-module-eval # Later gates use !cancelled() so a red earlier gate still reports its own # check (a skip would hide required deny / ast-grep). # clippy, test, windows, and macos start immediately. clippy stays parallel: # coverage waits on it, and a cold rust-cache is a full workspace compile. -# coverage waits for fmt / clippy / test. +# coverage and the advisory mutants shards wait for fmt / clippy / test. +# Weekly full mutants sweep stays mutants.yml. # nixos-module-runtime is a labeled job (nixos-module-runtime.yml), not this file. # Operator binaries are GitHub Releases (release.yml), not this workflow. # @@ -204,6 +205,102 @@ jobs: - name: store + node smoke run: ./scripts/ci-os-smoke.sh + # Crates, Cargo manifest/lock, or these workflow files. Docs-only PRs + # skip the four shard runners. Starts immediately; shards still wait for test. + mutants-diff: + if: github.event_name == 'pull_request' + runs-on: ubuntu-latest + timeout-minutes: 5 + outputs: + rust: ${{ steps.d.outputs.rust }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 + - name: rust or workflow diff + id: d + env: + BASE_REF: ${{ github.base_ref }} + run: | + set -euo pipefail + git diff "origin/${BASE_REF}".. --unified=0 -- \ + crates Cargo.toml Cargo.lock \ + .github/workflows/ci.yml .github/workflows/mutants.yml \ + > git.diff + if [[ -s git.diff ]]; then + echo "rust=true" >> "$GITHUB_OUTPUT" + else + echo "rust=false" >> "$GITHUB_OUTPUT" + fi + + # PR in-diff, four shards, same window as coverage. Advisory: a missed + # mutant does not fail the job. One ~300-mutant diff at -j 2 is ~50m and + # was canceled at 30m. Weekly sweep stays mutants.yml. Owner: TESTING.md. + mutants: + name: mutants (${{ matrix.shard }}/4) + needs: [fmt, clippy, test, mutants-diff] + if: >- + github.event_name == 'pull_request' + && needs.fmt.result == 'success' + && needs.clippy.result == 'success' + && needs.test.result == 'success' + && needs.mutants-diff.result == 'success' + && needs.mutants-diff.outputs.rust == 'true' + runs-on: ubuntu-latest + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + shard: [0, 1, 2, 3] + env: + # Workflow env disables incremental for rust-cache and denies warnings. + # cargo-mutants rebuilds one mutant at a time; incremental is the point. + # Weekly mutants.yml does not pass -D warnings. A warning-only build + # failure would look caught here and missed on Sunday. + CARGO_INCREMENTAL: "1" + CARGO_TERM_COLOR: never + RUSTFLAGS: "" + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 + - uses: dtolnay/rust-toolchain@a5f673d0ba8626c3977bb416a1612774bc82181b # rustc 1.95.0 + - uses: taiki-e/install-action@9534c84618278caac52cb373bb164ed464dbd8af # v2.87.11 + with: + tool: cargo-mutants@27.1.0 + - name: in-diff shard + id: run + continue-on-error: true + env: + BASE_REF: ${{ github.base_ref }} + run: | + set -euo pipefail + git diff "origin/${BASE_REF}".. --unified=0 > git.diff + set +e + cargo mutants --workspace --in-diff git.diff \ + --shard "${{ matrix.shard }}/4" --sharding slice -j 2 \ + > mutants-shard.log 2>&1 + ec=$? + set -e + missed=$(grep -c '^MISSED' mutants-shard.log || true) + echo "missed=${missed}" >> "$GITHUB_OUTPUT" + cat mutants-shard.log + exit "$ec" + - name: summary + if: always() + run: | + { + echo "### mutants ${{ matrix.shard }}/4" + echo "Advisory (not a merge gate). Outcome: ${{ steps.run.outcome }}. Missed: ${{ steps.run.outputs.missed }}." + } >> "$GITHUB_STEP_SUMMARY" + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + if: always() + with: + name: mutants-pr-${{ matrix.shard }} + path: mutants.out + if-no-files-found: ignore + retention-days: 5 + # Line-coverage 92% floor + CRAP --fail-above 30 (allowlist in .cargo-crap.toml). # Slow; waits for the fast gates so a red fmt/clippy/test does not start # this job. Required. Job+step timeouts so a hung llvm-cov test binary diff --git a/.github/workflows/mutants.yml b/.github/workflows/mutants.yml index 6dc0e50c9..f6bcf88d7 100644 --- a/.github/workflows/mutants.yml +++ b/.github/workflows/mutants.yml @@ -1,4 +1,5 @@ -# Weekly cargo-mutants + PR in-diff advisory. Not a required PR check. +# Weekly cargo-mutants. Not a required check. +# PR in-diff shards are ci.yml job `mutants` (parallel with coverage). # Must pass --workspace: default-members is rbitcoin-node only. # Owner: TESTING.md (Mutation testing). Snapshot lists: docs/mutants/. @@ -7,40 +8,12 @@ on: schedule: - cron: "0 3 * * 0" workflow_dispatch: - pull_request: - paths: - - "Cargo.toml" - - "Cargo.lock" - - "crates/**" - - ".github/workflows/mutants.yml" permissions: contents: read jobs: - mutants-pr: - if: github.event_name == 'pull_request' - concurrency: - group: mutants-pr-${{ github.ref }} - cancel-in-progress: true - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - submodules: false - fetch-depth: 0 - - uses: dtolnay/rust-toolchain@a5f673d0ba8626c3977bb416a1612774bc82181b # rustc 1.95.0 - - uses: taiki-e/install-action@9534c84618278caac52cb373bb164ed464dbd8af # v2.87.11 - with: - tool: cargo-mutants@27.1.0 - - run: git diff origin/${{ github.base_ref }}.. --unified=0 > git.diff - - run: cargo mutants --workspace --in-diff git.diff -j 2 - continue-on-error: true - mutants-weekly: - if: github.event_name != 'pull_request' runs-on: ubuntu-latest timeout-minutes: 360 strategy: diff --git a/CHANGELOG.md b/CHANGELOG.md index 814a5fd99..d031685a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,15 +33,21 @@ before 1.0). ### Changed +- **PR cargo-mutants is 4 in-diff shards:** `ci.yml` `mutants`, after + fmt/clippy/test, in parallel with coverage. Advisory + (`continue-on-error`), not a merge gate. One 30-minute job was canceled + on a ~300-mutant diff (`-j 2` is about 50 minutes). Weekly 8-shard sweep + is unchanged. + - **CI short gates share one runner:** `fmt` → `deny` → `ast-grep` → `nixos-module-eval`. clippy, test, windows, and macos still start - immediately. Concurrent-job slots stay free for future cargo-mutants - shards. + immediately. Concurrent-job slots stay free for coverage and the PR + mutants shards. -- **Weekly cargo-mutants:** `mutants.yml` — PR `--workspace --in-diff` - advisory (`continue-on-error`); Sunday 8-shard `--workspace` sweep. - Must use `--workspace` (`default-members` is node). Snapshot lists: - [`docs/mutants/`](docs/mutants/). How to run: [`TESTING.md`](TESTING.md). +- **Weekly cargo-mutants:** `mutants.yml` — Sunday 8-shard `--workspace` + sweep. Must use `--workspace` (`default-members` is node). Snapshot + lists: [`docs/mutants/`](docs/mutants/). How to run: + [`TESTING.md`](TESTING.md). - **Electrum/Esplora no longer require `--sh-index` to bind.** Address and scripthash methods return `scripthash index disabled` (Electrum JSON-RPC diff --git a/TESTING.md b/TESTING.md index ee487dce2..2efdcf5b1 100644 --- a/TESTING.md +++ b/TESTING.md @@ -254,7 +254,7 @@ matches scalar in default tests). Owner: [`docs/quality.md`](./docs/quality.md). | **cargo-crap** | After LCOV, `./scripts/coverage.sh` calls `./scripts/coverage-crap.sh` (skip if `cargo-crap` missing). `--fail-above --threshold 30`; `.cargo-crap.toml` allowlists today's production CRAP>30 functions (remove a name when it scores ≤30). Dry-run: `CRAP_DRY_RUN=1 ./scripts/coverage-crap.sh`. Self-test: `./scripts/coverage-crap.test.sh` | Rides required `coverage`. No `--fail-regression` (llvm-cov coverage % jitters per function) | | **coverage ignore / badge** | `./scripts/coverage.test.sh` (filename ignore, Tier A IBD not skipped, 92% floor, Shields JSON). Publish dry-run: `BADGE_DRY_RUN=1 ./scripts/publish-coverage-badge.sh` | `test` job self-test; `coverage` job writes `coverage/badge.json` and, on green `master`, pushes `badges/coverage.json` | | **Miri** | `./scripts/miri.sh` → `cargo +nightly miri test -p rbitcoin-primitives`. Dry-run: `MIRI_DRY_RUN=1 ./scripts/miri.sh`. Self-test: `./scripts/miri.test.sh` | Nightly `miri.yml` (not required). Never `--workspace` | -| **cargo-mutants** | Must pass `--workspace` (`Cargo.toml` `default-members` is `rbitcoin-node` only; without `--workspace` the list is ~7 mutants). PR advisory: `git diff origin/.. --unified=0 > git.diff` then `cargo mutants --workspace --in-diff git.diff -j 2`. Weekly: `cargo mutants --workspace --shard N/8 -j 2`. Snapshot of missed/timeouts: [`docs/mutants/`](docs/mutants/). | Weekly `mutants.yml` Sunday 03:00 UTC, 8 shards, 360min cap (not required). `workflow_dispatch`. PR job `mutants-pr` is `continue-on-error` (not a merge gate). First Sunday wall-clock is still a measurement. | +| **cargo-mutants** | Must pass `--workspace` (`Cargo.toml` `default-members` is `rbitcoin-node` only; without `--workspace` the list is ~7 mutants). PR: `git diff origin/.. --unified=0 > git.diff` then `cargo mutants --workspace --in-diff git.diff --shard k/4 --sharding slice -j 2` (same flags on every shard). Weekly: `cargo mutants --workspace --shard N/8 -j 2`. Snapshot of missed/timeouts: [`docs/mutants/`](docs/mutants/). | PR: `ci.yml` `mutants`, 4 shards, needs fmt/clippy/test (same window as coverage). `continue-on-error` (not a merge gate). Skipped when the diff does not touch `crates/`, `Cargo.toml`, `Cargo.lock`, `ci.yml`, or `mutants.yml`. Weekly `mutants.yml` Sunday 03:00 UTC, 8 shards, 360min cap (not required). `workflow_dispatch`. First Sunday wall-clock is still a measurement. | Artifact silos above are unchanged: ast-grep / Miri dry-run / crap dry-run do not write `target/`. `mutants.out/` is gitignored.