Skip to content

Fix 8.0.x build break introduced by 7.2.x merge (6ebc60c6d0)#15673

Open
jamesfredley wants to merge 7 commits into
8.0.xfrom
fix/8.0.x-merge-sb4-fallout
Open

Fix 8.0.x build break introduced by 7.2.x merge (6ebc60c6d0)#15673
jamesfredley wants to merge 7 commits into
8.0.xfrom
fix/8.0.x-merge-sb4-fallout

Conversation

@jamesfredley
Copy link
Copy Markdown
Contributor

@jamesfredley jamesfredley commented May 21, 2026

What this PR fixes

Merge commit 6ebc60c6d0 ("Merge branch '7.2.x' into 8.0.x") plus pre-existing version drift turned every CI job on 8.0.x red. Reproducing the failures locally surfaced six distinct problems that need to be addressed for 8.0.x to build again on the Grails 8 / Spring Boot 4 / Spring 7 / Hibernate Jakarta stack. A seventh commit aligns the new file with house Gradle conventions discovered during a post-fix audit.

CI failures and their fixes

CI job (representative run id) Root cause Fixed by
Validate Dependency Versions - root project graphql-java pinned to 24.3 in dependencies.gradle while Spring Boot 4.0.5's BOM ships 25.0; SB constraint wins and the validator flags the drift Commit 2
Validate Dependency Versions - cd grails-gradle && validateDependencyVersions gradle-groovy.version=4.0.31 drifted from main groovy.version=4.0.32, transitive resolution upgraded all Groovy artifacts to 4.0.32 while grails-gradle BOM still expected 4.0.31 Commit 6
Build Grails-Core (ubuntu/macos/windows, JDK 21+25) :grails-test-examples-graphql-spring-boot-app:compileGroovy couldn't resolve org.springframework.boot.hibernate.autoconfigure.HibernateJpaAutoConfiguration because SB4 split it into a separate module that the new example's build.gradle didn't depend on Commit 3
Functional Tests, Hibernate5 Functional Tests, Mongodb Functional Tests All cascade from the same :grails-test-examples-graphql-spring-boot-app:compileGroovy failure Commit 3
Hibernate5 Functional Tests (after compile fix) :grails-data-hibernate5-dbmigration:integrationTest was failing with LoggerFactory is not a Logback LoggerContext but Logback is on the classpath because the merge brought back a stray testRuntimeOnly 'org.slf4j:slf4j-simple' that competes with SB4's transitive Logback binding Commit 4
CodeQL (./gradlew testClasses) Same compile failure as the main build Commit 3
CI - Groovy Joint Validation Build Two failures: graphql spring-boot-app compile (already fixed by Commit 3) and dbmigration slf4j conflict (already fixed by Commit 4) Commits 3 + 4

Latent bugs caught while reproducing locally

These are not in any failed CI log yet because earlier failures stopped the build before reaching them, but they would surface immediately once the compile-blocking failures were fixed.

Bug Root cause Fixed by
Three string-literal @EnableAutoConfiguration exclusion paths still pointed at Spring Boot 3 packages, silently failing to exclude anything in user apps ApplicationClassInjector (Liquibase), ApplicationClassInjectorSpec (matching test), GrailsApplicationCompilerAutoConfiguration (DataSource) Commit 1
The forward-ported grails-test-examples-graphql-grails-multi-datastore-app didn't compile/run cleanly on the 8.0.x stack integration specs missing extends Specification, deprecated Logback Groovy DSL config, no MongoDB testcontainer, deprecated session.datastore GORM API, graphql-java 25's stricter scalar Coercing signature Commit 5

