jdaugherty opened a new pull request, #16428:
URL: https://github.com/apache/grails-core/pull/16428

   Fixes #15737
   
   ## Why this matters
   
   A Grails BOM that overrides a version Spring Boot also manages has to 
declare that override under **Spring Boot's property name**. If the names 
differ, setting a version property gives an **incorrect configuration without 
any error**: part of the dependency family moves and the rest does not.
   
   That was the case on `8.0.x`. Several overrides used Grails-specific names 
for modules Spring Boot controls under a different property:
   
   | Grails BOM property | Spring Boot's property for the same modules |
   |---|---|
   | `jackson2.version` | `jackson-2-bom.version` |
   | `jackson3.version` | `jackson-bom.version` |
   | `neo4j-driver.version` (`grails-neo4j-bom`) | `neo4j-java-driver.version` |
   
   For example, an application following the Spring Boot documentation sets 
`jackson-bom.version=3.1.7`. Spring Boot's imported `tools.jackson:jackson-bom` 
moves every Jackson 3 module to 3.1.7. But `grails-bom` pins `jackson-core` and 
`jackson-databind` directly under `jackson3.version`, and a BOM's own entries 
win over the ones it imports. So those two stay at 3.1.6, and the application 
runs on a mixed Jackson 3 classpath without any warning. Setting 
`jackson3.version` instead has the opposite problem: it moves only the two 
pinned modules. The same applies to the Neo4j driver. The Spring Dependency 
Management end-to-end build already had to copy the Jackson 3 version from one 
property to the other by hand to stay consistent.
   
   Nothing in the build caught this, because `validateDependencyVersions` only 
compares resolved versions and never looks at property names. The same gap let 
dead and redundant entries pile up:
   
   - `kotlin.version` and `mockito.version` were orphaned by a merge and never 
reached a published POM. Kotlin looked pinned below Spring Boot's version, but 
setting it did nothing.
   - `liquibase-hibernate5.version` and the Gradle BOM's `jquery.version` were 
used by no published entry.
   - The derived BOMs re-declare `groovy-bom`, `spock-bom` and 
`asset-pipeline-bom` as constraints so that `enforcedPlatform` consumers get 
them, but published them with **literal** versions. Overriding `groovy.version` 
did not move those constraints.
   
   ## What this adds
   
   A new build plugin, `org.apache.grails.buildsrc.bom-property-validator`, 
registers `validateBomProperties` on every BOM (`grails-base-bom`, 
`grails-bom`, `grails-hibernate5-bom`, `grails-hibernate7-bom`, 
`grails-neo4j-bom` and `grails-gradle-bom`). It reads the POM each BOM 
publishes, walks `spring-boot-dependencies` and the BOMs it imports using 
Maven's own model (`MavenXpp3Reader`), and fails when:
   
   1. **A version property the BOM owns is not used** by any entry it 
publishes. This catches dead keys, and keys whose dependency names don't reduce 
to them.
   2. **An override of a Spring Boot managed version uses a different property 
name** than Spring Boot's. For a module Spring Boot manages through an imported 
BOM, the right name is the property Spring Boot imports that BOM with, e.g. 
`jackson-bom.version` for all of `tools.jackson:jackson-bom`.
   3. **A pin repeats the version Spring Boot already manages**, which only 
makes the BOM larger.
   
   It is modelled on `validateDependencyVersions`:
   - It is not attached to `check` or `build`.
   - CI runs it by name in its own **Validate BOM Properties** job (root BOMs 
and `grails-gradle-bom`), and the release workflow runs it as a separate step.
   - It has its own opt-out, `-PskipBomPropertyValidation` / 
`ext.skipBomPropertyValidation = true`, which is separate from 
`skipDependencyValidation`.
   
   ## Fixes it drove
   
   - Renamed `jackson2.version` → `jackson-2-bom.version`, `jackson3.version` → 
`jackson-bom.version` and `neo4j-driver.version` → `neo4j-java-driver.version`, 
along with the dependency keys that must prefix them.
   - Removed `kotlin.version`, `mockito.version`, 
`liquibase-hibernate5.version` and the Gradle BOM's `jquery.version`.
   - `PropertyNameCalculator` now gives a platform BOM that a derived BOM 
re-declares as a constraint the same property as its import.
   - Documented deliberate exceptions in `dependencies.gradle`, each with its 
reason:
   
     | List | Entry | Reason |
     |---|---|---|
     | `bomPropertyNameExemptions` | `jackson2-annotations.version` | 
jackson-annotations has had no patch releases since 2.20, so it cannot share 
`jackson-2-bom.version` |
     | `bomPropertyNameExemptions` | `gradle-groovy.version` | the Gradle BOM 
tracks Gradle's embedded Groovy 4 line, which cannot share `groovy.version` 
with the application's Groovy 5 |
     | `bomRedundantVersionExemptions` | `graphql-java.version` | intentionally 
equal to Spring Boot's, as the #15674 tripwire for 
`graphql-java-extended-scalars` |
     | `bomUnusedVersionExemptions` | `maven-model.version` | read directly by 
build tooling and deliberately kept out of the published BOMs |
   
   - Fixed the shared skip-property check: Gradle passes a `-P` property given 
without a value as an empty string, which was parsed as `false`, so a bare 
`-PskipDependencyValidation` never actually skipped anything.
   
   ## User-facing changes
   
   The upgrade guide (section 14) and the Grails 8 upgrade skill now state that 
**Grails BOMs follow the upstream BOM property names**. They note that an 
override under a name no BOM declares is silently ignored, and list the renames:
   
   - Grails 7's `jackson.version` → `jackson-2-bom.version` (Jackson 3, the 
default, uses `jackson-bom.version`)
   - 8.0 milestone/RC names `jackson2.version`, `jackson3.version` and 
`neo4j-driver.version` → `jackson-2-bom.version`, `jackson-bom.version` and 
`neo4j-java-driver.version`
   
   The build guide's section on overriding managed versions also explains that 
one Spring Boot property now sets the version in both BOMs.
   
   ## Verification
   
   - New specs: `ValidateBomPropertiesTaskSpec` (TestKit, against a local fake 
parent BOM with an imported BOM and a parent POM), 
`ValidateDependencyVersionsTaskSpec`, `GrailsBomPropertyValidatorPluginSpec` 
and `PropertyNameCalculatorSpec`, plus new cases in `GradleUtilsSpec`. All 
build-logic tests pass.
   - For each rule, the parent-BOM walk and the skip property, I broke the 
behaviour on purpose and confirmed a test fails.
   - On this branch, `validateDependencyVersions` (245 tasks), 
`validateBomProperties` (all six BOMs, including `grails-gradle-bom`) and 
`validateActions` pass.
   - Not run locally: the full test suite, the docs guide build and the 
end-to-end suite.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to