You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@grails.apache.org by ja...@apache.org on 2026/07/20 18:24:13 UTC
(grails-core) branch chore/automate-project-conventions updated: Address repository convention review feedback
This is an automated email from the ASF dual-hosted git repository.
jamesfredley pushed a commit to branch chore/automate-project-conventions
in repository https://gitbox.apache.org/repos/asf/grails-core.git
The following commit(s) were added to refs/heads/chore/automate-project-conventions by this push:
new 8bc7b8fdc1 Address repository convention review feedback
8bc7b8fdc1 is described below
commit 8bc7b8fdc108416b8a1797484e2dd6679b106789
Author: James Fredley <ja...@users.noreply.github.com>
AuthorDate: Mon Jul 20 14:19:46 2026 -0400
Address repository convention review feedback
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
---
.../buildsrc/RepositoryConventionsTask.groovy | 9 ++--
.../buildsrc/RepositoryConventionsTaskSpec.groovy | 59 +++++++++++++++++++++-
2 files changed, 62 insertions(+), 6 deletions(-)
diff --git a/build-logic/plugins/src/main/groovy/org/apache/grails/buildsrc/RepositoryConventionsTask.groovy b/build-logic/plugins/src/main/groovy/org/apache/grails/buildsrc/RepositoryConventionsTask.groovy
index fd6da84452..009d4a3fc9 100644
--- a/build-logic/plugins/src/main/groovy/org/apache/grails/buildsrc/RepositoryConventionsTask.groovy
+++ b/build-logic/plugins/src/main/groovy/org/apache/grails/buildsrc/RepositoryConventionsTask.groovy
@@ -36,6 +36,7 @@ import org.yaml.snakeyaml.Yaml
import org.yaml.snakeyaml.constructor.SafeConstructor
import org.yaml.snakeyaml.error.YAMLException
+import java.nio.charset.StandardCharsets
import java.util.Set
import java.util.regex.Matcher
import java.util.regex.Pattern
@@ -141,11 +142,11 @@ abstract class RepositoryConventionsTask extends DefaultTask {
options.setAllowDuplicateKeys(false)
document = new Yaml(new SafeConstructor(options)).load(lines.subList(1, end).join('\n'))
} catch (YAMLException exception) {
- violations.add(sanitizeViolation("${path}: malformed skill front matter: ${exception.message}".toString()))
+ violations.add("${path}: malformed skill front matter: ${exception.message}".toString())
return [:]
}
if (!(document instanceof Map)) {
- violations.add(sanitizeViolation("${path}: skill front matter must be a YAML mapping".toString()))
+ violations.add("${path}: skill front matter must be a YAML mapping".toString())
return [:]
}
Map<String, String> values = [:]
@@ -154,7 +155,7 @@ abstract class RepositoryConventionsTask extends DefaultTask {
if (value instanceof String) {
values[key] = (String) value
} else if (value != null) {
- violations.add(sanitizeViolation("${path}: skill front matter field '${key}' must be a string".toString()))
+ violations.add("${path}: skill front matter field '${key}' must be a string".toString())
}
}
values
@@ -398,7 +399,7 @@ abstract class RepositoryConventionsTask extends DefaultTask {
List<PropertiesLine> result = []
String content = null
int start = 0
- file.readLines().eachWithIndex { String line, int index ->
+ file.readLines(StandardCharsets.UTF_8.name()).eachWithIndex { String line, int index ->
if (content == null) {
String trimmed = line.trim()
if (!trimmed || trimmed.startsWith('#') || trimmed.startsWith('!')) {
diff --git a/build-logic/plugins/src/test/groovy/org/apache/grails/buildsrc/RepositoryConventionsTaskSpec.groovy b/build-logic/plugins/src/test/groovy/org/apache/grails/buildsrc/RepositoryConventionsTaskSpec.groovy
index 24e078876c..2357c8709a 100644
--- a/build-logic/plugins/src/test/groovy/org/apache/grails/buildsrc/RepositoryConventionsTaskSpec.groovy
+++ b/build-logic/plugins/src/test/groovy/org/apache/grails/buildsrc/RepositoryConventionsTaskSpec.groovy
@@ -23,6 +23,7 @@ import org.gradle.testkit.runner.TaskOutcome
import spock.lang.Specification
import spock.lang.TempDir
+import java.nio.charset.StandardCharsets
import java.nio.file.Path
class RepositoryConventionsTaskSpec extends Specification {
@@ -362,6 +363,58 @@ injected\\r\\n|forged=two
!report.contains('\r')
}
+ def "validateRepositoryConventions sanitizes front matter violations once in reports and exceptions"() {
+ given:
+ writeBuild()
+ writeSkillContent('sample', '''---
+name: sample
+description: Test skill
+license: Apache-2.0
+"duplicate|key": one
+"duplicate|key": two
+---
+''')
+ writeAgents('.agents/skills/sample/SKILL.md')
+ writeWorkflow('valid.yml', "uses: actions/checkout@${SHA}")
+
+ when:
+ def result = runAndFail('validateRepositoryConventions')
+ def report = testProjectDir.resolve('build/reports/violations/REPOSITORY_CONVENTIONS.md').toFile().text
+
+ then:
+ result.output.contains('duplicate key duplicate\\|key')
+ !result.output.contains('duplicate key duplicate\\\\|key')
+ report.contains('duplicate key duplicate\\|key')
+ !report.contains('duplicate key duplicate\\\\|key')
+ }
+
+ def "validateRepositoryConventions reads duplicate UTF-8 message keys independently of the Gradle default charset"() {
+ given:
+ writeBuild()
+ writeSkill('sample', 'sample')
+ writeAgents('.agents/skills/sample/SKILL.md')
+ writeWorkflow('valid.yml', "uses: actions/checkout@${SHA}")
+ def build = testProjectDir.resolve('build.gradle').toFile()
+ build << '''
+tasks.register('verifyDefaultEncoding') {
+ doLast {
+ assert java.nio.charset.Charset.defaultCharset() == java.nio.charset.StandardCharsets.ISO_8859_1
+ }
+}
+'''
+ testProjectDir.resolve('gradle.properties').toFile().text = 'org.gradle.jvmargs=-Dfile.encoding=ISO-8859-1\n'
+ def file = testProjectDir.resolve('src/main/resources/messages.properties').toFile()
+ file.parentFile.mkdirs()
+ file.setText('caf\u00e9=one\ncaf\u00e9=two\n', StandardCharsets.UTF_8.name())
+
+ when:
+ def result = runAndFail('verifyDefaultEncoding', 'validateRepositoryConventions')
+
+ then:
+ result.task(':verifyDefaultEncoding').outcome == TaskOutcome.SUCCESS
+ result.output.contains("duplicate message key 'caf\u00e9'")
+ }
+
def "validateRepositoryConventions requires skill front matter to begin on line one"() {
given:
writeBuild()
@@ -570,10 +623,12 @@ ${withLicense ? 'license: Apache-2.0' : ''}
.build()
}
- private def runAndFail(String task) {
+ private def runAndFail(String task, String... additionalArguments) {
+ List<String> arguments = [task, '--stacktrace']
+ arguments.addAll(additionalArguments)
GradleRunner.create()
.withProjectDir(testProjectDir.toFile())
- .withArguments(task, '--stacktrace')
+ .withArguments(arguments)
.withPluginClasspath()
.buildAndFail()
}