ci(report): a pull request says what it changed and what that did to coverage - #1327
Conversation
…coverage The diff breakdown was spliced into the pull request body and the coverage was nowhere: four suites measure it on every run, and the numbers only ever reached an artifact and a floor. A floor says whether a number cleared a line, never whether this change moved it. One comment now carries the change table, the four suites' statements, branches, functions and lines against a baseline from main, and the coverage of the lines the pull request itself added. It is found again by a marker and updated in place, so a second push does not leave a trail. The body is the author's again. The four suites arrive in two formats. JaCoCo counts instructions and methods where istanbul counts statements and functions; neither pair is the same measurement, but they answer the same question per column, which is what lets one table hold both toolchains. Coverage paths are matched onto changed files by their longest common tail, because no report spells a path the repository's way and an ambiguous tail is dropped rather than guessed. It runs on workflow_run rather than inside Validate: a fork's pull request holds a read-only token and cannot comment, and forks and bots are most of this repository's volume. Nothing in it checks out head-ref code — it reads the artifacts and the pulls API, both data. The baseline is cached from merge-queue entries, which are the merged result about to become main, and written from this workflow so the cache lands on the default branch where pull requests can read it. An absent or malformed report is named in the comment rather than rendered as zero, and the job gates nothing. Closes #1326
|
One limitation worth stating plainly before review: a That is why the verification above is local rather than a green check: the script was run against the actual artifacts from runs 35339810953 and 35337672075 and against four real pull requests, including the degraded paths. What CI on this branch does verify is the If you would rather see it working before it lands, the alternative is to merge it and let the next pull request be the proof, with a quick revert if it misbehaves. It gates nothing, so a failure is a missing comment rather than a blocked merge. |
Changes
No production or test lines added. Coverage
No baseline is cached from Patch coverage: this pull request changes no line that coverage measures. |
Closes #1326.
A reviewer opening a pull request here saw a diff breakdown spliced into the body by a bot, and no coverage at all. The coverage exists — four suites measure it on every run — but it only ever reached an artifact nobody downloads and a floor that fails a job. A floor says whether a number cleared a line. It never says whether this change moved it, and that is the question a reviewer actually has.
nedap/moves-dev-cli(ADRs 0019 and 0020) andJorisJonkers-dev/deploy-kitboth answer this with one comment. The second of those names this repository as the prior art for the change-breakdown half; this brings the coverage half back the other way.What this achieves
One comment per pull request, found again by a marker and updated in place rather than appended to, carrying three things:
service · categorytaxonomy as a Markdown table with bar cells, a total that excludes generated files, and the test-lines-per-production-line ratio.mainbaseline and a delta beneath it.The pull request body belongs to its author again.
pr-diff-stats.ymlandpr_diff_stats.pyare deleted, and thepull_request_targetprivilege and theedited-retrigger guard that the splice needed go with them.How
.github/scripts/pr_report.py, stdlib only, in the shapepr_diff_stats.pyalready established: a script the workflow calls rather than logic in YAML. The taxonomy is not rewritten —.github/diff-stats.ymlis renamed to.github/pr-report-rules.ymland read unchanged, along with its glob translator and rules parser, so every classification the repository already made still holds.The four suites arrive in two formats. JaCoCo's
INSTRUCTION,BRANCH,METHODandLINEfill the statements, branches, functions and lines columns. Instructions are not statements and methods are not functions, but they answer the same question per column, and the alternative is two tables a reviewer cannot read across. Only the report-level counters are read: JaCoCo repeats every counter at package, class and method level, so summing all of them multiplies the same numbers several times over.Patch coverage reads line-level data from both formats —
DA:from lcov,<line ci=…>from JaCoCo — and matches their paths onto the changed files by longest common tail. No report spells a path the repository's way (frontend/src/App.vuefrom one,net/blueshell/api/…from the other), and a tail matching more than one changed file is dropped rather than guessed.It runs on
workflow_runbecausevalidate.ymlruns onpull_request, so a fork's run holds a read-only token and cannot comment — and forks and bots are most of this repository's volume. Nothing in it checks out or executes head-ref code: it reads artifacts and the pulls API, both data. The checkout is pinned to the default branch with noref:and carries a comment saying not to add one.ADR 0020 caches a baseline from
mainruns, andvalidate.ymlhas nopush: maintrigger to take one from. A merge-queue entry is the merged result about to becomemain, so that is what gets cached, and only from a run that went green. It is written from this workflow rather than from Validate on purpose: aworkflow_runjob runs on the default branch, so its cache is visible to every pull request. One written from agh-readonly-queue/**ref would not be.Not in scope
No Python test harness.
pr_diff_stats.pyhad none and inventing one inside a workflow that holds a write token is a larger decision than this; the script was exercised against real data instead, below.Open pull requests keep the old spliced block in their bodies until they are next edited. Nothing removes it and it does no harm.
Worth a reviewer's attention
workflow_runworkflow only ever runs from the version on the default branch, so GitHub will not runpr-report.ymlfrom this branch. The first real comment appears on whatever pull request opens after this merges. That is why the verification below is local rather than a green check.No coverage reached this report for **api integration**) rather than rendered as zero. That distinction is not academic: an empty coverage set rendering as0.00%is exactly the trap that hid a broken JaCoCo fan-in on ci(api): the integration suite runs in six shards, and the floors read all six #1322 earlier today.pr-report-metais a new one-line artifact from thechangesjob carrying the pull request number, becauseworkflow_run.pull_requestsis empty for fork pull requests.Verification
The script was run against the real artifacts from runs
35339810953and35337672075, and against real pull requests:2.18+1.50/-2.25; an exact zero renders0.00without a sign, per ADR 0020The
validate.ymlhalf is proven in CI on this branch:pr-report-metais produced and its contents round-trip to1327.The api unit row showing
40.49%for both statements and branches is not a bug.INSTRUCTIONis 40.4867% andBRANCHis 40.4948%, and both round to the same two decimals.