Skip to content

Commit 633492e

Browse files
janedbalondrejmirtes
authored andcommitted
Add PR-affected-lines coverage gate via shipmonk/coverage-guard
Draft. Adds a pull_request-only job that runs the test suite with pcov coverage (mirroring the existing paratest coverage step), computes the GitHub three-dot diff (base...head) for the PR, and runs `coverage-guard check --patch` so only lines the PR changed are gated. Co-Authored-By: Claude Code
1 parent 92a35f5 commit 633492e

2 files changed

Lines changed: 127 additions & 0 deletions

File tree

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# https://help.github.com/en/categories/automating-your-workflow-with-github-actions
2+
#
3+
# DRAFT — patch (PR-affected lines only) coverage gate via shipmonk/coverage-guard.
4+
# See https://github.com/shipmonk-rnd/coverage-guard
5+
#
6+
# Only runs on pull_request: it gates the lines the PR changed, so it has no
7+
# meaning on push to a maintained branch.
8+
9+
name: "Patch coverage"
10+
11+
on:
12+
pull_request:
13+
paths-ignore:
14+
- 'compiler/**'
15+
- 'apigen/**'
16+
- 'changelog-generator/**'
17+
- 'issue-bot/**'
18+
19+
concurrency:
20+
group: patch-coverage-${{ github.head_ref || github.run_id }}
21+
cancel-in-progress: true
22+
23+
jobs:
24+
patch-coverage:
25+
name: "Patch coverage"
26+
runs-on: ubuntu-latest
27+
timeout-minutes: 60
28+
29+
steps:
30+
- name: Harden the runner (Audit all outbound calls)
31+
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
32+
with:
33+
egress-policy: audit
34+
35+
# fetch-depth: 0 so the merge-base of base..head is present locally.
36+
# ref: head.sha checks out the *real* PR head (NOT the refs/pull/N/merge
37+
# commit) so the line numbers in the coverage report and in the diff
38+
# below refer to the same tree. See the PR description for why this
39+
# sidesteps GitHub's merge-commit/conflict handling entirely.
40+
- name: "Checkout"
41+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
42+
with:
43+
fetch-depth: 0
44+
ref: ${{ github.event.pull_request.head.sha }}
45+
46+
- name: "Install PHP"
47+
uses: "shivammathur/setup-php@7c071dfe9dc99bdf297fa79cb49ea005b9fcadbc" # v2.37.1
48+
with:
49+
coverage: "pcov"
50+
php-version: "8.3"
51+
tools: pecl
52+
extensions: ds,mbstring
53+
ini-file: development
54+
ini-values: memory_limit=-1
55+
56+
- uses: "ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda" # v4.0.0
57+
58+
- uses: "ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda" # v4.0.0
59+
with:
60+
working-directory: "tests/"
61+
62+
# coverage-guard is not yet a dependency; install it ad-hoc for the gate.
63+
# If we decide to keep this, move it into require-dev instead.
64+
- name: "Install coverage-guard"
65+
run: composer require --dev shipmonk/coverage-guard --ignore-platform-reqs
66+
67+
# Mirror the proven-green coverage step from `make infection` /
68+
# the mutation-testing job (paratest + pcov over the whole suite), but
69+
# emit clover so coverage-guard can read it. paratest merges the
70+
# per-worker coverage for us, so no `coverage-guard merge` step is needed.
71+
- name: "Tests with coverage"
72+
run: |
73+
php -d pcov.enabled=1 tests/vendor/bin/paratest \
74+
--runner WrapperRunner \
75+
--passthru-php="'-d' 'pcov.enabled=1'" \
76+
--coverage-clover=coverage/clover.xml
77+
78+
# GitHub's own "Files changed" diff is the three-dot diff between the
79+
# base branch tip and the PR head (git diff base...head == diff from
80+
# merge-base(base, head) to head). It performs NO merge, so it is
81+
# independent of whether the PR conflicts with base. The "+" line
82+
# numbers are in head's coordinate system, matching the coverage report
83+
# collected on the head checkout above.
84+
- name: "Compute PR diff"
85+
run: |
86+
git diff \
87+
"${{ github.event.pull_request.base.sha }}...${{ github.event.pull_request.head.sha }}" \
88+
> changes.patch
89+
echo "----- changed files -----"
90+
git diff --name-only "${{ github.event.pull_request.base.sha }}...${{ github.event.pull_request.head.sha }}"
91+
92+
- name: "Check patch coverage"
93+
run: vendor/bin/coverage-guard check coverage/clover.xml --patch changes.patch
94+
95+
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
96+
if: ${{ !cancelled() }}
97+
with:
98+
name: patch-coverage
99+
path: |
100+
coverage/clover.xml
101+
changes.patch

‎coverage-guard.php‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php declare(strict_types = 1);
2+
3+
// DRAFT config for shipmonk/coverage-guard — https://github.com/shipmonk-rnd/coverage-guard
4+
//
5+
// Used by .github/workflows/patch-coverage.yml to gate coverage on the lines a
6+
// PR changes. Thresholds below are a starting point, tune to taste.
7+
8+
use ShipMonk\CoverageGuard\Config;
9+
use ShipMonk\CoverageGuard\Rule\EnforceCoverageForMethodsRule;
10+
11+
$config = new Config();
12+
13+
// The coverage report is produced by PHPUnit/paratest on the same runner that
14+
// runs the gate, so the absolute file paths inside clover.xml already live
15+
// under this directory — git root is enough, no path mapping needed.
16+
// (The monorepo needs addCoveragePathMapping() only because PHPUnit there runs
17+
// inside a container at a different WORKDIR than the checkout.)
18+
$config->setGitRoot(__DIR__);
19+
20+
$config->addRule(new EnforceCoverageForMethodsRule(
21+
requiredCoveragePercentage: 50,
22+
minMethodChangePercentage: 50,
23+
minExecutableLines: 5,
24+
));
25+
26+
return $config;

0 commit comments

Comments
 (0)