Add CI coverage summaries and aggregate JaCoCo report (#6451)

This commit is contained in:
Anthony Stirling
2026-06-02 14:59:10 +01:00
committed by GitHub
parent 2c0ebc28a7
commit de9d6ad3f5
15 changed files with 1583 additions and 23 deletions
+66 -7
View File
@@ -1,8 +1,14 @@
name: Backend build, format check, and coverage
# Reusable workflow called from build.yml. Runs the backend build matrix
# (JDK 25 × spring-security on/off), Spotless formatting check, JUnit, and
# (JDK 25 × every flavor), Spotless formatting check, JUnit, and
# posts Jacoco coverage to PRs.
#
# Flavor axis (maps to STIRLING_FLAVOR in settings.gradle):
# core - DISABLE_ADDITIONAL_FEATURES=true, no proprietary, no saas
# proprietary - default build, no saas
# saas - proprietary + the saas subproject (build + JUnit only,
# never any runtime/integration testing)
on:
workflow_call:
@@ -25,7 +31,7 @@ jobs:
fail-fast: false
matrix:
jdk-version: [25]
spring-security: [true, false]
flavor: [core, proprietary, saas]
steps:
- name: Harden Runner
uses: step-security/harden-runner@ab7a9404c0f3da075243ca237b5fac12c98deaa5 # v2.19.3
@@ -58,7 +64,10 @@ jobs:
- name: Install Task
uses: go-task/setup-task@3be4020d41929789a01026e0e427a4321ce0ad44 # v2.0.0
- name: Check Java formatting (Spotless)
if: matrix.jdk-version == 25 && matrix.spring-security == false
# Runs once per matrix combination - pick the cheapest leg
# (core - no proprietary, no saas) so we don't wait for the
# heavier flavors just to fail formatting.
if: matrix.jdk-version == 25 && matrix.flavor == 'core'
id: spotless-check
run: task backend:format:check
continue-on-error: true
@@ -143,17 +152,24 @@ jobs:
});
}
- name: Build with Gradle and spring security ${{ matrix.spring-security }}
- name: Build with Gradle (flavor=${{ matrix.flavor }})
# STIRLING_FLAVOR is read by settings.gradle and expands into the
# right combination of DISABLE_ADDITIONAL_FEATURES + ENABLE_SAAS
# so we don't have to set them by hand. The saas flavor pulls in
# the app/saas subproject (unit tests only - no runtime tests).
run: task backend:build:ci
env:
MAVEN_USER: ${{ secrets.MAVEN_USER }}
MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }}
MAVEN_PUBLIC_URL: ${{ secrets.MAVEN_PUBLIC_URL }}
DISABLE_ADDITIONAL_FEATURES: ${{ matrix.spring-security }}
STIRLING_FLAVOR: ${{ matrix.flavor }}
- name: Check Test Reports Exist
if: always()
run: |
# Common + core + proprietary always build (proprietary is
# excluded only at runtime, not from the gradle subproject
# graph). Saas builds add a fourth report dir.
declare -a dirs=(
"app/core/build/reports/tests/"
"app/core/build/test-results/"
@@ -162,6 +178,9 @@ jobs:
"app/proprietary/build/reports/tests/"
"app/proprietary/build/test-results/"
)
if [ "${{ matrix.flavor }}" = "saas" ]; then
dirs+=("app/saas/build/reports/tests/" "app/saas/build/test-results/")
fi
for dir in "${dirs[@]}"; do
if [ ! -d "$dir" ]; then
echo "Missing $dir"
@@ -173,7 +192,7 @@ jobs:
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: test-reports-jdk-${{ matrix.jdk-version }}-spring-security-${{ matrix.spring-security }}
name: test-reports-jdk-${{ matrix.jdk-version }}-flavor-${{ matrix.flavor }}
path: |
app/**/build/reports/jacoco/test
app/**/build/reports/tests/
@@ -183,7 +202,47 @@ jobs:
retention-days: 3
if-no-files-found: warn
- name: Add coverage to PR with spring security ${{ matrix.spring-security }} and JDK ${{ matrix.jdk-version }}
- name: Install defusedxml for coverage summary
# coverage-summary.py parses JaCoCo XML through defusedxml to
# silence security scanners that pattern-match on the stdlib
# xml.etree.ElementTree.parse call.
if: always() && matrix.flavor == 'saas'
run: python -m pip install --quiet defusedxml
- name: JaCoCo coverage step summary
# Only the saas leg posts the JUnit summary - it's a strict
# superset of the core + proprietary legs (same .exec files plus
# the saas subproject). Posting from all three would mean three
# near-identical tables crowding out the aggregate report.
if: always() && matrix.flavor == 'saas'
run: |
python scripts/coverage-summary.py \
--title "Backend JUnit coverage (JDK ${{ matrix.jdk-version }})" \
--jacoco "common=app/common/build/reports/jacoco/test/jacocoTestReport.xml" \
--jacoco "core=app/core/build/reports/jacoco/test/jacocoTestReport.xml" \
--jacoco "proprietary=app/proprietary/build/reports/jacoco/test/jacocoTestReport.xml" \
--jacoco "saas=app/saas/build/reports/jacoco/test/jacocoTestReport.xml" \
--github-step-summary
- name: Upload raw JUnit .exec for aggregate merge
# Same dedup rationale as the summary step: upload from the saas
# leg only (the most complete set, includes app/saas/.../test.exec)
# so the aggregate workflow merges the union rather than three
# overlapping subsets.
#
# Separate artifact from the HTML reports so the aggregate
# workflow can grab just the .exec files with a name pattern
# (`jacoco-exec-*`) instead of unpacking the whole test-reports
# tarball.
if: always() && matrix.flavor == 'saas'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: jacoco-exec-junit-jdk-${{ matrix.jdk-version }}
path: app/*/build/jacoco/*.exec
retention-days: 7
if-no-files-found: warn
- name: Add coverage to PR (flavor=${{ matrix.flavor }}, JDK=${{ matrix.jdk-version }})
# The action only supports the pull_request event (it posts a PR comment),
# so skip it for merge_group runs and workflow_dispatch.
if: github.event_name == 'pull_request'