Automate repository conventions and stage static analysis enforcement - #16025
Automate repository conventions and stage static analysis enforcement#16025jamesfredley wants to merge 2 commits into
Conversation
Turn recurring objective review feedback into deterministic Gradle checks, pin executable workflow dependencies, and enforce the clean PMD baseline. Preserve report-only analysis for the quantified legacy backlog and document the remaining cleanup through focused follow-up issues. Assisted-by: opencode:gpt-5.6-sol
There was a problem hiding this comment.
Pull request overview
This PR adds deterministic build-time enforcement for previously manual repository hygiene checks (repository conventions, immutable GitHub Actions/Docker pins, message key uniqueness) and introduces staged, allowlist-based static analysis enforcement (PMD now blocking only where clean), without changing Grails runtime behavior.
Changes:
- Introduces
validateRepositoryConventions(RAT + skills/AGENTS/workflow/action YAML + container pins + messages key uniqueness) and wires it into aggregation tasks for the canonical root. - Refactors violation aggregation to use task-owned report outputs via root-relative marker files, cleaning scheduled outputs and failing on genuinely missing reports.
- Stages static-analysis enforcement via per-project PMD/SpotBugs allowlists and updates CI workflows + agent guidance accordingly.
Reviewed changes
Copilot reviewed 23 out of 23 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| grails-spring-security/ui/plugin/grails-app/i18n/messages.spring-security-ui.properties | Removes a duplicate message key entry to satisfy uniqueness validation. |
| gradle/rat-root-config.gradle | Aligns RAT excludes with agent-local state/config files so provenance checks remain consistent. |
| gradle.properties | Adds the staged PMD enforcement allowlist for currently clean modules. |
| build-logic/plugins/src/test/groovy/org/apache/grails/buildsrc/RepositoryConventionsTaskSpec.groovy | Adds TestKit coverage for repository conventions validation scenarios. |
| build-logic/plugins/src/test/groovy/org/apache/grails/buildsrc/GrailsViolationAggregationPluginSpec.groovy | Expands TestKit coverage for marker-based aggregation, allowlists, and disabled-tool reporting. |
| build-logic/plugins/src/test/groovy/org/apache/grails/buildsrc/GrailsCodeStylePluginSpec.groovy | Verifies code style plugin also registers code analysis surface. |
| build-logic/plugins/src/test/groovy/org/apache/grails/buildsrc/GrailsCodeAnalysisPluginSpec.groovy | Adds tests for allowlists and build-dir exclusions in PMD configuration. |
| build-logic/plugins/src/main/groovy/org/apache/grails/buildsrc/RepositoryConventionsTask.groovy | Implements the repository conventions validator (skills, actions, container pins, messages keys). |
| build-logic/plugins/src/main/groovy/org/apache/grails/buildsrc/GrailsViolationAggregationPlugin.groovy | Reworks style/analysis aggregation to use markers, staged allowlists, and deterministic report lifecycle. |
| build-logic/plugins/src/main/groovy/org/apache/grails/buildsrc/GrailsCodeStylePlugin.groovy | Emits marker files for style reports and applies code analysis plugin alongside style. |
| build-logic/plugins/src/main/groovy/org/apache/grails/buildsrc/GrailsCodeAnalysisPlugin.groovy | Adds per-project allowlists, marker emission, and build-dir source exclusion for PMD. |
| build-logic/plugins/src/main/groovy/org/apache/grails/buildsrc/GradleUtils.groovy | Adds project-path encoding for collision-free report naming + marker helpers. |
| build-logic/plugins/build.gradle | Adds SnakeYAML dependency to support safe YAML parsing in repository conventions checks. |
| AGENTS.md | Documents validateRepositoryConventions and updates skill inventory/guidance for staged analysis behavior. |
| .github/workflows/vulnerability-scan.yml | Pins external actions to immutable SHAs. |
| .github/workflows/release.yml | Pins reusable actions to SHAs, disables checkout credential persistence, and aligns ZIP excludes with RAT. |
| .github/workflows/release-publish-docs.yml | Pins reusable actions to immutable SHAs. |
| .github/workflows/release-close.yml | Pins reusable actions to immutable SHAs. |
| .github/workflows/groovy-joint-workflow.yml | Pins Mongo service image to an immutable digest. |
| .github/workflows/gradle.yml | Pins cache and deploy actions to immutable SHAs. |
| .github/workflows/codeanalysis.yml | Switches to staged enforcement + adds SpotBugs advisory passes with distinct artifacts. |
| .agents/skills/violation-fixer/SKILL.md | Updates guidance to reflect staged analysis and new conventions task/report semantics. |
| .agents/skills/mono-repo-integration/SKILL.md | Moves skill YAML front matter to the top so it conforms to the new validator. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## 8.0.x #16025 +/- ##
==================================================
+ Coverage 51.4404% 51.4507% +0.0103%
- Complexity 17728 17731 +3
==================================================
Files 2039 2039
Lines 95497 95507 +10
Branches 16564 16564
==================================================
+ Hits 49124 49139 +15
+ Misses 39074 39067 -7
- Partials 7299 7301 +2 🚀 New features to boost your workflow:
|
|
@jdaugherty @matrei @borinquenkid @sbglasius this plus the newly created issues, should get us closer to PRs automatically following the expected syntax and passing all of the automated checkers. This PR was the next logical step. Should save us immense time on PR reviews, once we get this gradually in place. |
This is a good beginning. In a follow up start adding SKILL.md for automating the clean up of some of these issues. The CodeNarc script is broken. |
|
I'd like to review this before merging, but am currently traveling. I can take a look at this by Friday. |
Sanitize convention violations only at output boundaries and decode message property bundles explicitly as UTF-8. Add TestKit coverage for both behaviors. Assisted-by: opencode:gpt-5.6-sol
✅ All tests passed ✅🏷️ Commit: 8bc7b8f Learn more about TestLens at testlens.app. |
|
@jdaugherty no rush and you will find this somewhat incomplete, with several things carved out to issues. I am sure it needs some polish somewhere. @borinquenkid Agreed, this adds two skills.md files, but more will be better. |
Description
This PR turns recurring, objective review feedback into deterministic repository checks while deliberately leaving subjective engineering judgment in review.
The starting point was an audit of recent merged and proposed changes in the
#16012through#15688PR-number window. The recurring mechanical concerns were action immutability, static-analysis drift, duplicate message keys, agent-skill metadata drift, report trustworthiness, and license/provenance checks. Repeating those comments by hand does not scale. This change creates a ratchet: enforce the clean baseline now, preserve compatibility for broader opt-in analysis, and expand enforcement only as the recorded backlog is resolved.No Grails runtime API or application behavior changes in this PR.
What this moves forward
1. PMD becomes blocking where it is already clean
PMD is now enabled by full Gradle project path for the four modules that produce clean, non-empty PMD XML today:
:grails-data-graphql-core:grails-data-mongodb-spring-data:grails-datasource:grails-testing-support-coreThe implementation supports both staged project allowlists and the existing global opt-in flags. It also preserves
grails.code-analysis.ignoreFailures=truefor report-only baseline runs.Analysis and style reports are now tied to exact task-owned outputs instead of scanning a broad directory. Before analyzers run, active outputs are cleaned. Each analyzer that actually executes writes a relocatable marker containing the configured XML output's root-relative path, so custom directories and filenames remain associated without serializing an absolute machine path. A genuinely missing report always fails while an unscheduled or legitimate
NO-SOURCEtask is omitted. One writer task owns each pair of Markdown summaries, runs after scheduled analyzers, and is reached through a no-output finalizer even when an analyzer fails. Direct single-module analyzer tasks therefore remain usable, consumers cannot race a second report writer, and build-cache relocation remains portable. Marker filenames encode the full project path, so nested projects with the same leaf name cannot collide.Generated files below each project's configured build directory are excluded from PMD inputs without excluding normal sources when a checkout ancestor happens to be named
build.2. Repository conventions have one public Gradle surface
./gradlew validateRepositoryConventionsnow validates the canonical repository root and writesbuild/reports/violations/REPOSITORY_CONVENTIONS.md.It checks these scenarios:
.agents/skills/*/SKILL.mdfiles start with valid top-level YAML front matter containing stringname,description, andlicensevalues.AGENTS.md, and every listed path exists.SafeConstructor.usesreference is pinned to a lowercase 40-character commit SHA.uses, Docker actionruns.image, and workflow job/service container images use immutable lowercasesha256digests rather than mutable tags.messages*.propertiesare unique, including escaped separators and continued lines.The task is wired into
aggregateStyleViolationsandaggregateViolationsonly for the canonical root. Independent builds such asgrails-gradleretain their existing aggregate style behavior and do not require a localAGENTS.md.3. GitHub Actions are immutable by construction
Remaining symbolic external action references were replaced with verified commit SHAs, including Checkout, Cache, Setup Java, and the shared Grails action repository. The Mongo service image is pinned to its Docker Hub manifest digest rather than the mutable
mongo:8tag.The new validator covers ordinary steps, reusable workflow jobs, quoted keys, inline YAML maps, YAML aliases, and repository-local composite actions in any
action.ymloraction.yamllocation, including a path segment namedbuild. Local./...references remain valid, but any external actions used inside their manifests are still validated.4. Release provenance matches RAT provenance
Agent-local state, configuration symlinks, and guidance files excluded from RAT are now excluded from the source ZIP with matching patterns. The source checkout no longer persists GitHub credentials, and Info-ZIP stores any remaining repository symlinks as links rather than dereferencing their targets. This prevents an excluded file or linked external path from being shipped unaudited.
5. Existing deterministic drift is repaired
spring.security.ui.forgotPassword.email.line4message key.AGENTS.md.Why the rollout is staged
Enabling every analyzer everywhere in this PR would require thousands of unrelated source edits and would make the convention work impossible to review safely. This PR goes as far as the repository can go without mixing a broad cleanup campaign into the build-policy change:
Subjective concerns such as architecture, compatibility judgment, semantic documentation quality, test sufficiency, and quote/type preferences remain review-only. They are documented for agents but are not encoded as brittle mechanical rules.
Remaining work and follow-up issues
IndexOutOfBoundsExceptionfailures. Tool execution must be reliable before enforcement.grails-gradle-modelThe clean PMD baseline was corrected during verification:
grails-scaffolding,grails-spring-security, andgrails-spring-security-aclinitially appeared as zero-finding modules, but their PMD tasks produce no XML because they have no PMD-analyzable Java source. They are not presented as clean and are not allowlisted.Suggested next steps
grails-gradle-modelcleanup in Resolve existing PMD findings in grails-gradle-model #16016.Verification
build-logic:..\gradlew.bat build --no-watch-fs --no-daemongrails-gradle:.\gradlew.bat aggregateStyleViolations --no-watch-fs --no-daemon --max-workers=2grails-gradlePMD baseline:.\gradlew.bat aggregateAnalysisViolations --continue "-Pgrails.code-analysis.enabled.pmd=true" "-Pgrails.code-analysis.ignoreFailures=true" --rerun-tasks --no-watch-fs --no-daemon --max-workers=2; it reports only the 113 maintained-source findings in:grails-gradle-modeland omits three legitimateNO-SOURCEtasks..\gradlew.bat :grails-datasource:pmdMain --no-watch-fs --no-daemon --max-workers=2; the single requested analyzer and its finalizer succeed without false missing reports for other allowlisted modules..\gradlew.bat aggregateAnalysisViolations --continue --no-daemon --max-workers=2 --no-watch-fscontinue-on-erroradvisory passes for the root andgrails-gradle, each uploaded under a distinct artifact name after the enforced PMD reports..\gradlew.bat aggregateAnalysisViolations --continue "-Pgrails.code-analysis.enabled.pmd=true" "-Pgrails.code-analysis.ignoreFailures=true" --no-watch-fs --no-daemon --max-workers=2; 5,551 maintained-source findings across 53 source-bearing modules..\gradlew.bat clean aggregateViolations :grails-test-report:check --continue --no-daemon --no-parallel --max-workers=2 --no-watch-fsHello\r\nWorld!versusHello\nWorld!mismatch inMailServiceSpec. The unchanged SiteMesh async multiple-layout scenario received a transient HTTP 500 during the full run; an isolated serial rerun ofEndToEndSpecthen passed all 14 tests. Neither example implementation nor test is changed by this PR.aggregateViolationsafter all review fixes and rebasing ontoorigin/8.0.x: 751 tasks completed successfully.SpotBugs is disabled.NO-SOURCE, custom report directories and filenames, portable root-relative markers, single-writer ordering, duplicate nested project names, top-level skill metadata typing, duplicate YAML keys, malformed YAML, semantic versus ordinaryusesfields, recursive build-path actions, repository path escapes, case-insensitive Docker metadata, immutable container images, and configuration-cache reuse are covered by TestKit.The Windows validation run disabled Gradle file-system watching because Gradle's native
gradle-fileevents.dllcrashed a long-lived daemon. The same checks passed with--no-watch-fs; no product or test failure was hidden.Open PR #15977 also changes the
AGENTS.mdskill inventory. It is not a semantic duplicate of this work, but the documentation overlap should be resolved when either branch is rebased.Contributor Checklist
Issue and Scope
8.0.x, the current major-development branch.Code Quality
./gradlew build --rerun-taskscommand was not run because no runtime module behavior changed. The mandatory:grails-test-report:checkmatrix did run: 3,617 integration tests reported the two unchanged example failures described above. A serial mail run confirmed the Windows CRLF mismatch, while an isolated SiteMesh rerun passed all 14 tests. The affected build-logic suite, independentgrails-gradlesurfaces, and 751-task root aggregate gate pass.Licensing and Attribution
Documentation
AGENTS.mdand the violation-fixer skill documents the new commands, properties, reports, and remediation paths.