Commits

  1. Fix Spring Boot 4 autoconfigure package paths in exclude strings - updates three stale Spring Boot 3 package names in @EnableAutoConfiguration(excludeName=...) style string literals and the matching unit test assertion.
  2. Stop pinning graphql-java; let spring-boot-dependencies own its version - drops the redundant 24.3 pin (and matching bomDependencies alias) so Spring Boot's 25.0 constraint is the single source of truth.
  3. Add spring-boot-hibernate dep to graphql spring-boot-app for SB4 module split - adds the runtime dep that SB4's split makes mandatory; uses implementation not compileOnly because GORM's @AutoConfigureBefore([HibernateJpaAutoConfiguration]) needs the class loaded.
  4. Remove slf4j-simple from dbmigration; Logback is now the only binding - removes the duplicate SLF4J binding that aborted Logback initialisation under SB4.
  5. Repair graphql multi-datastore-app integration tests on Grails 8 stack - five interrelated fixes to make this newly-included module's integration tests pass: Spock extends Specification, logback.xml (Groovy DSL gone in Logback 1.5), MongoDB testcontainer via grails-testing-support-mongodb, modern GormEnhancer.findStaticApi(...) access, and Coercing<ObjectId, String> for graphql-java 25.
  6. Bump gradle-groovy version to 4.0.32 to match groovy.version - aligns the grails-gradle subproject's BOM with the main BOM so its validateDependencyVersions task stops failing on the Groovy artifact drift.
  7. Use testImplementation for grails-testing-support-mongodb to match house style - small follow-up to commit 5: every other usage of grails-testing-support-mongodb in this repo declares it on testImplementation, so this module follows suit (the integrationTest source set extends test via the grails-extension-gradle-config so the testcontainer support still reaches integration tests).

Local verification

All testing on the merge commit with these patches applied. JDK 21 (Corretto), Gradle 9.4.1, GRADLE_OPTS=-Xms2G -Xmx5G.

Task (forced --rerun-tasks) Result
./gradlew testClasses (CodeQL target) exit 0, 693 tasks
./gradlew validateDependencyVersions --continue (root) exit 0, 186 tasks
cd grails-gradle && ./gradlew validateDependencyVersions --continue exit 0, 10 tasks
./gradlew :grails-data-graphql-core:test 254/254 PASSED on graphql-java 25.0
./gradlew :grails-test-examples-graphql-spring-boot-app:check 2/2 PASSED
./gradlew :grails-data-hibernate5-dbmigration:integrationTest 3/3 PASSED
./gradlew :grails-core:test --tests ...ApplicationClassInjectorSpec 8/8 PASSED
./gradlew :grails-shell-cli:test 105/105 PASSED
./gradlew :grails-test-examples-graphql-{grails-test,grails-tenant,grails-docs,grails-multi-datastore,spring-boot}-app integration tests All green (multi-datastore-app re-verified after commit 7)
./gradlew build :grails-shell-cli:installDist groovydoc --continue (matches CI Build Grails-Core command) 5004 tasks executed, the lone failure was a flaky HttpClientSupportSpec.httpPatchXml (server.verify() returned false) that passes consistently in isolation; not introduced by this PR

Convention audit

