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

   > **Status: DRAFT** - posted as a possible solution / discussion starter for 
the BOM-leak class of CI failures we keep absorbing into the Groovy 5 upgrade. 
Happy to redirect, narrow, or split further based on feedback before marking 
ready.
   
   ## Summary
   
   Stop leaking Grails-build-only artefacts (Gradle plugins, 
JavaParser-as-build-tool) into the **application** Grails BOMs. Those entries 
belong in `grails-gradle-bom` (which manages the Grails build classpath); 
republishing them in `grails-bom` / `grails-base-bom` / `grails-hibernate5-bom` 
/ `grails-micronaut-bom` adds no value to consumers and creates a class of CI 
failures that we keep paying off manually.
   
   ## The recurring problem
   
   The `Validate Dependency Versions` job has been catching transitive drift 
between Grails BOM constraints and the resolved runtime classpath whenever 
Apache Groovy bumps an internal dependency. The most recent instance, on PR 
#15557 against `8.0.x`:
   
   ```
   Dependency version validation failed for project 'grails-async-gpars'.
   The following dependencies resolved to versions different from the BOM 
(:grails-bom):
     com.github.javaparser:javaparser-core - resolved 3.28.1, expected 3.28.0
   A transitive dependency is upgrading these versions.
   ```
   
   The mechanism, every time:
   
   1. `dependencies.gradle` pins a Grails-build-only artefact (e.g. 
`javaparser-core: 3.28.0`) inside `gradleBomDependencyVersions` because the 
build itself uses it (here: `build-logic`'s `GroovydocEnhancerExtension`).
   2. `grails-bom/{base,default,hibernate5,micronaut}/build.gradle` iterates 
**all** of `gradleBomDependencies` into `constraints {}`, so the build-only pin 
gets republished as a managed-version constraint of the **application** BOM.
   3. Apache Groovy ships a patch that bumps the same artefact internally - 
e.g. [GROOVY-11989](https://issues.apache.org/jira/browse/GROOVY-11989) bumped 
`javaparser-core` 3.28.0 → 3.28.1 on `GROOVY_5_0_X` HEAD - and 
`groovy-groovysh` / `groovy-ginq` link against the new version.
   4. Any Grails subproject that resolves Groovy gets `javaparser-core` 3.28.1 
transitively, which now contradicts the 3.28.0 constraint the Grails BOM is 
republishing -> `:validateDependencyVersions` fails on every module.
   5. We bump the version in `dependencies.gradle` to match (PR #15557 did 
`813b131607`).
   6. Groovy ships another patch some weeks later. Goto 1.
   
   These artefacts are not used by user-facing Grails artefacts and have no 
business in the consumer BOM. Gradle plugins like `asciidoctor-gradle-jvm` or 
`asset-pipeline-gradle` are normally applied via the `plugins {}` block with a 
literal version, not via BOM resolution. `javaparser-core` reaches end users 
only as a transitive of `groovy-groovysh` (which is the version that should 
win, per Apache Groovy's release).
   
   ## Proposal
   
   Introduce a `gradleBomBuildOnlyDependencyKeys` set in `dependencies.gradle`:
   
   ```groovy
   gradleBomBuildOnlyDependencyKeys = [
       'asciidoctor-gradle-jvm',   // Gradle plugin
       'asset-pipeline-gradle',    // Gradle plugin
       'grails-publish-plugin',    // Gradle plugin
       'javaparser-core',          // build-time GroovydocEnhancer; transitive 
via groovy-groovysh for end users
       'spring-boot-gradle',       // Gradle plugin
   ] as Set
   ```
   
   The four application BOM projects 
(`grails-bom/{base,default,hibernate5,micronaut}/build.gradle`) skip these keys 
when iterating `gradleBomDependencies` into their `constraints {}`. 
`grails-gradle-bom` (in `grails-gradle/bom/`) is **unchanged** and continues to 
manage the build classpath, so Grails-plugin developers are unaffected.
   
   ## What stays in the application BOM
   
   I deliberately left these in `gradleBomDependencies` -> consumer BOM, 
because they are or may be used at app runtime / test runtime:
   
   | Entry | Why it stays |
   |---|---|
   | `ant`, `ant-junit` | Ant runtime artefacts (some Grails apps still use Ant 
tasks) |
   | `byte-buddy` | Spock 2 / Mockito need this at test runtime |
   | `commons-text` | Used by Grails CLI runtime + likely by app code |
   | `directory-watcher` | Grails dev-mode file watcher (runtime) |
   | `jansi`, `jline`, `jline2`, `jna` | Grails shell/console runtime |
   | `jquery` | Webjar (asset runtime) |
   | `objenesis` | Spock / Mockito test runtime |
   | `asciidoctorj` | Borderline - kept conservatively in case any user docs 
pipeline depends on it |
   | `spring-boot-cli`, `spring-boot-loader-tools` | Borderline - left in 
pending discussion |
   
   Three of those last items (`asciidoctorj`, `spring-boot-cli`, 
`spring-boot-loader-tools`) are arguably build-only too. I left them for a 
separate decision so the diff stays minimal and focused.
   
   ## Verification
   
   Verified locally on `origin/8.0.x` HEAD (`b47917c1fe`) **without** the 
manual `javaparser-core` 3.28.0 -> 3.28.1 bump that PR #15557 had to apply:
   
   ```
   ./gradlew validateDependencyVersions
   > 184 actionable tasks, BUILD SUCCESSFUL
   ```
   
   Without this fix, that exact command on the same source tree fails with the 
`javaparser-core - resolved 3.28.1, expected 3.28.0` error.
   
   Generated BOM POMs confirm the leak is gone:
   
   ```
   grails-base-bom / grails-bom / grails-hibernate5-bom / grails-micronaut-bom 
POMs:
     ABSENT  javaparser-core, asciidoctor-gradle-jvm, asset-pipeline-gradle,
             grails-publish, spring-boot-gradle-plugin   <-- the leak
     PRESENT jansi, jline, byte-buddy, commons-text,
             directory-watcher, ant-junit                <-- legitimate 
runtime/test deps
   
   grails-gradle-bom POM:
     PRESENT all of the above (this is the build-time BOM, unchanged)
   ```
   
   ## Why a draft
   
   A few open questions worth resolving before this lands:
   
   1. **Scope of "build-only"**: `asciidoctorj`, `spring-boot-cli`, 
`spring-boot-loader-tools` are arguably also build-only - I left them in the 
application BOM conservatively, but if anyone has clear opinions about whether 
end-user apps consume them via the BOM today I am happy to expand the set.
   
   2. **Backwards compatibility**: any Grails 7.x / 8.0.0-M1 app that was 
relying on `grails-bom` to manage `javaparser-core` etc. will need to either 
pin directly or import `grails-gradle-bom`. I surveyed grails-core and only 
`grails-data-graphql/plugin/build.gradle` line 38 directly references 
`javaparser-core` (and it pins 3.25.7 explicitly, ignoring the BOM-managed 
version anyway), so internally this is a no-op. External consumers may want a 
release note.
   
   3. **Whether to also publish `grails-gradle-bom` cross-referenced** from the 
application BOM POMs so any consumer that *does* want the Gradle classpath 
versions can opt in with one extra `platform(...)`.
   
   ## Context
   
   Surfaced while burning down workarounds on PR #15557 (Groovy 5 / Spring Boot 
4 upgrade). PR #15557 worked around the same javaparser leak by bumping 
`javaparser-core.version` to `3.28.1` (commit `813b131607`); this PR removes 
the underlying class of failure rather than playing whack-a-mole each time 
Groovy bumps an internal.
   
   


-- 
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