Share a Checkstyle, PMD or SpotBugs report
Java static analysis has a distribution problem shaped exactly like Java
test reports. The tools produce readable HTML —
build/reports/checkstyle/main.html and friends — and that HTML is
visible to precisely one machine.
In CI it becomes an artifact zip. In practice, the whole apparatus collapses down to a red X on a build, and the finding itself is never read.
Getting the HTML out
Under Gradle:
checkstyle { toolVersion = '10.17.0' }
// SpotBugs writes XML by default — HTML has to be asked for.
tasks.withType(com.github.spotbugs.snom.SpotBugsTask) {
reports {
html.required = true
xml.required = false
}
}
./gradlew checkstyleMain pmdMain spotbugsMain || true
# → build/reports/checkstyle/main.html
# → build/reports/pmd/main.html
# → build/reports/spotbugs/main.html
Under Maven, the site lifecycle collects all three into target/site/.
Note the || true. These tasks fail the build on violations, which is
usually what you want and always breaks a publish step that comes after
them.
Publish it
curl -fsS -X PATCH "https://commareports.com/api/v1/reports/$CHECKSTYLE_REPORT_ID" \
-H "Authorization: Bearer $COMMA_API_TOKEN" \
-H "Content-Type: application/json" \
-d "$(jq -n --rawfile html build/reports/checkstyle/main.html \
--arg title "Checkstyle — $(git rev-parse --short HEAD)" \
'{title: $title, html: $html}')"
Each of these is a single self-contained file, which is the shape that
publishes in one call — no assets to upload. Use a
scoped token (reports:write) from CI secrets, and
PATCH a saved id per tool so each keeps one permanent URL with a
revision per build. Make the step unconditional — if: always() in GitHub
Actions, post { always { … } } in Jenkins. See
publishing from CI.
For the Maven route, drop the whole target/site/ folder into
the app and the cross-links between the three
reports keep resolving.
The part that actually matters: arguing with the rule
Failing a build on an agreed rule is fine and needs no report. The findings that need a report are the other kind:
- The rule that's wrong for this codebase. Somebody has to say so once, with a reason, somewhere the next person will find it.
- The legacy module with 400 violations you're burning down over a quarter. That's a shared document with a trend, not a gate.
- The finding a reviewer thinks is a false positive. Suppress with a justification, or fix — either way the decision should be attached to the finding.
Published to a URL, all three happen on the report. Select the violation, leave a thread, and it stays anchored there across builds. See commenting on HTML.
Revision diffs give the burndown honestly: two revisions, one diff, which violations actually cleared.
Who can see it
Per report: private, your team, anyone signed in at your domain, or anyone with the link. A findings report names file paths and often quotes source lines, so team or private is usually the right default (domain-gating is Enterprise). See sharing & access control.
Limits
- Entry HTML: 5 MB. A module with thousands of violations can exceed that — scope the report to changed files or to the higher severities, which is what gets read anyway. Assets: 25 MB per file, 250 MB and 500 files total.
- Scripts run, sandboxed (
allow-scripts, noallow-same-origin), so the reports' collapsible sections keep working. - 60 requests/minute per token.
Try it
Comma is free — unlimited reports, unlimited commenters, unlimited revision history.