Generate configuration metadata from Groovy DSL sources - #16048
Generate configuration metadata from Groovy DSL sources#16048jamesfredley wants to merge 1 commit into
Conversation
Parse registered Groovy DSL sources without executing them and merge all remaining curated metadata through canonical generated resources. Assisted-by: opencode:gpt-5.6-sol codegraph
There was a problem hiding this comment.
Pull request overview
This pull request extends Grails’ configuration metadata generation to also support statically parsing explicitly-registered Groovy DSL sources (without executing them), then deterministically merging DSL-derived metadata, typed @ConfigurationProperties metadata, and curated overlays into a single canonical META-INF/spring-configuration-metadata.json published by each module.
Changes:
- Add a Groovy DSL metadata parser (via
SourceUnit) and integrate its output into the existing metadata generation/merge pipeline with defined precedence. - Route more in-repo modules (including Spring Security) through the canonical generated metadata JAR resource and add/refresh curated
additional-spring-configuration-metadata.jsonoverlays. - Add/adjust tests and docs to validate DSL parsing safety, precedence rules, and the Spring Security metadata contract.
Reviewed changes
Copilot reviewed 16 out of 24 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| grails-web-core/src/main/resources/META-INF/additional-spring-configuration-metadata.json | Adds curated overlay metadata for web-core properties to be merged into canonical generated metadata. |
| grails-web-core/build.gradle | Applies the configuration metadata build-logic plugin. |
| grails-spring-security/plugin/src/test/groovy/grails/plugin/springsecurity/ConfigurationMetadataSpec.groovy | Adds a contract test asserting Spring Security metadata is present, merged, and complete. |
| grails-spring-security/plugin/src/main/resources/META-INF/additional-spring-configuration-metadata.json | Adds curated Spring Security overlay metadata (groups/properties) used in the merge. |
| grails-spring-security/plugin/build.gradle | Applies the metadata plugin and registers DefaultSecurityConfig.groovy as a DSL source with root prefix mapping. |
| grails-gsp/plugin/src/main/resources/META-INF/additional-spring-configuration-metadata.json | Adds curated overlay metadata for GSP/plugin configuration properties. |
| grails-gsp/plugin/build.gradle | Applies the configuration metadata plugin. |
| grails-doc/src/en/guide/conf/config.adoc | Documents DSL-source parsing and the merged canonical metadata resource behavior. |
| grails-data-mongodb/grails-plugin/src/main/resources/META-INF/additional-spring-configuration-metadata.json | Adds curated overlay metadata for MongoDB plugin settings. |
| grails-data-mongodb/grails-plugin/build.gradle | Applies the configuration metadata plugin. |
| grails-data-hibernate7/dbmigration/src/main/resources/META-INF/additional-spring-configuration-metadata.json | Adds curated overlay metadata for Hibernate7 dbmigration plugin settings. |
| grails-data-hibernate7/dbmigration/build.gradle | Applies the configuration metadata plugin. |
| grails-data-hibernate7/core/src/main/resources/META-INF/additional-spring-configuration-metadata.json | Adds curated overlay metadata for Hibernate7 core settings. |
| grails-data-hibernate7/core/build.gradle | Applies the configuration metadata plugin. |
| grails-data-hibernate5/dbmigration/src/main/resources/META-INF/additional-spring-configuration-metadata.json | Adds curated overlay metadata for Hibernate5 dbmigration plugin settings. |
| grails-data-hibernate5/dbmigration/build.gradle | Applies the configuration metadata plugin. |
| grails-data-hibernate5/core/src/main/resources/META-INF/additional-spring-configuration-metadata.json | Adds curated overlay metadata for Hibernate5 core settings. |
| grails-data-hibernate5/core/build.gradle | Applies the configuration metadata plugin. |
| grails-core/src/main/resources/META-INF/spring-configuration-metadata.json | Removes the hand-maintained standard metadata file in favor of canonical generation. |
| grails-core/src/main/resources/META-INF/additional-spring-configuration-metadata.json | Expands curated overlays (groups/properties/hints) to be merged into canonical generation. |
| grails-core/build.gradle | Applies the configuration metadata plugin. |
| build-logic/plugins/src/test/groovy/org/apache/grails/buildsrc/ConfigurationMetadataPluginSpec.groovy | Adds/extends TestKit coverage for DSL parsing, precedence merging, and incremental behavior. |
| build-logic/plugins/src/main/groovy/org/apache/grails/buildsrc/GroovyDslConfigurationMetadataParser.groovy | Introduces the static Groovy DSL parser that extracts property paths/types/defaults without executing scripts/transforms. |
| build-logic/plugins/src/main/groovy/org/apache/grails/buildsrc/ConfigurationMetadataPlugin.groovy | Wires DSL inputs into the task and updates merge logic for DSL vs typed vs overlay precedence. |
💡 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 @@
## feature/automated-configuration-metadata #16048 +/- ##
==================================================================================
+ Coverage 51.4794% 51.4815% +0.0021%
Complexity 17793 17793
==================================================================================
Files 2040 2040
Lines 95681 95681
Branches 16610 16610
==================================================================================
+ Hits 49256 49258 +2
+ Misses 39084 39082 -2
Partials 7341 7341 🚀 New features to boost your workflow:
|
✅ All tests passed ✅🏷️ Commit: d12545a Learn more about TestLens at testlens.app. |
jdaugherty
left a comment
There was a problem hiding this comment.
I realized this review never got submitted, apologies:
Reviewed the DSL parser, the merge precedence changes, the module wiring, and the metadata moves. Verified the grails-core metadata consolidation is lossless (all groups, properties, and hints from both prior files are present in the merged additional file). A few comments below, mostly about robustness of the parser against ConfigSlurper semantics for future DSL registrations.
| if (!conditional && inference.literal) { | ||
| property.defaultValue = inference.value | ||
| } | ||
| properties << property |
There was a problem hiding this comment.
Duplicate assignments to the same property produce distinct entries here, and the DSL index in mergeNamed (indexByName) fails the task when two entries for one name aren't map-equal. Two patterns that are valid at runtime under ConfigSlurper's last-write-wins semantics trip this:
foo = 'default'
if (Environment.current == Environment.TEST) {
foo = 'test' // conditional entry has no defaultValue -> conflicts with the unconditional entry
}and if/else branches whose literals infer different types (e.g. = null in one branch, = true in the other). DefaultSecurityConfig.groovy avoids both today — the password branches assign the same types on both sides — but the next DSL source registered could fail the build on a perfectly legitimate config. Consider collapsing same-name entries before returning (union the type the way sharedType does, keep the unconditional defaultValue), or at least add a spec pinning the conflict failure so it's an explicit contract.
| ClosureExpression closure = closureArgument(expression) | ||
| String nestedName = expression.methodAsString | ||
| if (closure != null && nestedName != null) { | ||
| parseStatements(closure.code, "${prefix}.${nestedName}", conditional, properties) |
There was a problem hiding this comment.
These sources are ConfigSlurper scripts at runtime (SpringSecurityUtils loads DefaultSecurityConfig with new ConfigSlurper(Environment.current.name).parse(...)), so an environments block is valid in them. This branch would treat it as an ordinary nested section and silently emit names like <prefix>.environments.production.foo instead of <prefix>.foo. Nothing hits this today since DefaultSecurityConfig.groovy uses Environment.current if-checks instead, but it may be worth handling an environments call like IfStatement (recurse into each environment closure with the unchanged prefix and conditional = true) — or failing loudly when one is encountered.
|
|
||
| expect: 'the complete curated metadata contract is retained' | ||
| metadata.get('groups').size() == 14 | ||
| properties.size() == 145 |
There was a problem hiding this comment.
These exact counts mean every future addition to DefaultSecurityConfig.groovy or the curated overlay fails this spec with only a number mismatch. If a deliberate tripwire is the intent that's fine, but consider deriving the expectation from the packaged META-INF/additional-spring-configuration-metadata.json in the same JAR (assert every curated group/property name appears in the canonical file) — the spec then stays self-maintaining and a failure names the missing entry instead of reporting 146 != 145.
Summary
SourceUnitwithout executing scripts, resolving dependencies, or running AST transformsDefaultSecurityConfigand route every remaining in-repo metadata module through one canonical generated JAR resourceadditional-spring-configuration-metadata.jsonRelationship
This is a stacked follow-up to #16047 and should be reviewed and merged after it.
It completes the remaining in-repo work for #15469. Asset Pipeline metadata remains in its external repository and existing upstream pull request.
Verification
build-logic:checkgrails-core,grails-web-core,grails-gsp,grails-data-mongodb, Hibernate 5/7 core and dbmigration, andgrails-spring-security- 2,990 tests passed:grails-test-examples-config-report:integrationTest:grails-doc:publishGuide -x aggregateGroovydoc:grails-spring-security:codeStyle