After the initial six commits, I audited every change in this PR against existing patterns in the repo:

  • Gradle deps: bare GAV strings are universal in build.gradle files (the bomDependencies['key'] shortcut in dependencies.gradle is for BOM generation, not for build.gradle consumption). All my additions use bare GAVs. Verdict: consistent.
  • Comment style: //-prefixed multi-line for inline explanations in dependencies.gradle and build.gradle. Verdict: consistent.
  • logback.xml: matches the 37-line template used by all three sibling graphql apps. Verdict: consistent.
  • Integration specs: extends Specification implements GraphQLSpec, no @Stepwise since both feature methods are independent. Verdict: consistent with sibling specs.
  • Coercing<Internal, External> generic signature: the in-repo built-ins use <T, T> because their Java type is already a valid wire value; <ObjectId, String> is the correct divergent-type form for a Java object that does not JSON-serialise to the wire form on its own (graphql-java 25 no longer toString's scalar return values). Verdict: consistent with the generic contract.
  • Spring Boot 4 package renames: org.springframework.boot.autoconfigure.{jdbc,orm,liquibase,batch,flyway,amqp,cache,jms,mail,data,security,web.reactive}.* produce zero remaining matches in the repo. The only remaining old-path reference is in grails-data-neo4j/boot-plugin which is not included in the main build (the Neo4j module is not used). Verdict: complete.
  • testImplementation vs integrationTestImplementation: every other use of grails-testing-support-mongodb in the repo is on testImplementation; commit 7 brings the multi-datastore-app in line.

Known non-blocking notes

  • HttpClientSupportSpec.httpPatchXml (custom client variant) intermittently fails under heavy parallel load because Ersatz's server.verify() races with the JDK HttpClient request completion. The test predates the merge and passes in isolation. It is not a regression from this PR.
  • org.grails.datastore.mapping.mongo.ProjectionsSpec (3 feature methods) is flaky when the full ./gradlew build is run on a machine where many tasks share the Docker daemon; root cause is a ServiceLoader singleton + @Shared manager interaction in GrailsDataTckSpec that predates this PR and did not block CI on fc747cc76a. Out of scope for the merge-fix PR but worth tracking separately.
  • grails-data-neo4j (separate Gradle build, not in main settings.gradle) has one remaining Spring Boot 3 import in Neo4jAutoConfiguration.groovy. The module is not used and not built by CI, so it is left for a separate Neo4j-focused PR.

Fixes the CI breakage on 8.0.x introduced by 6ebc60c6d0 plus the gradle-groovy drift that surfaced the same week.

The merge from 7.2.x reintroduced three Spring Boot 3 era package names
that are referenced as string literals (so the compiler cannot catch
them). On the Grails 8 / Spring Boot 4 stack those classes live in
new modules and the strings silently fail to match anything, so the
@EnableAutoConfiguration exclusions they describe never take effect.

* ApplicationClassInjector#CONDITIONAL_EXCLUSIONS pointed
  org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration
  at the wrong package; the class moved to
  org.springframework.boot.liquibase.autoconfigure in SB4.

* GrailsApplicationCompilerAutoConfiguration injected an exclude for
  org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
  into auto generated Application classes; that class moved to
  org.springframework.boot.jdbc.autoconfigure in SB4.

The matching ApplicationClassInjectorSpec assertion is updated so the
unit test catches future regressions of the liquibase entry.

Assisted-by: claude-code:claude-opus-4-7
The 7.2.x merge brought back graphql-java.version=24.3 from a Grails 7
era stack where Spring Boot did not yet manage graphql-java. On the
Grails 8 stack Spring Boot 4's spring-boot-dependencies BOM pins
graphql-java at 25.0, so the SB BOM constraint always wins resolution.
That left the Grails BOM declaring 24.3 while every project resolved
25.0, which validateDependencyVersions correctly flagged as drift on
grails-data-hibernate5-docs and grails-data-mongodb-docs.

Remove the redundant pin and the associated bomDependencies entry so
spring-boot-dependencies is the single source of truth for graphql-java
going forward. graphql-java-extended-scalars is not managed by SB, so
that one is kept; its 24.0 release is compatible with graphql-java 25.0
and is already exercised by 254 grails-data-graphql-core tests.

Assisted-by: claude-code:claude-opus-4-7
…le split

The merge from 7.2.x moved this standalone Spring Boot demo from
grails-data-graphql/examples/spring-boot-app/ to its current location
and updated DemoApplication.groovy to import the Spring Boot 4 path
org.springframework.boot.hibernate.autoconfigure.HibernateJpaAutoConfiguration,
but the build.gradle was not updated to actually depend on the module
that class now lives in.

The result was that :grails-test-examples-graphql-spring-boot-app
:compileGroovy failed every CI job that runs ./gradlew build
including the main Build Grails-Core matrix on every OS/JDK combo,
Functional Tests, Hibernate5 Functional Tests and MongoDB Functional
Tests, because all of those jobs depend on the root build target.

The class must be on the runtime classpath (not compileOnly) because
GORM's HibernateGormAutoConfiguration declares
@AutoConfigureBefore([HibernateJpaAutoConfiguration]). Without it
present at runtime the GORM auto config silently fails to load and
HibernateDatastore never gets registered, which AuthorIntegrationTests
exposes. Spring Boot's own JPA auto configuration is still suppressed
by the existing @EnableAutoConfiguration(exclude = ...) annotation in
DemoApplication, so GORM remains the sole Hibernate configurer.

Assisted-by: claude-code:claude-opus-4-7
The 7.2.x merge brought back testRuntimeOnly 'org.slf4j:slf4j-simple'
in grails-data-hibernate5/dbmigration/build.gradle. On the 8.0.x stack
spring-boot-starter-tomcat transitively pulls in
spring-boot-starter-logging which provides logback-classic, so leaving
slf4j-simple on the integration test classpath gives SLF4J two
competing bindings. Spring Boot's LogbackLoggingSystem aborts context
startup with "LoggerFactory is not a Logback LoggerContext but Logback
is on the classpath" the moment a Grails @Integration spec tries to
load an ApplicationContext.

Locally this took down all three specs in the module:
AutoRunWithMultipleDataSourceSpec, AutoRunWithSingleDataSourceSpec,
and DbUpdateCommandSpec. Removing the slf4j-simple line lets the SB
provided Logback binding take over and all three pass again.

jul-to-slf4j is left in place because Liquibase still uses
java.util.logging and we want that output bridged to SLF4J.

Assisted-by: claude-code:claude-opus-4-7
The merge from 7.2.x moved this example into the main settings.gradle
for the first time on 8.0.x. The forward port surfaced three
independent regressions that together made :integrationTest fail with
"no tests discovered" and then with broken assertions once discovery
was restored.

1) Spock test discovery. FooIntegrationSpec and BarIntegrationSpec
   were declared as `class X implements GraphQLSpec` without extending
   spock.lang.Specification, so JUnit's Spock engine never recognised
   them as tests. With nothing to run, the Grails @Integration
   bootstrapper never even tried to start a Spring context and
   Gradle 8.3+ raised "no tests discovered" rather than running them.
   Both specs are now `class X extends Specification implements
   GraphQLSpec` to match the sibling graphql examples.

