--- name: violation-fixer description: Guide for running, interpreting, and fixing code style and analysis violations in grails-core using GrailsCodeStylePlugin, GrailsCodeAnalysisPlugin, and GrailsViolationAggregationPlugin - covering CodeNarc, Checkstyle, PMD, SpotBugs, and JaCoCo license: Apache-2.0 --- ## What I Do - Explain how `GrailsCodeStylePlugin`, `GrailsCodeAnalysisPlugin`, and `GrailsViolationAggregationPlugin` enforce code quality across all 60+ modules. - Guide you through running style and analysis checks, interpreting the per-tool Markdown violation reports, and fixing each class of violation. - Describe which tools are always-on vs. opt-in, how to configure them via Gradle properties, and which violations can be auto-fixed. ## When to Use Me Activate this skill when: - Running `./gradlew aggregateViolations` and interpreting the resulting `*_VIOLATIONS.md` files. - Fixing CodeNarc, Checkstyle, PMD, SpotBugs, or Spotless violations reported in those files. - Configuring code style or analysis tools across the repo (enabling/disabling tools or adjusting rule files). - Preparing a commit - the plugin output must be clean before merging. --- ## Plugin Overview | Plugin | Applied to | Responsibility | |--------|-----------|----------------| | `org.apache.grails.gradle.grails-code-style` | Every subproject | Applies Checkstyle, CodeNarc, and code analysis; registers per-project `codeStyle` task; redirects XML reports to root `build/reports/code-style/` | | `org.apache.grails.gradle.grails-code-analysis` | Every subproject | Applies PMD and SpotBugs (both opt-in); registers per-project `codeAnalysis` task; redirects XML reports to root `build/reports/code-analysis/` | | `org.apache.grails.gradle.grails-jacoco` | Every subproject | Applies JaCoCo; wires `jacocoTestReport` to run after each `test` task | | `org.apache.grails.gradle.grails-violation-aggregation` | **Root project only** | Registers `aggregateViolations` and `aggregateJacocoCoverage` tasks; writes Markdown summaries to `build/reports/violations/` | --- ## Key Tasks | Task | Scope | Description | |------|-------|-------------| | `./gradlew codeStyle` | per-project | Runs Checkstyle and CodeNarc for that project | | `./gradlew codeAnalysis` | per-project | Runs PMD and/or SpotBugs for that project (when enabled) | | `./gradlew aggregateViolations` | root | Runs all checks across every module, then writes `*_VIOLATIONS.md` to `build/reports/violations/` | | `./gradlew validateRepositoryConventions` | root | Validates canonical skill metadata, AGENTS paths, GitHub Action pins, and message keys. RAT provenance is the separate `rat` task, which `aggregateViolations` runs | | `./gradlew cleanViolationReports` | root | Deletes analyzer XML reports, their markers, and the aggregate Markdown so the next aggregate run re-analyzes every module. The root `clean` runs it | | `./gradlew aggregateJacocoCoverage` | root | Runs JaCoCo reports across every module, then writes `JACOCO_COVERAGE.md` to `build/reports/violations/` | | `./gradlew codenarcFix` | per-project | Auto-fixes a subset of CodeNarc violations | ### Quick commands ```bash # Check a single module (style only) ./gradlew :grails-core:codeStyle # Check a single module (analysis - enable through the module extension or an override) ./gradlew :grails-core:codeAnalysis -Pgrails.code-analysis.enabled.pmd=true # Full multi-module check + report (use --continue so the reports are written even when an analyzer fails) ./gradlew aggregateViolations --continue # Force every module to be re-analyzed instead of reusing UP-TO-DATE analyzer results ./gradlew cleanViolationReports aggregateViolations --continue # Repository conventions only ./gradlew validateRepositoryConventions # Include test sources in style checks ./gradlew aggregateViolations -Pgrails.code-style.enabled.tests=true # Include test sources in analysis ./gradlew aggregateViolations -Pgrails.code-analysis.enabled.tests=true # Ignore failures (collect reports without failing the build) ./gradlew aggregateViolations -Pgrails.code-style.ignoreFailures=true -Pgrails.code-analysis.ignoreFailures=true # Auto-fix some CodeNarc violations before running checks ./gradlew codenarcFix codeStyle # JaCoCo coverage report ./gradlew aggregateJacocoCoverage ``` --- ## Output Files After running `aggregateViolations`, these files appear under `build/reports/violations/` in the **root project build directory**: | File | Tool | Always generated | |------|------|-----------------| | `build/reports/violations/CODENARC_VIOLATIONS.md` | CodeNarc | Yes | | `build/reports/violations/CHECKSTYLE_VIOLATIONS.md` | Checkstyle | Yes | | `build/reports/violations/PMD_VIOLATIONS.md` | PMD | Yes - reports `PMD is disabled.` when PMD is disabled | | `build/reports/violations/SPOTBUGS_VIOLATIONS.md` | SpotBugs | Yes - reports `SpotBugs is disabled.` when SpotBugs is disabled | | `build/reports/violations/REPOSITORY_CONVENTIONS.md` | Repository conventions | Yes - lists skill, Action, or message-key failures. Ordered after `rat` in the `aggregateViolations` lane | After running `aggregateJacocoCoverage`: | File | Tool | Generated | |------|------|-----------| | `build/reports/violations/JACOCO_COVERAGE.md` | JaCoCo | Only when at least one subproject has a JaCoCo CSV report | All reports are inside `build/` and are excluded from version control via `.gitignore`. A clean run produces `No violations found! 🎉` in each style file. **The build must be clean before committing.** Each aggregated style or analysis report begins with `Modules analyzed:`, which names only modules that contributed data. Each file is a Markdown table grouped by module, with columns: **Class**, **Tool**, **Violation**, **Line**, **Message**. Only the aggregate lane (`aggregateViolations`, `aggregateStyleViolations`, `aggregateAnalysisViolations`) writes these Markdown files. Running an analyzer task directly, such as `./gradlew :grails-core:checkstyleMain`, produces only that task's own XML report and deliberately leaves the aggregate Markdown untouched, so a partial run can never overwrite an authoritative full-repository report. Because the writer is part of that lane rather than a per-task finalizer, pass `--continue` when you expect violations, otherwise the failing analyzer stops the build before the report explaining the failure is written. Analyzers for unchanged modules stay UP-TO-DATE between aggregate runs and their previous results are aggregated; run `cleanViolationReports` (or the root `clean`) first to force a full re-analysis. ## Repository Conventions Run `./gradlew validateRepositoryConventions` to write `build/reports/violations/REPOSITORY_CONVENTIONS.md`. Fix the reported source rather than suppressing the validation. | Finding | Fix | |---------|-----| | Skill | Start `SKILL.md` with YAML front matter, supply string `name`, `description`, and `license` values, use a valid directory name that matches `name`, and keep names unique. Every skill path that `AGENTS.md` references must exist, but `AGENTS.md` is not required to index every skill. | | GitHub Action | Pin third-party references to one lowercase 40-hex immutable reference for that action across workflows and repository-local `action.yml` or `action.yaml` manifests. A 40-hex value may be a commit or annotated-tag object SHA. `actions/*` must use the full tag of a release (`vX.Y.Z`) and `apache/*` a version or branch reference, never a 40-hex SHA, and local `./...` uses are permitted. Pin Docker `uses`, Docker action `runs.image`, and workflow job/service container images to literal immutable `name@sha256:` values. | | Message key | Remove or rename the duplicate logical key in the reported `grails-app/i18n/**/*.properties` file, preserving escaped separators and continuation semantics. | --- ## Tool Details ### CodeNarc (Groovy - always enabled) Rule file: `build/code-style/codenarc/codenarc.groovy` (generated by the plugin during setup; not intended to be edited directly). Most common violations and how to fix them: | Rule | Fix | |------|-----| | `UnnecessaryGString` | Replace `"plain string"` with `'plain string'` | | `UnnecessarySemicolon` | Remove trailing `;` | | `SpaceBeforeOpeningBrace` | Add space before `{` → `method() {` | | `SpaceAroundMapEntryColon` | `[key: value]` not `[key:value]` | | `ConsecutiveBlankLines` | Collapse 3+ blank lines to 2 | | `ClassStartsWithBlankLine` | Remove blank line right after `class Foo {` | | `NoWildcardImports` | Expand `import org.foo.*` to explicit imports | | `UnusedImport` | Remove imports not referenced in the file | | `MethodName` | Method names must be camelCase (not `snake_case`) | | `VariableName` | Variable names must be camelCase | | `LineLength` | Keep lines ≤ 200 chars (default) | Auto-fixable via `codenarcFix`: `ClassStartsWithBlankLine`, `SpaceAroundMapEntryColon`, `UnnecessaryGString`, `UnnecessarySemicolon`, `SpaceBeforeOpeningBrace`, `ConsecutiveBlankLines`. ### Checkstyle (Java - always enabled) Rule file: `build/code-style/checkstyle/checkstyle.xml`. Common violations: | Rule | Fix | |------|-----| | `ImportOrder` | Re-order imports: `java|javax`, then `groovy`, then `jakarta`, then blank, then `io.spring|org.springframework`, then `grails|org.apache.grails|org.grails`, then static imports | | `AvoidStarImport` | Use explicit class imports | | `UnusedImports` | Remove unused imports | | `WhitespaceAround` | Add spaces around operators and keywords | | `NeedBraces` | Add `{}` to single-statement `if`/`for`/`while` | | `FileTabCharacter` | Replace tabs with 4 spaces | | `NewlineAtEndOfFile` | Ensure file ends with `\n` | ### PMD (Java/Groovy - opt-in) Enable PMD in each clean module's `build.gradle` with `grailsCodeAnalysis { enablePmd() }`. Use `-Pgrails.code-analysis.enabled.pmd.projects=:project-a,:project-b` to also enable selected project paths for a baseline run. Use `-Pgrails.code-analysis.enabled.pmd=true` or `=false` to switch PMD on or off for every project; when set, it wins over both the module opt-ins and the `.projects` list. PMD excludes sources under each project's configured build directory. `enablePmd()` configures PMD immediately, so customize the `pmd*` tasks directly after it, for example `tasks.named('pmdMain') { ... }`. Rule file: `build/code-analysis/pmd/pmd.xml`. ### SpotBugs (Java bytecode - opt-in) Enable SpotBugs in each clean module's `build.gradle` with `grailsCodeAnalysis { enableSpotbugs() }`. Use `-Pgrails.code-analysis.enabled.spotbugs.projects=:project-a,:project-b` to also enable selected project paths for a baseline run. Use `-Pgrails.code-analysis.enabled.spotbugs=true` or `=false` to switch SpotBugs on or off for every project; when set, it wins over both the module opt-ins and the `.projects` list. `enableSpotbugs()` configures SpotBugs immediately, so customize the `spotbugs*` tasks directly after it, for example `tasks.named('spotbugsMain') { ... }`. Runs at `Effort.MAX` / `Confidence.HIGH`. Only high-confidence bugs are reported. ### Spotless (Java auto-formatting - opt-in) Enable: `-Pgrails.code-style.enabled.spotless=true` Uses Palantir Java Format. Can auto-fix by running: ```bash ./gradlew spotlessApply ``` --- ## Configuration Properties All properties can be set in `gradle.properties` or passed as `-P` flags: ### `grails-code-style` plugin (Checkstyle + CodeNarc) | Property | Default | Description | |----------|---------|-------------| | `grails.code-style.enabled.checkstyle` | `true` | Enable Checkstyle | | `grails.code-style.enabled.codenarc` | `true` | Enable CodeNarc | | `grails.code-style.enabled.spotless` | `false` | Enable Spotless | | `grails.code-style.enabled.tests` | `false` | Also check test source sets | | `grails.code-style.ignoreFailures` | `false` | Collect reports without failing build | | `grails.code-style.codenarc.fix` | `false` | Run `codenarcFix` before CodeNarc tasks | | `grails.codestyle.dir.checkstyle` | (auto) | Custom path to Checkstyle config dir | | `grails.codestyle.dir.codenarc` | (auto) | Custom path to CodeNarc config dir | | `skipCodeStyle` | unset | If present, every static check is skipped: CodeNarc and Checkstyle, and PMD and SpotBugs as well. The aggregate reports say the tools were skipped | ### `grails-code-analysis` plugin (PMD + SpotBugs) Enable PMD and SpotBugs primarily in each module's `build.gradle`: ```groovy grailsCodeAnalysis { enablePmd() enableSpotbugs() } ``` The Gradle properties below are all-project or selected-project overrides for baseline runs. | Property | Default | Description | |----------|---------|-------------| | `grails.code-analysis.enabled.pmd` | unset | When set, `true` or `false` overrides PMD for every project, including `enablePmd()` opt-ins and the `.projects` list | | `grails.code-analysis.enabled.pmd.projects` | unset | Also enable PMD for comma-separated project paths, unless the all-project property is set | | `grails.code-analysis.enabled.spotbugs` | unset | When set, `true` or `false` overrides SpotBugs for every project, including `enableSpotbugs()` opt-ins and the `.projects` list | | `grails.code-analysis.enabled.spotbugs.projects` | unset | Also enable SpotBugs for comma-separated project paths, unless the all-project property is set | | `grails.code-analysis.enabled.tests` | `false` | Also analyse test source sets | | `grails.code-analysis.ignoreFailures` | `false` | Collect ordinary findings without failing the build; missing expected XML always fails | | `grails.code-analysis.dir.pmd` | (auto) | Custom path to PMD config dir | | `skipCodeAnalysis` | unset | If present, PMD and SpotBugs are skipped while CodeNarc and Checkstyle still run. `skipCodeStyle` also skips them | --- ## Fixing Violations Workflow 1. Run `./gradlew aggregateViolations -Pgrails.code-style.ignoreFailures=true -Pgrails.code-analysis.ignoreFailures=true` 2. Open `build/reports/violations/CODENARC_VIOLATIONS.md` and `build/reports/violations/CHECKSTYLE_VIOLATIONS.md` to see all issues by module 3. For CodeNarc, run `./gradlew codenarcFix` to auto-fix what it can 4. Fix remaining violations manually using the table above 5. Re-run `./gradlew aggregateViolations` and confirm files contain `No violations found! 🎉` 6. The reports are inside `build/` and do not need to be deleted before committing --- ## Reports Directory Structure All XML reports are consolidated at: ``` build/reports/code-style/ ← XML inputs for style aggregation ├── checkstyle/ │ ├── -checkstyleMain.xml │ ├── -checkstyleCli.xml │ └── ... └── codenarc/ ├── -codenarcMain.xml └── ... build/reports/code-analysis/ ← XML inputs for analysis aggregation (if enabled) ├── pmd/ └── spotbugs/ build/reports/violations/ ← Markdown summaries written by aggregateViolations ├── CODENARC_VIOLATIONS.md ├── CHECKSTYLE_VIOLATIONS.md ├── PMD_VIOLATIONS.md ├── SPOTBUGS_VIOLATIONS.md ├── REPOSITORY_CONVENTIONS.md └── JACOCO_COVERAGE.md ← written by aggregateJacocoCoverage ``` The filename prefix is the UTF-8 hexadecimal encoding of the full Gradle project path. Aggregation decodes it back to paths such as `:grails-core`, preventing nested projects with the same leaf name from colliding.