feat: Replace Spring Dependency Management plugin with Gradle platform + lightweight BOM property overrides#15467
Open
jamesfredley wants to merge 12 commits into8.0.xfrom
Open
feat: Replace Spring Dependency Management plugin with Gradle platform + lightweight BOM property overrides#15467jamesfredley wants to merge 12 commits into8.0.xfrom
jamesfredley wants to merge 12 commits into8.0.xfrom
Conversation
…m and lightweight BOM property overrides Replace the Spring Dependency Management Gradle plugin with Gradle's native platform() support plus a lightweight BomManagedVersions utility that preserves the ability to override BOM-managed dependency versions via project properties (ext[] or gradle.properties). This allows Grails to standardize on Gradle platforms - the modern dependency management solution - while retaining the one feature Gradle platforms lack: property-based version overrides from BOMs. Changes: - Add BomManagedVersions: parses BOM POM XML to extract property-to- artifact mappings, applies version overrides via eachDependency() - Update GrailsGradlePlugin to use platform() + BomManagedVersions instead of Spring DM plugin - Deprecate GrailsExtension.springDependencyManagement flag - Remove Spring DM plugin from plugins/build.gradle dependency - Remove Spring DM plugin from example projects - Update documentation to reflect Gradle platform approach - Add unit tests (BomManagedVersionsSpec) and functional test (BomPlatformFunctionalSpec) Note: build-logic/docs-core/ExtractDependenciesTask still uses Spring DM's shaded Maven model classes and should be addressed in a follow-up. Assisted-by: Claude Code <Claude@Claude.ai>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR replaces the Spring Dependency Management plugin with Gradle's native platform() mechanism and introduces a lightweight utility (BomManagedVersions) to enable property-based version overrides from BOMs—the one feature Gradle platforms don't natively support.
Changes:
- Removed Spring Dependency Management plugin dependency and usage across the codebase
- Added
BomManagedVersionsutility (~350 lines) to parse BOM POMs and apply property-based version overrides - Updated plugin to use
platform()for BOM import instead of Spring DM'smavenBom()
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
grails-gradle/plugins/src/main/groovy/org/grails/gradle/plugin/core/BomManagedVersions.groovy |
New utility class that parses BOM POMs and enables property-based version overrides via Gradle's resolution strategy |
grails-gradle/plugins/src/main/groovy/org/grails/gradle/plugin/core/GrailsGradlePlugin.groovy |
Replaced Spring DM plugin application with native Gradle platform support plus BomManagedVersions |
grails-gradle/plugins/src/main/groovy/org/grails/gradle/plugin/core/GrailsExtension.groovy |
Deprecated springDependencyManagement flag with guidance on new approach |
grails-gradle/plugins/build.gradle |
Removed Spring DM plugin dependency |
grails-test-examples/gsp-spring-boot/app/build.gradle |
Removed Spring DM plugin from example project |
grails-data-graphql/examples/spring-boot-app/build.gradle |
Removed Spring DM plugin from example project |
grails-doc/src/en/guide/commandLine/gradleBuild/gradleDependencies.adoc |
Updated documentation to reflect platform-based approach and property override mechanism |
grails-profiles/plugin/templates/grailsCentralPublishing.gradle |
Updated comment to reflect new version management approach |
grails-bom/build.gradle |
Updated comment about version property references |
grails-gradle/plugins/src/test/groovy/org/grails/gradle/plugin/core/BomManagedVersionsSpec.groovy |
Unit tests for BOM parsing and property extraction |
grails-gradle/plugins/src/test/groovy/org/grails/gradle/plugin/core/BomPlatformFunctionalSpec.groovy |
Functional test verifying platform integration |
grails-gradle/plugins/src/test/resources/test-projects/bom-platform-basic/* |
Test project files for functional testing |
grails-gradle/plugins/src/test/resources/test-poms/test-bom.pom |
Test BOM POM for unit tests |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…h constant Add factory.setXIncludeAware(false) for explicit XML security hardening and extract magic number 10 to MAX_PROPERTY_INTERPOLATION_DEPTH constant. Assisted-by: Claude Code <Claude@Claude.ai>
eb85805 to
5643538
Compare
The Spring Dependency Management plugin applied version constraints globally to every configuration via configurations.all() and resolutionStrategy.eachDependency(). With the switch to Gradle's native platform(), version constraints must be added explicitly. Apply the grails-bom platform to all declarable configurations using configureEach, matching the previous global behavior. Non-declarable configurations (apiElements, runtimeElements, etc.) inherit constraints through their parent configurations. Code quality tool configurations (checkstyle, codenarc, etc.) are excluded because platform() constraints participate in version conflict resolution and can upgrade transitive dependencies, breaking the tools. Also ensure the developmentOnly configuration always exists via maybeCreate. Assisted-by: Claude Code <Claude@Claude.ai>
5643538 to
5e89656
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
# Conflicts: # grails-data-graphql/examples/spring-boot-app/build.gradle
…atform applyGrailsBom() adds a regular platform(grails-bom) to every declarable configuration, including 'implementation'. Micronaut projects still need to declare an enforcedPlatform(grails-bom) explicitly. With both present on the same configuration, validateEnforcedBom() was failing on the first grails-bom it encountered (the plugin-injected regular platform) before it could check the user's enforcedPlatform declaration. Scan all grails-bom declarations on 'implementation' and accept the project as correctly configured if any one of them is an enforcedPlatform. Only error when grails-bom is present but no enforcedPlatform exists. Assisted-by: claude-code:claude-opus-4
…form applyGrailsBom() adds a regular platform(grails-bom) to every declarable configuration. For annotation-processor classpaths this conflicts with the platform the user imports themselves (typically io.micronaut.platform:micronaut-platform for Micronaut projects). Since platform() constraints participate in Gradle version conflict resolution, grails-bom's higher javaparser-core version wins over what Micronaut's inject-java processor was compiled against, producing NoSuchMethodError on StaticJavaParser.parseJavadoc(String) during compileJava. Exclude annotationProcessor and *AnnotationProcessor configurations using the same mechanism that already excludes code-quality tool classpaths (checkstyle, codenarc, pmd, spotbugs). Rename the helper to reflect the broadened scope. Assisted-by: claude-code:claude-opus-4
✅ All tests passed ✅🏷️ Commit: 54fbe93 Learn more about TestLens at testlens.app. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR standardizes Grails on Gradle's native
platform()dependency management - the modern, recommended approach - while preserving the one feature that Gradle platforms currently lack: property-based version overrides from BOMs (e.g.ext['slf4j.version'] = '2.0.9'orslf4j.version=2.0.9ingradle.properties).The Spring Dependency Management plugin served Grails well, but it duplicates significant Gradle functionality (dependency resolution, exclusions, BOM imports) and introduces behavioral differences from standard Gradle resolution. Gradle platforms now handle the vast majority of what the Spring DM plugin provided. The one gap is reading
<properties>from BOM POMs and allowing project-level overrides (Gradle issue #9160 - confirmed "very unlikely" to be implemented).This PR bridges that gap with a lightweight ~350-line utility (
BomManagedVersions) so Grails can remove the Spring DM plugin entirely.What This Changes
Core:
BomManagedVersions.groovy(new)A lightweight utility that:
@pomartifact notation)DocumentBuilderFactory- no external dependencies. XML parsing is hardened (disallow-doctype-decl, external entity/DTD access disabled,setXIncludeAware(false))<properties>block and<dependencyManagement>entries, building a mapping of property names to the artifacts they control<scope>import</scope>BOM imports (e.g., grails-bom imports spring-boot-dependencies). Interpolation is capped atMAX_PROPERTY_INTERPOLATION_DEPTH = 10to prevent runaway recursionConfiguration.resolutionStrategy.eachDependency()- only for properties whereproject.hasProperty(name)is true (covering bothext['...']andgradle.properties)Plugin:
GrailsGradlePlugin.groovy(modified)applySpringDependencyManagementPlugin()withapplyGrailsBom()applyGrailsBom()addsproject.dependencies.platform(bomCoordinates)to every declarable project configuration (mirroring the global behaviour that Spring DM provided viaconfigurations.all() + resolutionStrategy.eachDependency()). Non-declarable configurations (apiElements,runtimeElements, etc.) inherit the constraints through their parent configurations.platform()constraints participate in version conflict resolution and would otherwise upgrade transitive dependencies and break the tool:checkstyle,codenarc,pmd,spotbugs,spotbugsPluginsannotationProcessorand any*AnnotationProcessor(e.g.testAnnotationProcessor,integrationTestAnnotationProcessor). Micronaut projects importio.micronaut.platform:micronaut-platformon these configurations themselves; a second non-enforced grails-bom platform would let Gradle's highest-version conflict resolution upgradejavaparser-core,micronaut-inject-java, etc. beyond what the processor was compiled against.developmentOnlyconfiguration is always available viaconfigurations.maybeCreate('developmentOnly'), even when Spring Boot isn't applied or plugin ordering changes.BomManagedVersions.resolve()in anafterEvaluateblock to enable property-based overrides.validateEnforcedBom()(used byconfigureMicronaut()) now scans allgrails-bomdeclarations on theimplementationconfiguration and accepts the project as correctly configured if any one of them is anenforcedPlatform. This is required becauseapplyGrailsBom()injects a regularplatform(grails-bom)alongside the user's explicitenforcedPlatform(grails-bom); the previous first-match logic tripped on the plugin-injected one and rejected valid Micronaut projects.Extension:
GrailsExtension.groovy(modified)springDependencyManagementflag is deprecated with a message directing users to Gradle's native platform supportBuild:
plugins/build.gradle(modified)implementation 'io.spring.gradle:dependency-management-plugin'dependencyExamples & Docs
io.spring.dependency-managementplugin from example projects (gsp-spring-boot,graphql spring-boot-app)gradleDependencies.adocto document the Gradle platform approach and property override mechanismgrailsCentralPublishing.gradletemplate andgrails-bom/build.gradleHow Version Overrides Work
The mechanism is intentionally simple and mirrors exactly what the Spring DM plugin provided:
BomManagedVersionsreads the BOM's<properties>to discover thatslf4j.versioncontrolsorg.slf4j:slf4j-api(and other slf4j artifacts), then useseachDependencyto force the override at resolution time.Why Not Just Use Gradle Platforms?
Gradle platforms (
platform()) handle:Gradle platforms do not handle reading
<properties>from BOM POMs and allowing project-level overrides (Gradle issue #9160 - confirmed "very unlikely" to be implemented).This is the specific gap
BomManagedVersionsfills.Testing
BomManagedVersionsSpec): 4 tests covering POM parsing, property extraction, nested BOM imports, and property-to-artifact mappingBomPlatformFunctionalSpec): End-to-end Gradle TestKit test verifying thatgradle.propertiesoverrides are applied during dependency resolution8.0.x: Core/Forge/Gradle Plugin builds, all Functional / Hibernate5 / Mongodb functional test matrices (Java 21 & 25, indy=false & indy=true), Analyze (java), Validate Dependency Versions, rat-audit, Code Style, CodeQLCommits
ca495abfeat: replace Spring Dependency Management plugin with Gradle platform + lightweight BOM property overrides- initial implementation ofBomManagedVersions, switch from Spring DM toplatform(), deprecateGrailsExtension.springDependencyManagement, update docs and example projects531041baAddress review: add XInclude hardening and extract interpolation depth constant-setXIncludeAware(false)and extractMAX_PROPERTY_INTERPOLATION_DEPTH = 105e89656efix: apply grails-bom platform to all declarable configurations- restore Spring DM's global behaviour by addingplatform(grails-bom)to every declarable configuration; exclude code-quality tool classpaths1a2cea34→28a68177Merge branch '8.0.x' into feat/gradle-managed-version-overridescommits keeping the branch in sync with8.0.x(Spring Boot 4, Jackson 3, Java 25 Micronaut bumps, dependency-validator plugin, etc.). The final merge resolved one textual conflict ingrails-data-graphql/examples/spring-boot-app/build.gradle(drop the Spring DM plugin while keepingorg.apache.grails.buildsrc.dependency-validator, matching how other examplebuild.gradles on8.0.xlook).77932f2efix: allow Micronaut BOM validator to coexist with plugin-injected platform-validateEnforcedBom()now scans allgrails-bomdeclarations and passes if any one isenforcedPlatform, rather than failing on the first regularplatform(grails-bom)the plugin injected54fbe932fix: exclude annotation-processor configurations from grails-bom platform- do not addplatform(grails-bom)toannotationProcessor/*AnnotationProcessorconfigurations; renameisCodeQualityConfigurationtoisExcludedFromBomPlatform. FixesNoSuchMethodErroronStaticJavaParser.parseJavadoc(String)in Micronaut test-example projects after the8.0.xbump to javaparser-core 3.28.0Known Follow-up Items
build-logic/docs-core/ExtractDependenciesTask.groovystill uses Spring DM's shaded Maven model classes (io.spring.gradle.dependencymanagement.org.apache.maven.model.Model) for POM reading at build time. This should be migrated to JDK XML parsing in a separate follow-up.Motivation
Standardizing on Gradle platforms is the direction the Gradle ecosystem is heading. The Spring DM plugin, while feature-rich, introduces a parallel dependency resolution system that can conflict with Gradle's native behavior. By removing it and adding only the minimal property-override bridge, Grails 8 gets:
ext['version.property']andgradle.propertiesoverrides continue to work exactly as beforeRelated