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]