Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 11 additions & 10 deletions .agents/skills/mono-repo-integration/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
---
name: mono-repo-integration
description: Step-by-step process for merging a previously-standalone Grails plugin repository (e.g. grails-spring-security, grails-redis) into the grails-core monorepo as one or more Gradle subprojects, wiring it into the shared build, publishing, docs, and CI the same way the existing modules are.
license: Apache-2.0
compatibility: opencode, claude, grok, gemini, copilot, cursor, windsurf
metadata:
audience: maintainers
frameworks: grails
versions: 7
---

<!--
SPDX-License-Identifier: Apache-2.0

Expand All @@ -13,16 +24,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
---
name: mono-repo-integration
description: Step-by-step process for merging a previously-standalone Grails plugin repository (e.g. grails-spring-security, grails-redis) into the grails-core monorepo as one or more Gradle subprojects, wiring it into the shared build, publishing, docs, and CI the same way the existing modules are.
license: Apache-2.0
compatibility: opencode, claude, grok, gemini, copilot, cursor, windsurf
metadata:
audience: maintainers
frameworks: grails
versions: 7
---

## What I Do

Expand Down
42 changes: 30 additions & 12 deletions .agents/skills/violation-fixer/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Activate this skill when:

| Plugin | Applied to | Responsibility |
|--------|-----------|----------------|
| `org.apache.grails.gradle.grails-code-style` | Every subproject | Applies Checkstyle and CodeNarc; registers per-project `codeStyle` task; redirects XML reports to root `build/reports/code-style/` |
| `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/` |
Expand All @@ -44,6 +44,7 @@ Activate this skill when:
| `./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, message keys, and RAT provenance |
| `./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 |

Expand All @@ -59,6 +60,9 @@ Activate this skill when:
# Full multi-module check + report
./gradlew aggregateViolations

# Repository conventions only
./gradlew validateRepositoryConventions

# Include test sources in style checks
./gradlew aggregateViolations -Pgrails.code-style.enabled.tests=true

Expand All @@ -85,8 +89,9 @@ After running `aggregateViolations`, these files appear under `build/reports/vio
|------|------|-----------------|
| `build/reports/violations/CODENARC_VIOLATIONS.md` | CodeNarc | Yes |
| `build/reports/violations/CHECKSTYLE_VIOLATIONS.md` | Checkstyle | Yes |
| `build/reports/violations/PMD_VIOLATIONS.md` | PMD | Yes — contains `No violations found!` when PMD is disabled |
| `build/reports/violations/SPOTBUGS_VIOLATIONS.md` | SpotBugs | Yes — contains `No violations found!` when SpotBugs is disabled |
| `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 after RAT succeeds |

After running `aggregateJacocoCoverage`:

Expand All @@ -98,6 +103,16 @@ All reports are inside `build/` and are excluded from version control via `.giti

Each file is a Markdown table grouped by module, with columns: **Class**, **Tool**, **Violation**, **Line**, **Message**.

## 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 `name`, `description`, and `license`, match the directory name, and synchronize its literal path in `AGENTS.md`. |
| GitHub Action | Pin external references to one lowercase 40-hex SHA for that action across workflows and repository-local `action.yml` or `action.yaml` manifests. Local `./...` uses are permitted. Pin Docker `uses`, Docker action `runs.image`, and workflow job/service container images to immutable `@sha256:` digests. |
| Message key | Remove or rename the duplicate logical key in the reported `messages*.properties` file, preserving escaped separators and continuation semantics. |

---

## Tool Details
Expand Down Expand Up @@ -140,9 +155,9 @@ Common violations:
| `FileTabCharacter` | Replace tabs with 4 spaces |
| `NewlineAtEndOfFile` | Ensure file ends with `\n` |

### PMD (Java/Groovy opt-in)
### PMD (Java/Groovy - opt-in)

Enable: `-Pgrails.code-analysis.enabled.pmd=true`
Enable all projects with `-Pgrails.code-analysis.enabled.pmd=true`, or enable selected project paths with `-Pgrails.code-analysis.enabled.pmd.projects=:project-a,:project-b`. The root build enforces PMD only for the clean-baseline paths configured in `gradle.properties`. PMD excludes sources under each project's configured build directory.

Rule file: `build/code-analysis/pmd/pmd.xml`.

Expand Down Expand Up @@ -185,10 +200,12 @@ All properties can be set in `gradle.properties` or passed as `-P` flags:

| Property | Default | Description |
|----------|---------|-------------|
| `grails.code-analysis.enabled.pmd` | `false` | Enable PMD |
| `grails.code-analysis.enabled.spotbugs` | `false` | Enable SpotBugs |
| `grails.code-analysis.enabled.pmd` | `false` | Enable PMD for every project |
| `grails.code-analysis.enabled.pmd.projects` | unset | Comma-separated project paths for selective PMD enablement |
| `grails.code-analysis.enabled.spotbugs` | `false` | Enable SpotBugs for every project |
| `grails.code-analysis.enabled.spotbugs.projects` | unset | Comma-separated project paths for selective SpotBugs enablement |
| `grails.code-analysis.enabled.tests` | `false` | Also analyse test source sets |
| `grails.code-analysis.ignoreFailures` | `false` | Collect reports without failing build |
| `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 |
| `skipCodeStyle` | unset | If present, all analysis tasks are also skipped |

