ci: report Go test coverage on pull requests (#6061)

* ci: report Go test coverage on pull requests

Adds octocov to the existing 'Test Go code' job. It reads the coverage
profile, posts a PR comment with the coverage percentage and the delta
against master, and writes the same report to the job summary.

The master-branch report is stored as a GitHub Actions artifact, so no
external service or secret is needed.

* ci: merge the plugins job coverage into the same report

The plugins suite runs in its own job, so its coverage was missing from
the report. Both jobs now upload their profile as an artifact and a new
'Report coverage' job merges them into a single PR comment.

* ci: update the coverage comment in place instead of reposting

octocov's default is to collapse the previous comment and create a new
one. updatePrevious edits the existing comment instead, so a PR keeps a
single coverage comment across pushes.

* ci: fix octocov timeout and step-time lookup

Storing the report hit the 30s default timeout: scanning this repo's
artifacts for the baseline consumed it first. Raise it to 5m.

The step-time lookup also matched the Windows job's 'Test' step and
waited for a job that was still running, so execution time was dropped
from the report. Rename the step to make it unique.

* ci: only store the coverage baseline from the default branch

* ci: report statement coverage instead of line coverage

octocov reports statement coverage for a single profile but switches to
line counting when it merges several itself, which made the number
disagree with 'go tool cover -func'. Merge the two job profiles into one
file first, so the reported number matches what developers see locally.

* ci: stop the download-link comment from clobbering the coverage report

Both comments are posted by github-actions[bot], and the download-link
job updated the first bot comment it found. On a new PR the coverage
comment is created first, so it would be overwritten. Match on the body
as well, and keep the coverage profiles out of the download list.
This commit is contained in:
Deluan Quintão 2026-08-31 23:03:09 -04:00 • committed by GitHub
commit 4ed7494a32
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 84 additions and 7 deletions

View file

@ -34,16 +34,19 @@ jobs:
}
const {data: {artifacts}} = await github.rest.actions.listWorkflowRunArtifacts({owner, repo, run_id});
if (!artifacts.length) {
const downloadable = artifacts.filter((art) => !art.name.startsWith('octocov-'));
if (!downloadable.length) {
return core.error(`No artifacts found`);
}
let body = `Download the artifacts for this pull request:\n`;
for (const art of artifacts) {
const header = `Download the artifacts for this pull request:`;
let body = `${header}\n`;
for (const art of downloadable) {
body += `\n* [${art.name}.zip](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
}
const {data: comments} = await github.rest.issues.listComments({repo, owner, issue_number});
const existing_comment = comments.find((c) => c.user.login === 'github-actions[bot]');
// Match on the body too: octocov also comments as github-actions[bot].
const existing_comment = comments.find((c) => c.user.login === 'github-actions[bot]' && c.body.startsWith(header));
if (existing_comment) {
core.info(`Updating comment ${existing_comment.id}`);
await github.rest.issues.updateComment({repo, owner, comment_id: existing_comment.id, body});

View file

@ -137,8 +137,10 @@ jobs:
- name: Download dependencies
run: go mod download
- name: Test
run: go test -shuffle=on -tags netgo,sqlite_fts5 -race -v $(go list ./... | grep -v '/plugins$')
# Name must stay unique across the workflow: octocov matches step names
# by name across every job, and waits for each match to finish.
- name: Test with coverage
run: go test -shuffle=on -tags netgo,sqlite_fts5 -race -v -covermode=atomic -coverprofile=coverage.out $(go list ./... | grep -v '/plugins$')
- name: Test ndpgen
run: |
@ -147,6 +149,13 @@ jobs:
go build -o ndpgen .
./ndpgen --help
- name: Upload coverage profile
uses: actions/upload-artifact@v7
with:
name: octocov-go
path: coverage.out
if-no-files-found: error
go-plugins:
name: Test Go plugins
runs-on: ubuntu-latest
@ -169,7 +178,39 @@ jobs:
restore-keys: wazero-${{ runner.os }}-
- name: Test plugins
run: go tool ginkgo -p -race -tags netgo,sqlite_fts5 ./plugins/
run: go tool ginkgo -p -race -tags netgo,sqlite_fts5 --cover --covermode=atomic --coverprofile=coverage.out --output-dir=. ./plugins/
- name: Upload coverage profile
uses: actions/upload-artifact@v7
with:
name: octocov-plugins
path: coverage.out
if-no-files-found: error
coverage:
name: Report coverage
runs-on: ubuntu-latest
needs: [go, go-plugins]
permissions:
contents: read
pull-requests: write
actions: write
steps:
- uses: actions/checkout@v7
- uses: actions/download-artifact@v8
with:
pattern: octocov-*
# Merge here rather than letting octocov do it: octocov reports statement
# coverage for a single profile, but switches to line counting for several.
- name: Merge coverage profiles
run: |
echo "mode: atomic" > coverage.out
awk 'FNR==1 && /^mode:/ {next} {k=$1" "$2; c[k]+=$3} END {for (k in c) print k, c[k]}' \
octocov-*/coverage.out | sort >> coverage.out
- uses: k1LoW/octocov-action@v1
go-windows:
name: Test Go code (Windows)