From cb618d6f9c8aed24cb57508b07ad508f5fe9b447 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Sat, 22 Aug 2026 13:46:06 +0200 Subject: [PATCH] CI: Factor out coverage report generation --- .github/workflows/coverage.yml | 47 ++++++------------- Makefile.in | 2 + ci/ci-coverage-report.bash | 86 ++++++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+), 32 deletions(-) create mode 100755 ci/ci-coverage-report.bash diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index cfe7865aa..b7ce9609a 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -132,52 +132,35 @@ jobs: env: GH_TOKEN: ${{ github.token }} run: | - ls -lsha obj_coverage - # Combine reports from test jobs - nodist/fastcov.py -C obj_coverage/verilator-*.info --lcov -o obj_coverage/verilator.info - # For a PR, report patch coverage against the merge-base between the head of the PR and the target branch + ci/ci-coverage-report.bash \ + ${{ github.run_id }} \ + "${{ github.event_name == 'pull_request' && github.event.number || '' }}" \ + "${{ github.event.pull_request.base.sha }}" \ + "${{ github.event.pull_request.head.sha }}" + # Create the report artifact. Note there is no report if the patch is empty. + mkdir ../report-artifact + [ ! -d obj_coverage/report ] || mv obj_coverage/report ../report-artifact/ if [[ "${{ github.event_name }}" == "pull_request" ]]; then - COVERAGE_BASE=$(git rev-parse --short $(git merge-base ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }})) - make coverage-report COVERAGE_BASE=${COVERAGE_BASE} |& tee ${{ github.workspace }}/make-coverage-report.log - else - make coverage-report + echo ${{ github.event.number }} > ../report-artifact/pr-number.txt fi - # Remove data files - rm -f obj_coverage/verilator*.info - # Some extra work for PRs only + # Create the notification artifact, for pull requests only if [[ "${{ github.event_name }}" == "pull_request" ]]; then - # Save PR number in report - echo ${{ github.event.number }} > obj_coverage/pr-number.txt - # Generate notification comment content - mkdir -p notification - echo ${{ github.event.number }} > notification/pr-number.txt - NUM=$(gh run view ${{ github.run_id }} --json number --jq ".number") - URL=$(gh run view ${{ github.run_id }} --json url --jq ".url") - echo "Patch coverage from PR workflow [#$NUM]($URL) (code coverage of lines changed relative to ${COVERAGE_BASE}):" > notification/body.txt - if [[ ! -f obj_coverage/empty-patch ]]; then - echo "
" >> notification/body.txt
-              grep -E "(lines|branches)\.*:" ${{ github.workspace }}/make-coverage-report.log | sed "s/\.*:/:/" >> notification/body.txt || true
-              echo "
" >> notification/body.txt - echo "Report: [${{ github.run_id }}](https://${{ github.repository_owner }}.github.io/verilator/coverage-reports/${{ github.run_id }}/index.html)" >> notification/body.txt - echo "" >> notification/body.txt - echo "Please get to 100% line coverage, and understand all branches; see https://github.com/verilator/verilator/blob/master/docs/internals.rst#code-coverage-results" >> notification/body.txt - else - echo "Patch contains no code changes" >> notification/body.txt - fi - cat notification/body.txt + mkdir ../notification-artifact + mv obj_coverage/notification.txt ../notification-artifact/body.txt + echo ${{ github.event.number }} > ../notification-artifact/pr-number.txt fi - name: Upload report uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: - path: repo/obj_coverage + path: report-artifact name: coverage-report - name: Upload notification if: ${{ github.event_name == 'pull_request' }} uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: - path: repo/notification + path: notification-artifact name: pr-notification # Create GitHub issue for failed scheduled jobs diff --git a/Makefile.in b/Makefile.in index 60cb2c2f6..8ec594833 100644 --- a/Makefile.in +++ b/Makefile.in @@ -467,9 +467,11 @@ analyzer-include: # Bash/sh files BASH_FILES = \ + ci/ci-coverage-report.bash \ ci/ci-install.bash \ ci/ci-pages-notify.bash \ ci/ci-pages.bash \ + ci/ci-rtlmeter-report.bash \ ci/ci-script.bash \ ci/docker/run/hooks/post_push \ ci/docker/run/verilator-docker \ diff --git a/ci/ci-coverage-report.bash b/ci/ci-coverage-report.bash new file mode 100755 index 000000000..60a9e67e0 --- /dev/null +++ b/ci/ci-coverage-report.bash @@ -0,0 +1,86 @@ +#!/usr/bin/env bash +# DESCRIPTION: Verilator: CI script for 'coverage.yml' results +# +# SPDX-FileCopyrightText: 2026 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +# This script combines the code coverage data gathered by the test jobs into +# the HTML report published on GitHub Pages, and for a pull request, builds the +# content of the response comment posted on it. + +# Developer note: You should be able to run this script in your local checkout +# if you have GitHub CLI (command 'gh') setup, authenticated ('gh auth login'), +# and have set a default repository ('gh repo set-default'). + +set -eo pipefail + +# Trace when running in the CI +[ "$GITHUB_ACTIONS" != "true" ] || set -x + +# Arguments: +# 1. run ID +# 2. number of the pull request, or empty for a non-pull-request run +# 3. base SHA of the pull request (pull requests only) +# 4. head SHA of the pull request (pull requests only) +RUN_ID=$1 +PR_NUMBER=$2 +PR_BASE_SHA=$3 +PR_HEAD_SHA=$4 + +# Coverage data and report directory, uploaded as the 'coverage-report' artifact +COVERAGE_DIR=obj_coverage +ls -lsha ${COVERAGE_DIR} + +# Combine the reports from the test jobs +nodist/fastcov.py -C ${COVERAGE_DIR}/verilator-*.info --lcov -o ${COVERAGE_DIR}/verilator.info + +# Create the report. For a pull request, report patch coverage against the +# merge-base between the head of the pull request and the target branch. The +# summary quoted in the notification below is only in the log of 'make'. +MAKE_LOG=make-coverage-report.log +if [ -n "${PR_NUMBER}" ]; then + COVERAGE_BASE=$(git rev-parse --short $(git merge-base ${PR_BASE_SHA} ${PR_HEAD_SHA})) + make coverage-report COVERAGE_BASE=${COVERAGE_BASE} |& tee ${MAKE_LOG} +else + make coverage-report +fi + +# Remove the data files, only the HTML report is published +rm -f ${COVERAGE_DIR}/verilator*.info + +# The rest is for pull requests only +if [ -z "${PR_NUMBER}" ]; then + exit 0 +fi + +# Get some metadata about the run +RUN_NUM=$(gh run view ${RUN_ID} --json number --jq ".number") +RUN_URL=$(gh run view ${RUN_ID} --json url --jq ".url") + +# Repository owner and name of the default repository, used to build the +# GitHub Pages URL of the report. The owner is lowercased, as required for the +# '.github.io' Pages domain. +PAGES_OWNER=$(gh repo view --json owner --jq '.owner.login' | tr '[:upper:]' '[:lower:]') +PAGES_NAME=$(gh repo view --json name --jq '.name') + +# Create notification comment content +NOTIFICATION=${COVERAGE_DIR}/notification.txt +cat > ${NOTIFICATION} <> ${NOTIFICATION} +else + cat >> ${NOTIFICATION} < +$(grep -E "(lines|branches)\.*:" ${MAKE_LOG} | sed "s/\.*:/:/" || true) + +Report: [${RUN_ID}](https://${PAGES_OWNER}.github.io/${PAGES_NAME}/coverage-reports/${RUN_ID}/index.html) + +Please get to 100% line coverage, and understand all branches; see https://github.com/verilator/verilator/blob/master/docs/internals.rst#code-coverage-results +SUMMARY_TEMPLATE +fi + +# Print it +cat ${NOTIFICATION}