2) Logback configuration format. The app shipped a logback.groovy.
   Logback's Groovy DSL was removed in logback-classic 1.5; on the
   8.0.x stack that file is now read by the XML parser and aborts
   with SAXParseException "Content is not allowed in prolog". Convert
   the file to an equivalent logback.xml.

3) MongoDB infrastructure. The application.yml binds MongoDB at
   localhost:27017 with no fallback. The main Build Grails-Core CI
   job and local builds do not start a MongoDB, so the Spring context
   could not initialise even after test discovery was fixed. Add
   integrationTestImplementation
   'org.apache.grails.testing:grails-testing-support-mongodb' so a
   MongoDB testcontainer is started for the integration test JVM and
   the host/port are injected via grails.mongodb.host.

4) GORM datastore lookup. Both specs asserted
   `session.datastore instanceof HibernateDatastore / MongoDatastore`
   inside a withNewSession closure. On the modern GORM/Hibernate
   stack the closure receives org.hibernate.internal.SessionImpl which
   does not expose a `datastore` property; the assertion blew up with
   MissingPropertyException. Replace with GormEnhancer.findStaticApi
   which is the public entry point that survives the upgrade.

5) graphql-java 25 scalar serialisation. MyGraphQLCustomizer declared
   Coercing<ObjectId, ObjectId> and returned an ObjectId from
   serialize. graphql-java 25 no longer toString()'s the returned
   scalar value during serialisation; it serialises the bean's
   structured properties (timestamp/date) instead. The custom scalar
   is changed to Coercing<ObjectId, String> and serialize now returns
   the 24 character hex form, matching the existing scalar
   description and what BarIntegrationSpec parses with new ObjectId.

After these changes :integrationTest passes 2/2 locally on the merge
commit with my other fixes applied.

Assisted-by: claude-code:claude-opus-4-7
Copilot AI review requested due to automatic review settings May 21, 2026 23:33
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request restores the 8.0.x branch build and CI health after the 7.2.x merge introduced Spring Boot 4 / Spring 7 / Jakarta-stack related breakages, by aligning dependency management and example/test modules with the new module/package layout and updated library behavior.

Changes:

  • Align Spring Boot 4 auto-configuration exclusions and required dependencies (including the SB4 Hibernate/JPA module split).
  • Remove a conflicting SLF4J binding and rely on Spring Boot’s Logback binding.
  • Repair GraphQL multi-datastore example integration tests and scalar coercion behavior for graphql-java 25.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated no comments.

