mirror of
https://github.com/praktimarc/kst4contest.git
synced 2026-10-18 21:34:03 +02:00
* build: write SpotBugs XML and HTML reports (#110) The SpotBugs tasks had no report configured, so findings went to stdout only and, with ignoreFailures, scrolled past unread. Every SpotBugsTask now writes XML (for CI to count) and HTML (for people) to the plugin's default location, <module>/build/reports/spotbugs/main.{xml,html}. ignoreFailures stays; the build still passes with findings present. Fixes #110 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * ci: run PMD and SpotBugs on pull requests and publish the reports (#110) The compile job runs pmdMain and spotbugsMain after compiling (report only: both tools have ignoreFailures, a crash of a tool still fails), counts <violation> and <BugInstance> elements per module from the report XML into the job summary, and uploads */build/reports/pmd and */build/reports/spotbugs as the static-analysis artifact for 14 days. A missing report prints "no report" without failing the step. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs: name the PMD and SpotBugs report files and the CI artifact (#110) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
89d37e62e1
commit
f2b0fdd5b4
4 files changed
+70
-1
No files matched your search
@@ -41,6 +41,64 @@ jobs:
|
||||
- name: Compile
|
||||
run: ./gradlew -S classes testClasses
|
||||
|
||||
# Report only: both tools run with ignoreFailures, so a finding cannot fail this step
|
||||
# -- a crash of the tool itself still does, which is wanted. The findings used to exist
|
||||
# only in the console log; since #110 they are files, and the table and the artifact
|
||||
# below are what makes them readable from a pull request.
|
||||
- name: Static analysis (report only)
|
||||
run: ./gradlew -S pmdMain spotbugsMain
|
||||
|
||||
# Counted from the report XML, never from Gradle's output or exit code. A missing
|
||||
# report prints "no report" and does not fail the step: that case is already visible
|
||||
# in the step above.
|
||||
- name: Summarise the static analysis
|
||||
if: always()
|
||||
run: |
|
||||
set -euo pipefail
|
||||
shopt -s nullglob
|
||||
|
||||
count() {
|
||||
local file="$1" element="$2"
|
||||
if [ -f "$file" ]; then
|
||||
# grep exits 1 on zero matches, which pipefail would turn into a failed step.
|
||||
{ grep -o "<$element " "$file" || true; } | wc -l | tr -d ' '
|
||||
else
|
||||
echo "no report"
|
||||
fi
|
||||
}
|
||||
|
||||
{
|
||||
echo "### Static analysis"
|
||||
echo ""
|
||||
echo "| module | PMD violations | SpotBugs bugs |"
|
||||
echo "|---|---|---|"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
for module in core app-desktop i18n-generator; do
|
||||
pmd=$(count "$module/build/reports/pmd/main.xml" violation)
|
||||
spotbugs=$(count "$module/build/reports/spotbugs/main.xml" BugInstance)
|
||||
echo "$module: pmd=$pmd spotbugs=$spotbugs"
|
||||
echo "| \`$module\` | $pmd | $spotbugs |" >> "$GITHUB_STEP_SUMMARY"
|
||||
done
|
||||
|
||||
{
|
||||
echo ""
|
||||
echo "Report only: nothing fails on a finding. The XML and HTML reports are the"
|
||||
echo "static-analysis artifact of this run. i18n-generator is Kotlin only, so PMD"
|
||||
echo "has no Java source there and writes no report."
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
- name: Keep the static analysis reports
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: static-analysis
|
||||
path: |
|
||||
*/build/reports/pmd/
|
||||
*/build/reports/spotbugs/
|
||||
if-no-files-found: ignore
|
||||
retention-days: 14
|
||||
|
||||
# What a translation PR is actually about, put where its author will look. The counts
|
||||
# come from the files rather than from Gradle's output, so an up-to-date task cannot
|
||||
# make the table disappear.
|
||||
|
||||
@@ -129,7 +129,7 @@ Important: a test failure fails the build, but PMD and SpotBugs run with `ignore
|
||||
- inspect the Gradle test summary;
|
||||
- inspect the test-results XML under `*/build/test-results/` when needed;
|
||||
- do not infer "all tests passed" from exit code 0 -- read the XML;
|
||||
- report PMD/SpotBugs findings that are visible in the relevant build.
|
||||
- report PMD/SpotBugs findings that are visible in the relevant build. Both tools write XML and HTML: `<module>/build/reports/pmd/main.{xml,html}` and `<module>/build/reports/spotbugs/main.{xml,html}` (since #110; before that SpotBugs printed to the console only). Count from the XML (`<violation`, `<BugInstance`), not from the console. The PR workflow runs `pmdMain spotbugsMain`, prints the counts per module into the job summary and publishes the reports as the `static-analysis` artifact.
|
||||
|
||||
Run focused tests first, then normally the relevant broader test/build command for the scope.
|
||||
|
||||
|
||||
@@ -41,6 +41,16 @@ subprojects {
|
||||
ignoreFailures.set(true)
|
||||
}
|
||||
|
||||
// Without a configured report SpotBugs prints its findings to stdout only and leaves
|
||||
// no file behind; with ignoreFailures they then scroll past unread. AGENTS.md asks for
|
||||
// the findings visible in a build to be reported, which needs something to read them
|
||||
// from: XML for CI to count, HTML for people. Default location, which the plugin names
|
||||
// after the source set: <module>/build/reports/spotbugs/main.{xml,html} (#110).
|
||||
tasks.withType<com.github.spotbugs.snom.SpotBugsTask>().configureEach {
|
||||
reports.create("xml") { required.set(true) }
|
||||
reports.create("html") { required.set(true) }
|
||||
}
|
||||
|
||||
/*
|
||||
* Coverage is a report, not a gate: there is no jacocoTestCoverageVerification and no
|
||||
* minimum percentage. A threshold set at today's number would trip on the next unrelated
|
||||
|
||||
@@ -202,6 +202,7 @@ CR/LF framing, XML framing, ports/transports, callsign normalization and frequen
|
||||
- JUnit 5/Mockito, PMD and SpotBugs are part of the verification environment. JUnit and Mockito are test-only dependencies; nothing of them reaches the shipped app image.
|
||||
- Test classes live under `src/test` only. Until #62 a few JUnit classes sat under `src/main`, compiled on every build and were never executed; that is the historical reason the two modules once declared JUnit and Mockito as `implementation` dependencies.
|
||||
- A test failure fails the build. Only PMD and SpotBugs run with `ignoreFailures`, so their findings do not. Still read the test-result XML under `*/build/test-results/test/` rather than inferring a pass from the exit code.
|
||||
- Static analysis is a report, not a gate, and since #110 both tools leave a file behind: PMD under `<module>/build/reports/pmd/main.{xml,html}`, SpotBugs under `<module>/build/reports/spotbugs/main.{xml,html}` (the plugin names the report after the source set, not the task). The PR workflow runs `pmdMain spotbugsMain`, counts `<violation` and `<BugInstance` elements per module into the job summary and uploads both report directories as the `static-analysis` artifact for 14 days. `i18n-generator` is Kotlin only, so PMD has no Java source there and writes no report.
|
||||
- Coverage is measured with JaCoCo as a report only: no `jacocoTestCoverageVerification`, no threshold (rationale in `docs/superpowers/specs/2026-10-11-test-coverage-design.md`). `./gradlew jacocoTestReport` writes a per-module report under `*/build/reports/jacoco/test/` (XML and HTML); `./gradlew jacocoAggregatedReport` merges `core` and `app-desktop` into `build/reports/jacoco/aggregated/jacoco.xml` and `.../aggregated/html/`. Both run the tests first when needed. `i18n-generator` still gets its per-module report but is left out of the aggregated one: it is build tooling and says nothing about the shipped application. The PR workflow prints line and branch coverage per module and aggregated into the job summary and uploads the aggregated HTML as the `coverage-report` artifact for 14 days. The nightly and tagged-release workflows run the test job first; no packaging job starts before it has passed.
|
||||
|
||||
## Documentation Surfaces
|
||||
|
||||
Reference in new issue
Block a user