11name : verify
22on :
33 pull_request :
4+
5+ # Code Scanning needs write access to upload SARIF results for inline annotations.
6+ permissions :
7+ contents : read
8+ security-events : write
9+
410jobs :
5- pmd :
11+ # Fast, early-fail lint lane: PMD + Checkstyle (+ CPD). Turns red in a few
12+ # minutes on any violation, independent of the long build below, so a stray
13+ # PMD/Checkstyle issue is reported immediately rather than after `verify`.
14+ #
15+ # `compile` is in the same invocation as the analysis goals: PMD's
16+ # type-resolving rules (e.g. InvalidLogMessageFormat on the SLF4J
17+ # trailing-Throwable idiom) need Tycho's aux-classpath, which a fresh `mvn`
18+ # does not inherit from a prior step's target/classes.
19+ #
20+ # `--fail-never` lets every module produce its report (no Maven cascade-skip),
21+ # so the uploaded SARIF — and therefore the inline annotations — are complete.
22+ # The trade-off: --fail-never suppresses even compile and target-resolution
23+ # failures (mvn exits 0 on a broken build), so the gate pairs the jq count
24+ # with a presence check — zero valid analyzer inputs fails the lane rather
25+ # than reading as a clean pass. Compilation itself is independently gated by
26+ # maven-verify.
27+ lint :
628 runs-on : ubuntu-24.04
729 steps :
830 - uses : actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
@@ -12,10 +34,111 @@ jobs:
1234 java-version : ' 21'
1335 - name : Set up Workspace Environment Variable
1436 run : echo "WORKSPACE=${{ github.workspace }}" >> $GITHUB_ENV
15- - name : PMD Check
16- run : mvn pmd:pmd pmd:cpd pmd:check pmd:cpd-check -f ./ddk-parent/pom.xml --batch-mode --fail-at-end
17- checkstyle :
37+ - name : Restore Maven dependency cache
38+ # Restore-only, mirroring snapshot.yml's producer cache exactly (path and
39+ # key are hashed into the cache version — see the maven-verify step).
40+ uses : actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
41+ with :
42+ path : ~/.m2/repository
43+ key : ${{ runner.os }}-maven-publish-${{ hashFiles('**/pom.xml', '**/*.target') }}
44+ restore-keys : ${{ runner.os }}-maven-publish-
45+
46+ - name : PMD + Checkstyle reports (SARIF)
47+ # PMD: SarifRenderer FQCN — emits pmd.sarif.json AND keeps pmd.xml.
48+ # Checkstyle: output.format=sarif — SARIF content in checkstyle-result.xml.
49+ # CPD is excluded here: the global -Dformat flag uses PMD's Renderer
50+ # hierarchy and would ClassCastException CPD's CPDReportRenderer.
51+ run : |
52+ mvn -T 2C -f ./ddk-parent/pom.xml --batch-mode --fail-never \
53+ compile \
54+ pmd:pmd checkstyle:checkstyle \
55+ -Dformat=net.sourceforge.pmd.renderers.SarifRenderer \
56+ -Dcheckstyle.output.format=sarif
57+
58+ - name : CPD report (separate invocation — no SARIF support)
59+ # CPD has no SARIF renderer; emits cpd.xml only. Run standalone so the
60+ # PMD -Dformat flag isn't in scope.
61+ # NOTE: project CPD token threshold is currently very high (issue #1339),
62+ # which effectively disables detection; re-tune once #1339 lands.
63+ run : |
64+ mvn -T 2C -f ./ddk-parent/pom.xml --batch-mode --fail-never \
65+ compile \
66+ pmd:cpd-check
67+
68+ - name : Merge per-module SARIFs (PMD + Checkstyle)
69+ if : always()
70+ # Code Scanning accepts one run per category per upload; each analyzer
71+ # writes one SARIF per module, so concatenate each analyzer's results
72+ # into a single run under .sarif-merged/.
73+ run : |
74+ mkdir -p .sarif-merged
75+ # Merge per-module SARIFs into one run. Filter to JSON-parseable files:
76+ # the ddk-parent aggregator writes a plain-XML checkstyle-result.xml that
77+ # would break jq, and modules with no findings may emit non-SARIF stubs.
78+ merge() { # $1 = find-glob, $2 = output
79+ local f valid=()
80+ while IFS= read -r f; do
81+ jq -e . "$f" >/dev/null 2>&1 && valid+=("$f")
82+ done < <(find . -path "$1")
83+ if [ ${#valid[@]} -gt 0 ]; then
84+ # del(.ruleIndex): each result's ruleIndex points into its OWN run's
85+ # rules array, but the merge keeps only the first run's tool — Code
86+ # Scanning must resolve rules by ruleId string instead.
87+ jq -s '{
88+ "$schema": .[0]."$schema", version: .[0].version,
89+ runs: [{ tool: .[0].runs[0].tool,
90+ results: [.[].runs[].results[]? | del(.ruleIndex)],
91+ invocations: [.[].runs[].invocations[]?] }]
92+ }' "${valid[@]}" > "$2"
93+ fi
94+ }
95+ merge '*/target/pmd.sarif.json' .sarif-merged/pmd.sarif
96+ merge '*/target/checkstyle-result.xml' .sarif-merged/checkstyle.sarif
97+
98+ - name : Gate on PMD / CPD / Checkstyle violations
99+ # merge() only writes its output when it found at least one valid input,
100+ # so a missing merged file means that analyzer silently died (e.g. a
101+ # plugin bump broke a renderer flag) — never a clean pass.
102+ run : |
103+ set -eu
104+ for f in .sarif-merged/pmd.sarif .sarif-merged/checkstyle.sarif; do
105+ if [ ! -s "$f" ]; then
106+ echo "::error::No valid SARIF input produced for ${f##*/} — the analysis silently failed."
107+ exit 1
108+ fi
109+ done
110+ if [ "$(find . -name 'cpd.xml' -path '*/target/*' | wc -l)" -eq 0 ]; then
111+ echo "::error::No cpd.xml produced — CPD silently failed."
112+ exit 1
113+ fi
114+ sarif_total=$(jq '[.runs[].results[]?] | length' \
115+ .sarif-merged/pmd.sarif .sarif-merged/checkstyle.sarif 2>/dev/null \
116+ | awk '{s+=$1} END {print s+0}')
117+ cpd_total=$(find . -name 'cpd.xml' -path '*/target/*' -exec grep -c '<duplication ' {} + 2>/dev/null \
118+ | awk -F: '{s+=$2} END {print s+0}')
119+ echo "PMD/Checkstyle SARIF violations: $sarif_total"
120+ echo "CPD duplications: $cpd_total"
121+ if [ "$sarif_total" != "0" ] || [ "$cpd_total" != "0" ]; then
122+ echo "::error::Static analysis found violations (PMD/CPD/Checkstyle)."
123+ exit 1
124+ fi
125+
126+ - name : Upload PMD/Checkstyle SARIF to Code Scanning
127+ if : always()
128+ # Annotation-only, never the gate: a fork PR gets a read-only token and
129+ # upload-sarif 403s, which must not red an otherwise-clean lane.
130+ continue-on-error : true
131+ uses : github/codeql-action/upload-sarif@7fd177fa680c9881b53cdab4d346d32574c9f7f4 # v3.35.4
132+ with :
133+ sarif_file : .sarif-merged
134+ category : lint
135+
136+ # SpotBugs is the slow critical-path analysis (the experiments' durable
137+ # finding), so it runs in its own parallel lane and never delays `lint`.
138+ spotbugs :
18139 runs-on : ubuntu-24.04
140+ env :
141+ MAVEN_OPTS : -Xmx4g
19142 steps :
20143 - uses : actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
21144 - uses : actions/setup-java@0f481fcb613427c0f801b606911222b5b6f3083a # v5
@@ -24,14 +147,79 @@ jobs:
24147 java-version : ' 21'
25148 - name : Set up Workspace Environment Variable
26149 run : echo "WORKSPACE=${{ github.workspace }}" >> $GITHUB_ENV
27- - name : Checkstyle Check
28- run : mvn checkstyle:checkstyle checkstyle:check -f ./ddk-parent/pom.xml --batch-mode --fail-at-end
150+ - name : Restore Maven dependency cache
151+ # Restore-only, mirroring snapshot.yml's producer cache exactly (path and
152+ # key are hashed into the cache version — see the maven-verify step).
153+ uses : actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
154+ with :
155+ path : ~/.m2/repository
156+ key : ${{ runner.os }}-maven-publish-${{ hashFiles('**/pom.xml', '**/*.target') }}
157+ restore-keys : ${{ runner.os }}-maven-publish-
158+
159+ - name : SpotBugs report (SARIF)
160+ # sarifOutput=true emits spotbugsSarif.json (also writes spotbugsXml.xml).
161+ run : |
162+ mvn -T 2C -f ./ddk-parent/pom.xml --batch-mode --fail-never \
163+ compile \
164+ spotbugs:spotbugs \
165+ -Dspotbugs.sarifOutput=true
166+
167+ - name : Merge per-module SpotBugs SARIFs
168+ if : always()
169+ run : |
170+ mkdir -p .sarif-merged
171+ valid=()
172+ while IFS= read -r f; do
173+ jq -e . "$f" >/dev/null 2>&1 && valid+=("$f")
174+ done < <(find . -path '*/target/spotbugsSarif.json')
175+ if [ ${#valid[@]} -gt 0 ]; then
176+ # del(.ruleIndex): see the lint merge — indexes are per-run, the
177+ # merged tool keeps only the first run's rules.
178+ jq -s '{
179+ "$schema": .[0]."$schema", version: .[0].version,
180+ runs: [{ tool: .[0].runs[0].tool,
181+ results: [.[].runs[].results[]? | del(.ruleIndex)],
182+ invocations: [.[].runs[].invocations[]?] }]
183+ }' "${valid[@]}" > .sarif-merged/spotbugs.sarif
184+ fi
185+
186+ - name : Gate on SpotBugs violations
187+ # A missing merged SARIF means the analysis silently died (--fail-never
188+ # suppresses even compile/resolution failures) — never a clean pass.
189+ run : |
190+ set -eu
191+ if [ ! -s .sarif-merged/spotbugs.sarif ]; then
192+ echo "::error::No SpotBugs SARIF produced — the analysis silently failed."
193+ exit 1
194+ fi
195+ sb_total=$(jq '[.runs[].results[]?] | length' .sarif-merged/spotbugs.sarif 2>/dev/null || echo 0)
196+ echo "SpotBugs SARIF violations: $sb_total"
197+ if [ "$sb_total" != "0" ]; then
198+ echo "::error::SpotBugs found violations."
199+ exit 1
200+ fi
201+
202+ - name : Upload SpotBugs SARIF to Code Scanning
203+ if : always()
204+ # Annotation-only, never the gate: a fork PR gets a read-only token and
205+ # upload-sarif 403s, which must not red an otherwise-clean lane.
206+ continue-on-error : true
207+ uses : github/codeql-action/upload-sarif@7fd177fa680c9881b53cdab4d346d32574c9f7f4 # v3.35.4
208+ with :
209+ sarif_file : .sarif-merged
210+ category : spotbugs
211+
29212 line-endings :
30213 runs-on : ubuntu-24.04
31214 steps :
32215 - uses : actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
33216 - name : Check LF line endings
34217 run : bash .github/scripts/check-line-endings.sh
218+
219+ # Build + tests only. Static analysis now lives in the `lint` and `spotbugs`
220+ # jobs, so the redundant checkstyle/pmd/spotbugs goals are dropped from here —
221+ # this is the wall-clock long pole and no longer re-runs analysis.
222+ # No `-T 2C`: tests are not known to pass reliably under reactor parallelism.
35223 maven-verify :
36224 runs-on : ubuntu-24.04
37225 steps :
58246 key : ${{ runner.os }}-maven-publish-${{ hashFiles('**/pom.xml', '**/*.target') }}
59247 restore-keys : ${{ runner.os }}-maven-publish-
60248 - name : Build with Maven within a virtual X Server Environment
61- # Run pmd:pmd and pmd:cpd first to generate reports for all modules, then run pmd:check and pmd:cpd-check
62- # This ensures all violations are collected and reported before the build fails
63- run : xvfb-run mvn clean verify checkstyle:check pmd:pmd pmd:cpd pmd:check pmd:cpd-check spotbugs:check -f ./ddk-parent/pom.xml --batch-mode --fail-at-end
249+ run : xvfb-run mvn clean verify -f ./ddk-parent/pom.xml --batch-mode --fail-at-end
64250 - name : Fail on missing surefire reports
65251 if : always()
66252 run : bash .github/scripts/check-surefire-reports.sh
0 commit comments