Show a summary per file
File Description
grails-test-examples/graphql/spring-boot-app/build.gradle Adds required spring-boot-hibernate dependency for SB4’s Hibernate auto-config module split.
grails-test-examples/graphql/grails-multi-datastore-app/src/main/groovy/myapp/MyGraphQLCustomizer.groovy Updates ObjectId scalar coercing to match graphql-java 25 serialization expectations.
grails-test-examples/graphql/grails-multi-datastore-app/src/integration-test/groovy/myapp/FooIntegrationSpec.groovy Fixes integration spec base class and updates datastore assertion to modern GORM API.
grails-test-examples/graphql/grails-multi-datastore-app/src/integration-test/groovy/myapp/BarIntegrationSpec.groovy Same as above for Mongo-backed integration test.
grails-test-examples/graphql/grails-multi-datastore-app/grails-app/conf/logback.xml Replaces deprecated Groovy Logback DSL config with XML config compatible with current stack.
grails-test-examples/graphql/grails-multi-datastore-app/grails-app/conf/logback.groovy Removes deprecated Logback Groovy DSL configuration.
grails-test-examples/graphql/grails-multi-datastore-app/build.gradle Adds MongoDB test support for integration tests via grails-testing-support-mongodb.
grails-shell-cli/src/main/groovy/org/grails/cli/boot/GrailsApplicationCompilerAutoConfiguration.java Updates DataSource auto-config class reference to the SB4 package.
grails-data-hibernate5/dbmigration/build.gradle Removes conflicting slf4j-simple test runtime binding and documents rationale.
grails-core/src/test/groovy/org/grails/compiler/injection/ApplicationClassInjectorSpec.groovy Updates expected Liquibase auto-config exclusion class name for SB4.
grails-core/src/main/groovy/org/grails/compiler/injection/ApplicationClassInjector.groovy Updates Liquibase auto-config exclusion class name for SB4.
dependencies.gradle Stops pinning graphql-java so Spring Boot BOM remains the single source of truth.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

The grails-gradle subproject's BOM derives its Groovy version from
`gradle-groovy.version` in dependencies.gradle, while the main grails-
core BOM uses `groovy.version`. Those drifted: main was on 4.0.32 but
gradle was still pinned at 4.0.31, so when the grails-gradle modules
resolved Groovy artifacts transitively they landed on 4.0.32 (the
highest available) while their own BOM still declared 4.0.31.

The Validate Dependency Versions CI job for `cd grails-gradle && ./gradlew
validateDependencyVersions` failed for grails-gradle-common,
grails-gradle-model, grails-gradle-plugins, and grails-gradle-tasks
with messages like:

    org.apache.groovy:groovy-ant - resolved 4.0.32, expected 4.0.31
    org.apache.groovy:groovy-groovydoc - resolved 4.0.32, expected 4.0.31
    org.apache.groovy:groovy-templates - resolved 4.0.32, expected 4.0.31
    org.apache.groovy:groovy-xml - resolved 4.0.32, expected 4.0.31
    org.apache.groovy:groovy-docgenerator - resolved 4.0.32, expected 4.0.31

Align gradle-groovy.version with groovy.version so the two BOMs agree.

Assisted-by: claude-code:claude-opus-4-7
@jamesfredley jamesfredley self-assigned this May 22, 2026
@jamesfredley jamesfredley moved this to In Progress in Apache Grails May 22, 2026
@jamesfredley jamesfredley added this to the grails:8.0.0-M2 milestone May 22, 2026
…use style

Every other usage of grails-testing-support-mongodb in this repo uses
testImplementation (or testImplementation project(:grails-testing-support
-mongodb) for the internal modules). The integrationTest source set
extends test under the grails-extension-gradle-config so the testcontainer
support flows through to integration tests without needing the more
specific integrationTestImplementation configuration.

Spotted while auditing this PR against existing Gradle conventions in
the repo.

Assisted-by: claude-code:claude-opus-4-7
@testlens-app
Copy link
Copy Markdown

testlens-app Bot commented May 22, 2026

✅ All tests passed ✅

🏷️ Commit: fcf1f99
▶️ Tests: 31927 executed
⚪️ Checks: 35/35 completed


Learn more about TestLens at testlens.app.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

2 participants