You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@grails.apache.org by jd...@apache.org on 2026/07/17 01:55:41 UTC
(grails-core) 01/01: Restructure skills publishing into separate gradle projects
This is an automated email from the ASF dual-hosted git repository.
jdaugherty pushed a commit to branch rework/publish-grails-skills
in repository https://gitbox.apache.org/repos/asf/grails-core.git
commit e1d358be7849035f9f9455e26c124dffc8f0df89
Author: James Daugherty <jd...@jdresources.net>
AuthorDate: Thu Jul 2 08:53:14 2026 -0400
Restructure skills publishing into separate gradle projects
---
.agents/skills/README.md | 35 ++++++------
build.gradle | 62 ----------------------
gradle/publish-root-config.gradle | 3 ++
gradle/rat-root-config.gradle | 2 +-
grails-skills/developer/build.gradle | 29 ++++++++++
.../developer/src/main/resources/SKILL.md | 1 +
grails-skills/upgrade-guide-8/build.gradle | 29 ++++++++++
.../upgrade-guide-8/src/main/resources/SKILL.md | 1 +
settings.gradle | 7 +++
skills/grails-8-upgrade/SKILL.md | 1 -
skills/grails-developer/SKILL.md | 1 -
11 files changed, 89 insertions(+), 82 deletions(-)
diff --git a/.agents/skills/README.md b/.agents/skills/README.md
index c727193d0d..a951e40c71 100644
--- a/.agents/skills/README.md
+++ b/.agents/skills/README.md
@@ -6,28 +6,29 @@ Licensed to the Apache Software Foundation (ASF) under one or more contributor l
# Grails Agent Skills
-The canonical Grails agent skills live in this directory. Skills that are only useful while working in the Grails framework repository stay here and are not published through SkillsJars.
+The canonical Grails agent skills live in this directory. Skills that are only useful while working in the Grails framework repository stay here and are not published.
-The repository root `skills/` directory contains normal skill directories for app-facing skills that are useful when building or upgrading end-user Grails applications. Each published directory contains only a `SKILL.md` symbolic link back to the canonical source in `.agents/skills`, so [SkillsJars](https://www.skillsjars.com/) can discover and package the skills without duplicating content. SkillsJars deploys from public GitHub repositories by scanning `skills/**/SKILL.md` and then publ [...]
+App-facing skills that are useful when building or upgrading end-user Grails applications are published to Maven Central as individual artifacts. Each published skill has its own Gradle project under `grails-skills/`. The project packages the skill's `SKILL.md` into a jar via a symlink back to the canonical source in this directory, so there is no content duplication.
-| Published skill | Source skill | Use |
-|-----------------|--------------|-----|
-| `grails-developer` | `.agents/skills/grails-developer` | Building current Grails web applications, REST APIs, GORM models, controllers, services, views, plugins, and tests |
-| `grails-8-upgrade` | `.agents/skills/grails-8-upgrade` | Upgrading Grails applications from Grails 7.x to Grails 8 |
+| Published skill | Gradle project | Maven coordinates | Source skill | Use |
+|-----------------|----------------|-------------------|--------------|-----|
+| `grails-developer` | `grails-skills/developer` | `org.apache.grails.skills:grails-developer` | `.agents/skills/grails-developer` | Building current Grails web applications, REST APIs, GORM models, controllers, services, views, plugins, and tests |
+| `grails-8-upgrade` | `grails-skills/upgrade-guide-8` | `org.apache.grails.skills:grails-8-upgrade` | `.agents/skills/grails-8-upgrade` | Upgrading Grails applications from Grails 7.x to Grails 8 |
-The repository-specific `hibernate-developer`, `test-fixer`, and `violation-fixer` skills are intentionally not linked from `skills/` because they are for Grails framework development. Contributors already receive them from this repository when working on Grails core.
+The repository-specific `hibernate-developer`, `test-fixer`, `violation-fixer`, `groovy-developer`, and `java-developer` skills are intentionally not published because they are for Grails framework development. Contributors already receive them from this repository when working on Grails core.
-Run this check before publishing or updating a skill:
+Each `grails-skills/<name>/src/main/resources/SKILL.md` is a symbolic link to the canonical source under `.agents/skills/<name>/SKILL.md`. Edit the canonical source; the published jar always packages the current content.
-```bash
-./gradlew verifySkillsJarsSources
-```
+To add a new published skill:
-After changes are merged to the public branch, publish them from SkillsJars by submitting:
+1. Add the canonical `SKILL.md` under `.agents/skills/<name>/`.
+2. Create a Gradle project under `grails-skills/<project>/` with a `build.gradle` that applies `java` and `org.apache.grails.buildsrc.publish`, sets `group = 'org.apache.grails.skills'`, and declares `pomArtifactId`, `pomTitle`, and `pomDescription`.
+3. Symlink `grails-skills/<project>/src/main/resources/SKILL.md` to the canonical source.
+4. Register the project in `settings.gradle` and add it to the `publishedProjects` list in `gradle/publish-root-config.gradle`.
-| Field | Value |
-|-------|-------|
-| GitHub Org | `apache` |
-| GitHub Repo | `grails-core` |
+Build a skill jar locally with:
-Expected coordinates use the `apache__grails-core__<skill-name>` artifact pattern, for example `com.skillsjars:apache__grails-core__grails-developer:<date>-<commit>`.
+```bash
+./gradlew :grails-skills-developer:build
+./gradlew :grails-skills-developer:publishToMavenLocal
+```
diff --git a/build.gradle b/build.gradle
index 6e5908e012..36c73e7c5d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -119,68 +119,6 @@ tasks.register('publishAllToMavenLocal') {
}
}
-tasks.register('verifySkillsJarsSources') {
- description = 'Verifies the SkillsJars publication paths point at the app-facing Grails skill sources.'
- group = 'verification'
-
- def skillsJarsDir = layout.projectDirectory.dir('skills')
- def publishedSkills = [
- 'grails-8-upgrade': '../../.agents/skills/grails-8-upgrade/SKILL.md',
- 'grails-developer': '../../.agents/skills/grails-developer/SKILL.md',
- ]
-
- inputs.dir(layout.projectDirectory.dir('.agents/skills'))
- inputs.dir(skillsJarsDir)
-
- doLast {
- if (!skillsJarsDir.asFile.directory) {
- throw new GradleException('The SkillsJars publication path must be a skills directory containing app-facing skill symlinks.')
- }
-
- def actualSkillNames = skillsJarsDir.asFile.listFiles().findAll { File file -> file.directory }.collect { File file -> file.name } as Set
- def expectedSkillNames = publishedSkills.keySet() as Set
- def missingSkillNames = expectedSkillNames - actualSkillNames
- def extraSkillNames = actualSkillNames - expectedSkillNames
- if (missingSkillNames || extraSkillNames) {
- throw new GradleException("SkillsJars must publish exactly ${expectedSkillNames}, but missing ${missingSkillNames} and found extra ${extraSkillNames}.")
- }
-
- publishedSkills.each { String skillName, String expectedSkillTarget ->
- def skillDir = skillsJarsDir.dir(skillName).asFile
- if (!skillDir.directory) {
- throw new GradleException("The SkillsJars skill ${skillName} must be a directory containing SKILL.md.")
- }
-
- def skillLink = skillsJarsDir.file("${skillName}/SKILL.md").asFile
- def skillPath = skillLink.toPath()
- def actualTarget = null
-
- if (java.nio.file.Files.isSymbolicLink(skillPath)) {
- actualTarget = java.nio.file.Files.readSymbolicLink(skillPath).toString().replace('\\', '/')
- }
- else if (skillLink.isFile()) {
- actualTarget = skillLink.getText('UTF-8').trim().replace('\\', '/')
- }
-
- if (actualTarget != expectedSkillTarget) {
- throw new GradleException("The SkillsJars skill ${skillName} marker must be a symlink to ${expectedSkillTarget}, but found ${actualTarget ?: 'no link target'}.")
- }
-
- def gitMode = providers.exec {
- commandLine 'git', 'ls-files', '-s', "skills/${skillName}/SKILL.md"
- }.standardOutput.asText.get().trim().split(' ')[0]
- if (gitMode != '120000') {
- throw new GradleException("The SkillsJars skill ${skillName} marker must be committed as a symlink with git mode 120000, but found mode ${gitMode ?: 'none'}.")
- }
-
- def sourceSkillFile = layout.projectDirectory.file(expectedSkillTarget.substring(6))
- if (!sourceSkillFile.asFile.file) {
- throw new GradleException("The SkillsJars skill ${skillName} target ${expectedSkillTarget} does not exist.")
- }
- }
- }
-}
-
apply {
from layout.projectDirectory.file('gradle/publish-root-config.gradle')
from layout.projectDirectory.file('gradle/rat-root-config.gradle')
diff --git a/gradle/publish-root-config.gradle b/gradle/publish-root-config.gradle
index 123bafc419..6959e78e58 100644
--- a/gradle/publish-root-config.gradle
+++ b/gradle/publish-root-config.gradle
@@ -140,6 +140,9 @@ def publishedProjects = [
'grails-data-graphql-core',
// wrapper
'grails-wrapper',
+ // agent skills
+ 'grails-skills-developer',
+ 'grails-skills-upgrade-guide-8',
// profiles
'grails-profiles-base',
'grails-profiles-plugin',
diff --git a/gradle/rat-root-config.gradle b/gradle/rat-root-config.gradle
index c160aac011..23d127f38b 100644
--- a/gradle/rat-root-config.gradle
+++ b/gradle/rat-root-config.gradle
@@ -26,7 +26,7 @@ tasks.named('rat') {
'CHECKSUMS', // build artifact for storing checksums for easy verification
'PUBLISHED_ARTIFACTS', // build artifact for storing published artifacts coordinates & paths
'licenses/**', // licenses directory excluded
- 'skills/**', // SkillsJars discovery symlinks; licensed skill sources live under .agents/skills
+ 'grails-skills/*/src/main/resources/SKILL.md', // published skill symlinks; licensed skill sources live under .agents/skills
'.claude/skills/**', // Claude skill aliases; licensed skill sources live under .agents/skills
'.git-blame-ignore-revs', // git configuration isn't code
'grails-web-common/src/main/groovy/org/grails/web/json/JSONObject.java',
diff --git a/grails-skills/developer/build.gradle b/grails-skills/developer/build.gradle
new file mode 100644
index 0000000000..1d2f8a2f6f
--- /dev/null
+++ b/grails-skills/developer/build.gradle
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * 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.
+ */
+plugins {
+ id 'java'
+ id 'org.apache.grails.buildsrc.publish'
+}
+
+group = 'org.apache.grails.skills'
+version = projectVersion
+
+ext {
+ pomArtifactId = 'grails-developer'
+ pomTitle = 'Apache Grails® Developer Agent Skill'
+ pomDescription = 'Agent skill for building Apache Grails® applications: controllers, services, GORM, views, plugins, and testing with Spock and Geb. The canonical SKILL.md source lives under .agents/skills/grails-developer.'
+}
diff --git a/grails-skills/developer/src/main/resources/SKILL.md b/grails-skills/developer/src/main/resources/SKILL.md
new file mode 120000
index 0000000000..fdc0c1877e
--- /dev/null
+++ b/grails-skills/developer/src/main/resources/SKILL.md
@@ -0,0 +1 @@
+../../../../../.agents/skills/grails-developer/SKILL.md
\ No newline at end of file
diff --git a/grails-skills/upgrade-guide-8/build.gradle b/grails-skills/upgrade-guide-8/build.gradle
new file mode 100644
index 0000000000..5a0b7d7e91
--- /dev/null
+++ b/grails-skills/upgrade-guide-8/build.gradle
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * 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.
+ */
+plugins {
+ id 'java'
+ id 'org.apache.grails.buildsrc.publish'
+}
+
+group = 'org.apache.grails.skills'
+version = projectVersion
+
+ext {
+ pomArtifactId = 'grails-8-upgrade'
+ pomTitle = 'Apache Grails® 8 Upgrade Agent Skill'
+ pomDescription = 'Agent skill for upgrading Apache Grails® applications from Grails 7.x to Grails 8. The canonical SKILL.md source lives under .agents/skills/grails-8-upgrade.'
+}
diff --git a/grails-skills/upgrade-guide-8/src/main/resources/SKILL.md b/grails-skills/upgrade-guide-8/src/main/resources/SKILL.md
new file mode 120000
index 0000000000..54634cb5ff
--- /dev/null
+++ b/grails-skills/upgrade-guide-8/src/main/resources/SKILL.md
@@ -0,0 +1 @@
+../../../../../.agents/skills/grails-8-upgrade/SKILL.md
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
index b0b8843788..1a1ffb303b 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -224,6 +224,10 @@ include(
// wrapper
'grails-wrapper',
+
+ // Agent Skills (published for consumption by AI coding agents)
+ 'grails-skills-developer',
+ 'grails-skills-upgrade-guide-8',
)
project(':grails-bom').projectDir = file('grails-bom/default')
@@ -249,6 +253,9 @@ project(':grails-async-rxjava2').projectDir = file('grails-async/rxjava2')
project(':grails-async-rxjava3').projectDir = file('grails-async/rxjava3')
project(':grails-async').projectDir = file('grails-async/plugin')
+project(':grails-skills-developer').projectDir = file('grails-skills/developer')
+project(':grails-skills-upgrade-guide-8').projectDir = file('grails-skills/upgrade-guide-8')
+
project(':grails-dependencies-starter-web').projectDir = file('grails-dependencies/starter-web')
project(':grails-dependencies-test').projectDir = file('grails-dependencies/test')
project(':grails-dependencies-assets').projectDir = file('grails-dependencies/assets')
diff --git a/skills/grails-8-upgrade/SKILL.md b/skills/grails-8-upgrade/SKILL.md
deleted file mode 120000
index d4c2988030..0000000000
--- a/skills/grails-8-upgrade/SKILL.md
+++ /dev/null
@@ -1 +0,0 @@
-../../.agents/skills/grails-8-upgrade/SKILL.md
\ No newline at end of file
diff --git a/skills/grails-developer/SKILL.md b/skills/grails-developer/SKILL.md
deleted file mode 120000
index 9128af3297..0000000000
--- a/skills/grails-developer/SKILL.md
+++ /dev/null
@@ -1 +0,0 @@
-../../.agents/skills/grails-developer/SKILL.md
\ No newline at end of file