From 5f4efff40c80c74ce9e29d64a66cb3b8c919cbeb Mon Sep 17 00:00:00 2001 From: DemchaAV Date: Mon, 13 Jul 2026 17:01:46 +0100 Subject: [PATCH] build(coverage): gate aggregate coverage against a non-regression ratchet The graph-compose-coverage module produced a cross-module JaCoCo aggregate report but enforced no threshold, so coverage could silently decline. Add a non-regression ratchet on the aggregate (core + render-pdf + templates, counting the qa cross-module exec). JaCoCo has no native aggregate-check goal, so the check reproduces the aggregate inputs: unpack the measured modules' classes, merge their execs, and run jacoco:check on the combined BUNDLE for INSTRUCTION and BRANCH covered-ratio. The floors (0.875 / 0.68) sit just below the current baseline (0.87857 / 0.68310, identical on Windows and CI Linux), leaving only rounding and cross-platform headroom. The check runs in the existing build-and-test verify step, which feeds the required CI Gate, so a coverage regression fails the PR with no new CI job. --- .github/workflows/ci.yml | 19 ++++--- coverage/pom.xml | 106 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 113 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1673a1d3..134fee62 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -154,9 +154,11 @@ jobs: # only via the examples job), the extracted graph-compose-testing module, the # graph-compose-qa cross-module suites (which need the testing module + the # engine test-jar on the classpath and so cannot live in the engine's own test - # scope), and the graph-compose-coverage aggregator (report-only JaCoCo over - # core + qa exec). -am pulls the fonts / emoji upstreams; only examples / - # benchmarks are excluded (their own CI jobs cover them). + # scope), and the graph-compose-coverage aggregator (JaCoCo over core + + # render-pdf + templates, counting the qa exec, now with a NON-REGRESSION + # RATCHET — this step fails if aggregate INSTRUCTION or BRANCH coverage drops + # below the floor in coverage/pom.xml). -am pulls the fonts / emoji upstreams; + # only examples / benchmarks are excluded (their own CI jobs cover them). run: ./mvnw -B -ntp clean verify -pl :graph-compose-core,:graph-compose-render-pdf,:graph-compose-render-docx,:graph-compose-render-pptx,:graph-compose-templates,:graph-compose-testing,:graph-compose,:graph-compose-bundle,:graph-compose-qa,:graph-compose-coverage -am - name: Generate Javadoc @@ -168,14 +170,15 @@ jobs: run: ./mvnw -B -ntp javadoc:javadoc -pl :graph-compose-core - name: Upload aggregate coverage report - # Report-only: the cross-module JaCoCo report (core coverage counting the - # qa suites) is published as an artifact for inspection; no threshold gate. - # One JDK is enough — coverage is identical across JVMs. + # The cross-module JaCoCo report (core + render-pdf + templates coverage, + # counting the qa suites) is published as an artifact for inspection. The + # non-regression gate itself is enforced by the `verify` step above; this just + # captures the numbers. One JDK is enough — coverage is identical across JVMs. if: matrix.java == '17' uses: actions/upload-artifact@v7 with: - # Core-scope aggregate (engine coverage counting the qa suites); - # render-pdf / templates are a follow-up, so the name is explicit. + # Cross-module aggregate (core + render-pdf + templates, counting the qa + # suites). The artifact name is kept for continuity with earlier runs. name: coverage-core-aggregate-${{ github.run_id }} path: coverage/target/site/jacoco-aggregate/** if-no-files-found: error diff --git a/coverage/pom.xml b/coverage/pom.xml index fb9b6ae2..e1be3587 100644 --- a/coverage/pom.xml +++ b/coverage/pom.xml @@ -13,10 +13,13 @@ traverse test-scope dependencies, hence this dedicated module that compile-depends on the measured modules). - Report-only: no coverage threshold is enforced yet. The aggregate HTML/XML - lands in target/site/jacoco-aggregate/. Sits at the reactor tail (listed - last in the aggregator) so every measured module's exec exists first, and - never publishes (maven.deploy.skip=true, inherited from the parent). + Enforced: a non-regression ratchet on the aggregate (the jacoco check-aggregate + execution below fails the build if INSTRUCTION or BRANCH coverage drops below + the floor). The aggregate HTML/XML also lands in target/site/jacoco-aggregate/. + Sits at the reactor tail (listed last in the aggregator) so every measured + module's exec exists first, and never publishes (maven.deploy.skip=true, + inherited from the parent). The check runs in CI's build-and-test job (which + runs `verify` on this module), which feeds the required CI Gate. Scope: the engine (graph-compose-core), the PDF backend (graph-compose-render-pdf), and the built-in templates @@ -64,8 +67,54 @@ + + + 0.875 + 0.68 + 3.8.1 + + + + + org.apache.maven.plugins + maven-dependency-plugin + ${maven.dependency.plugin.version} + + + unpack-measured-classes + process-classes + + unpack-dependencies + + + + graph-compose-core,graph-compose-render-pdf,graph-compose-templates + **/*.class + ${project.build.outputDirectory} + true + true + + + + org.jacoco jacoco-maven-plugin @@ -78,6 +127,55 @@ report-aggregate + + merge-exec + verify + + merge + + + + + ${project.basedir}/.. + + core/target/jacoco.exec + render-pdf/target/jacoco.exec + templates/target/jacoco.exec + qa/target/jacoco.exec + + + + ${project.build.directory}/aggregate.exec + + + + check-aggregate + verify + + check + + + ${project.build.directory}/aggregate.exec + true + + + BUNDLE + + + INSTRUCTION + COVEREDRATIO + ${coverage.min.instruction} + + + BRANCH + COVEREDRATIO + ${coverage.min.branch} + + + + + +