From c9ad90b52f7a0b75f5bfb47e38bc820d187264ed Mon Sep 17 00:00:00 2001 From: hyperpolymath <6759885+hyperpolymath@users.noreply.github.com> Date: Mon, 6 Jul 2026 17:23:54 +0100 Subject: [PATCH] feat(rust-ci-reusable): real owned coverage gate (llvm-cov + ratcheted floor) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The coverage job was opt-in tarpaulin -> external codecov with NO enforced threshold (just an upload), so 'covered' claims were unbacked estate-wide. Now, when enable_coverage is set, it: - measures line coverage with cargo-llvm-cov (accurate, no external service); - enforces a caller-supplied coverage_floor (new input, default 0 = report-only) and FAILS the job below it — a gate we OWN on our own runner; - reports the % to the job summary. Ratchet model: set coverage_floor at the repo's honest baseline and raise it toward target over time; never lower it. First consumer: hybrid-automation-router (floor 84). This is the banked 'reusable tests for all repos' coverage gate. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/rust-ci-reusable.yml | 41 +++++++++++++++++++------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/.github/workflows/rust-ci-reusable.yml b/.github/workflows/rust-ci-reusable.yml index 8cc9ad52..e1ef5cef 100644 --- a/.github/workflows/rust-ci-reusable.yml +++ b/.github/workflows/rust-ci-reusable.yml @@ -65,10 +65,15 @@ on: required: false default: false enable_coverage: - description: Run `cargo tarpaulin` + upload to codecov (slow; off by default) + description: Measure line coverage with cargo-llvm-cov and enforce `coverage_floor` (off by default) type: boolean required: false default: false + coverage_floor: + description: Minimum line-coverage percent when enable_coverage is set; ratchet upward, never lower. + type: string + required: false + default: "0" clippy_args: description: Args appended to `cargo clippy` type: string @@ -254,8 +259,8 @@ jobs: run: cargo audit coverage: - timeout-minutes: 20 - name: Coverage (tarpaulin + codecov) + timeout-minutes: 25 + name: llvm-cov line coverage runs-on: ${{ inputs.runs-on }} needs: detect if: ${{ inputs.enable_coverage && needs.detect.outputs.has_cargo == 'true' }} @@ -264,6 +269,9 @@ jobs: defaults: run: working-directory: ${{ inputs.working_directory }} + env: + # Ratcheted line-coverage floor (from the caller). Raise toward target; never lower. + FLOOR: ${{ inputs.coverage_floor }} steps: - name: Checkout repository uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 @@ -276,14 +284,25 @@ jobs: uses: dtolnay/rust-toolchain@67ef31d5b988238dd797d409d6f9574278e20537 # stable with: toolchain: stable + components: llvm-tools-preview - - name: Install tarpaulin - run: cargo install cargo-tarpaulin --locked + - name: Install cargo-llvm-cov + run: cargo install cargo-llvm-cov --locked - - name: Generate coverage - run: cargo tarpaulin --out Xml + - name: Measure line coverage + id: cov + # Self-contained: no external coverage service. Own the gate on our runner. + run: | + set -euo pipefail + cargo llvm-cov --workspace --locked --json --output-path cov.json + PCT=$(jq -r '.data[0].totals.lines.percent' cov.json) + printf '### Line coverage: %.2f%% (floor %s%%)\n' "$PCT" "$FLOOR" >> "$GITHUB_STEP_SUMMARY" + echo "pct=$PCT" >> "$GITHUB_OUTPUT" - - name: Upload to codecov - uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 - with: - files: cobertura.xml + - name: Enforce ratchet floor + run: | + set -euo pipefail + awk -v p="${{ steps.cov.outputs.pct }}" -v f="$FLOOR" 'BEGIN { + if (p + 0 < f + 0) { printf "FAIL: line coverage %.2f%% is below floor %s%%\n", p, f; exit 1 } + printf "OK: line coverage %.2f%% >= floor %s%%\n", p, f + }'