Expand All @@ -211,11 +228,11 @@ All XML reports are consolidated at:
```
build/reports/code-style/ ← XML inputs for style aggregation
├── checkstyle/
│ ├── grails-core-checkstyleMain.xml
│ ├── grails-web-mvc-checkstyleMain.xml
│ ├── <hex-project-path>-checkstyleMain.xml
│ ├── <hex-project-path>-checkstyleCli.xml
│ └── ...
└── codenarc/
├── grails-core-codenarcMain.xml
├── <hex-project-path>-codenarcMain.xml
└── ...

build/reports/code-analysis/ ← XML inputs for analysis aggregation (if enabled)
Expand All @@ -227,7 +244,8 @@ build/reports/violations/ ← Markdown summaries written by aggregateViola
├── CHECKSTYLE_VIOLATIONS.md
├── PMD_VIOLATIONS.md
├── SPOTBUGS_VIOLATIONS.md
├── REPOSITORY_CONVENTIONS.md
└── JACOCO_COVERAGE.md ← written by aggregateJacocoCoverage
```

The module name is derived from the filename: everything before the last `-` (e.g. `grails-core-checkstyleMain.xml` → module `grails-core`).
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.
41 changes: 39 additions & 2 deletions .github/workflows/codeanalysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ jobs:
cache-provider: basic # 'basic' uses the MIT-licensed, open-source cache provider; the default 'enhanced' provider (v6+) is proprietary (Gradle commercial Terms of Use)
develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
- name: "🔎 Check Core Projects"
run: ./gradlew aggregateAnalysisViolations --continue -Pgrails.code-analysis.enabled.pmd=true -Pgrails.code-analysis.enabled.spotbugs=true -Pgrails.code-analysis.ignoreFailures=true
run: ./gradlew aggregateAnalysisViolations --continue
- name: "📤 Upload Reports"
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
Expand All @@ -60,6 +60,24 @@ jobs:
file="build/reports/violations/$report"
[ -f "$file" ] && cat "$file" >> $GITHUB_STEP_SUMMARY || true
done
- name: "🔎 Run Core SpotBugs Advisory"
if: always()
continue-on-error: true
run: |
rm -f build/reports/violations/SPOTBUGS_VIOLATIONS.md
./gradlew aggregateAnalysisViolations --continue "-Pgrails.code-analysis.enabled.pmd.projects=" -Pgrails.code-analysis.enabled.spotbugs=true -Pgrails.code-analysis.ignoreFailures=true
- name: "📤 Upload SpotBugs Advisory Reports"
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: core-spotbugs-advisory-reports
path: build/reports/violations/
- name: "📋 Publish SpotBugs Advisory Report in Job Summary"
if: always()
run: |
echo "## 🔎 SpotBugs Advisory Report - Core Projects" >> $GITHUB_STEP_SUMMARY
file="build/reports/violations/SPOTBUGS_VIOLATIONS.md"
[ -f "$file" ] && cat "$file" >> $GITHUB_STEP_SUMMARY || true
check_gradle_plugin_projects:
name: "Gradle Plugin Projects"
runs-on: ubuntu-24.04
Expand All @@ -80,7 +98,7 @@ jobs:
develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
- name: "🔎 Check Gradle Plugin Projects"
working-directory: grails-gradle
run: ./gradlew aggregateAnalysisViolations --continue -Pgrails.code-analysis.enabled.pmd=true -Pgrails.code-analysis.enabled.spotbugs=true -Pgrails.code-analysis.ignoreFailures=true
run: ./gradlew aggregateAnalysisViolations --continue -Pgrails.code-analysis.enabled.pmd=true -Pgrails.code-analysis.ignoreFailures=true
- name: "📤 Upload Reports"
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
Expand All @@ -95,3 +113,22 @@ jobs:
file="grails-gradle/build/reports/violations/$report"
[ -f "$file" ] && cat "$file" >> $GITHUB_STEP_SUMMARY || true
done
- name: "🔎 Run Gradle Plugin SpotBugs Advisory"
if: always()
continue-on-error: true
working-directory: grails-gradle
run: |
rm -f build/reports/violations/SPOTBUGS_VIOLATIONS.md
./gradlew aggregateAnalysisViolations --continue -Pgrails.code-analysis.enabled.spotbugs=true -Pgrails.code-analysis.ignoreFailures=true
- name: "📤 Upload Gradle Plugin SpotBugs Advisory Reports"
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: gradle-plugin-spotbugs-advisory-reports
path: grails-gradle/build/reports/violations/
- name: "📋 Publish Gradle Plugin SpotBugs Advisory Report in Job Summary"
if: always()
run: |
echo "## 🔎 SpotBugs Advisory Report - Gradle Plugin Projects" >> $GITHUB_STEP_SUMMARY
file="grails-gradle/build/reports/violations/SPOTBUGS_VIOLATIONS.md"
[ -f "$file" ] && cat "$file" >> $GITHUB_STEP_SUMMARY || true
26 changes: 13 additions & 13 deletions .github/workflows/gradle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
distribution: liberica
java-version: 21
- name: "🗄️ Restore dependency jar cache"
uses: actions/cache@v4
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
# Cache only downloaded dependency jars and wrapper distributions, never Grails build outputs.
# Keyed by branch version so each release branch maintains its own warm cache.
Expand Down Expand Up @@ -81,7 +81,7 @@ jobs:
distribution: liberica
java-version: ${{ matrix.java }}
- name: "🗄️ Restore dependency jar cache"
uses: actions/cache@v4
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
# Cache only downloaded dependency jars and wrapper distributions, never Grails build outputs.
# Keyed by branch version so each release branch maintains its own warm cache.
Expand Down Expand Up @@ -143,7 +143,7 @@ jobs:
distribution: liberica
java-version: ${{ matrix.java }}
- name: "🗄️ Restore dependency jar cache"
uses: actions/cache@v4
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
# Cache only downloaded dependency jars and wrapper distributions, never Grails build outputs.
# Keyed by branch version so each release branch maintains its own warm cache.
Expand Down Expand Up @@ -193,7 +193,7 @@ jobs:
distribution: liberica
java-version: ${{ matrix.java }}
- name: "🗄️ Restore dependency jar cache"
uses: actions/cache@v4
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
# Cache only downloaded dependency jars and wrapper distributions, never Grails build outputs.
# Keyed by branch version so each release branch maintains its own warm cache.
Expand Down Expand Up @@ -242,7 +242,7 @@ jobs:
distribution: 'liberica'
java-version: ${{ matrix.java }}
- name: "🗄️ Restore dependency jar cache"
uses: actions/cache@v4
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
# Cache only downloaded dependency jars and wrapper distributions, never Grails build outputs.
# Keyed by branch version so each release branch maintains its own warm cache.
Expand Down Expand Up @@ -323,7 +323,7 @@ jobs:
distribution: liberica
java-version: ${{ matrix.java }}
- name: "🗄️ Restore dependency jar cache"
uses: actions/cache@v4
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
# Cache only downloaded dependency jars and wrapper distributions, never Grails build outputs.
# Keyed by branch version so each release branch maintains its own warm cache.
Expand Down Expand Up @@ -495,7 +495,7 @@ jobs:
distribution: liberica
java-version: ${{ matrix.java }}
- name: "🗄️ Restore dependency jar cache"
uses: actions/cache@v4
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
# Cache only downloaded dependency jars and wrapper distributions, never Grails build outputs.
# Keyed by branch version so each release branch maintains its own warm cache.
Expand Down Expand Up @@ -547,7 +547,7 @@ jobs:
distribution: liberica
java-version: ${{ matrix.java }}
- name: "🗄️ Restore dependency jar cache"
uses: actions/cache@v4
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
# Cache only downloaded dependency jars and wrapper distributions, never Grails build outputs.
# Keyed by branch version so each release branch maintains its own warm cache.
Expand Down Expand Up @@ -628,7 +628,7 @@ jobs:
distribution: liberica
java-version: 21
- name: "🗄️ Restore dependency jar cache"
uses: actions/cache@v4
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
# Cache only downloaded dependency jars and wrapper distributions, never Grails build outputs.
# Keyed by branch version so each release branch maintains its own warm cache.
Expand Down Expand Up @@ -694,7 +694,7 @@ jobs:
distribution: liberica
java-version: 21
- name: "🗄️ Restore dependency jar cache"
uses: actions/cache@v4
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
# Cache only downloaded dependency jars and wrapper distributions, never Grails build outputs.
# Keyed by branch version so each release branch maintains its own warm cache.
Expand Down Expand Up @@ -812,7 +812,7 @@ jobs:
distribution: liberica
java-version: 21
- name: "🗄️ Restore dependency jar cache"
uses: actions/cache@v4
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
# Cache only downloaded dependency jars and wrapper distributions, never Grails build outputs.
# Keyed by branch version so each release branch maintains its own warm cache.
Expand Down Expand Up @@ -923,7 +923,7 @@ jobs:
distribution: liberica
java-version: 21
- name: "🗄️ Restore dependency jar cache"
uses: actions/cache@v4
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
# Cache only downloaded dependency jars and wrapper distributions, never Grails build outputs.
# Keyed by branch version so each release branch maintains its own warm cache.
Expand All @@ -950,7 +950,7 @@ jobs:
compression-level: 0 # Already a zip
- name: "🚀 Publish to Github Pages"
if: github.event_name == 'push'
uses: apache/grails-github-actions/deploy-github-pages@asf
uses: apache/grails-github-actions/deploy-github-pages@fe90f651c47c5230cdd70b6fee0e0d250fea30ac # asf
env:
GH_TOKEN: ${{ secrets.GRAILS_GHTOKEN }}
GRADLE_PUBLISH_RELEASE: 'false'
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/groovy-joint-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ jobs:
runs-on: ubuntu-latest
services:
mongodb:
image: mongo:8
image: mongo@sha256:951c2ff9fc6bdb6cb89b1dfea4a0e8ae3ee4fb287c0bf579b2bba54c7803f75d # mongo:8
ports:
- 27017:27017
options: >-
Expand Down Expand Up @@ -163,4 +163,4 @@ jobs:
-PskipMicronautProjects
-PmaxTestParallel=3
env:
GRAILS_INCLUDE_MAVEN_LOCAL: true
GRAILS_INCLUDE_MAVEN_LOCAL: true
2 changes: 1 addition & 1 deletion .github/workflows/release-close.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ jobs:
ref: ${{ env.TAG }}
token: ${{ secrets.GITHUB_TOKEN }}
- name: "⚙️ Run post-release"
uses: apache/grails-github-actions/post-release@asf
uses: apache/grails-github-actions/post-release@fe90f651c47c5230cdd70b6fee0e0d250fea30ac # asf
env:
PR_LABELS: skip-changelog
RELEASE_SCRIPT_PATH: '.github/scripts/setSnapshotGrailsVersion.sh'
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release-publish-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ jobs:
# The docs themselves have no code dependency on Micronaut.
run: ./gradlew grails-doc:build -PgithubBranch=${TARGET_BRANCH}
- name: "🚀 Publish to GitHub Pages"
uses: apache/grails-github-actions/deploy-github-pages@asf
uses: apache/grails-github-actions/deploy-github-pages@fe90f651c47c5230cdd70b6fee0e0d250fea30ac # asf
env:
GH_TOKEN: ${{ secrets.GRAILS_GHTOKEN }} # To be able to push to grails-website repo
GRADLE_PUBLISH_RELEASE: 'true'
Expand Down
Loading
Loading