jamesfredley opened a new issue, #16157: URL: https://github.com/apache/grails-core/issues/16157
Tracking issue for the changes Apache Grails needs in order to build and test against Apache Groovy 6. It documents what the [`grails8-groovy6-canary`](https://github.com/apache/grails-core/pull/15558) branch carries, why each item exists, and the condition under which it can be deleted. Current canary state: Groovy `6.0.0-beta-2`, Grails 9 (`9.0.x` base), JDK 21, Spring Boot 4.1, Spock `2.4-groovy-5.0`. Every row below was established empirically: a workaround was only kept when removing it produced an observed failure, and only removed when the build and tests stayed green without it. ## 1. Upstream Groovy 6 regression (not yet reported) Static type checking merges the flow state of a `||` inside a closure to `void`, so a variable guarded by `x == null || ...` becomes unusable in the branch body. ```groovy @groovy.transform.CompileStatic class Repro { void bind(Collection val, Class componentType) { List boundItems = [] ((Collection) val).each { item -> if (item == null || componentType.isAssignableFrom(item.getClass())) { boundItems << item // FAILS on 6.0.0-beta-2 } } } } ``` ``` [Static type checking] - Cannot find matching method java.util.ArrayList#leftShift(void) ``` The Map form fails equivalently with `Cannot find matching method java.util.LinkedHashMap#putAt(java.lang.Object, void)`. - Compiles cleanly on Groovy `5.0.8`; fails on `6.0.0-beta-2`. It is a regression, not intended behaviour. - `&&` in the same position compiles. Only `||` triggers it. - Adding type arguments to the cast, declaring the closure parameter type, converting to `for`-in with an explicit `Object` declaration, and renaming shadowed variables all still fail. The trigger is the `||` flow merge itself. - Workaround: hoist the disjunction into a `boolean` local. Short-circuiting and null handling are unchanged. Applied in `grails-web-databinding/.../GrailsWebDataBinder.groovy` (both the Collection and Map branches). ## 2. Workarounds currently required on 6.0.0-beta-2 | # | File | Groovy 6 behaviour | Observed failure without it | Removable when | |---|---|---|---|---| | 1 | `grails-web-databinding/.../GrailsWebDataBinder.groovy` | `\|\|` flow state inside a closure infers as `void` | `Cannot find matching method java.util.ArrayList#leftShift(void)` | The regression in section 1 is fixed upstream | | 2 | `grails-core/.../config/external/WriterFilteringMap.groovy` | `@Delegate` now generates mutator methods that were previously excluded | `WriteFilteringMapSpec`: `getWrittenValues().size() == 0` | Groovy restores the previous `@Delegate` mutator exclusion behaviour, or the exclusion is made explicit upstream | | 3 | `grails-datamapping-core/.../GormEntity.groovy` | Generic trait method signatures are specialized differently, so the trait-injected `merge(Object)` is not found | `NoSuchMethodException: Book.merge(Object)` | Groovy restores the prior generic trait-signature resolution | | 4 | `grails-datastore-core/.../reflect/ClassPropertyFetcher.java` | Interface methods surface differently during property introspection | `MissingMethodException ... __transients$get` | Unknown - needs upstream confirmation of intended behaviour | | 5 | `grails-testing-support-http-client/.../utils/XmlUtils.groovy` | The SAX/JAXP feature set recognised by the default parser configuration changed | `DOCTYPE is disallowed...` | Unknown - needs upstream confirmation. Note this code is XXE-hardening; it must not be dropped for convenience | | 6 | `grails-validation/.../ValidateableTraitSpec.groovy` | Static trait methods are emitted with a modifier combination the verifier rejects | `illegal combination of modifiers: abstract and static` | Groovy fixes static trait-method emission | | 7 | `grails-data-hibernate7/.../HibernateGormInstanceApi.groovy` | Negated `instanceof` requires explicit parenthesization | Compile failure | Groovy restores the prior parsing precedence | | 8 | `grails-fields/.../BeanPropertyAccessorImpl.groovy` | `@Canonical` no longer implies `@MapConstructor` | Compile failure | Groovy reinstates the implied `@MapConstructor`, or Grails adopts the explicit annotation permanently | | 9 | `grails-views-gson/.../GrailsJsonViewHelper.groovy`, `.../internal/TemplateRenderer.groovy` | Closure/generic inference changes under `@CompileStatic` | Compile failure | Unknown - needs upstream confirmation | | 10 | `grails-testing-support-http-client/.../HttpClientSupport.groovy` | A `static final` constant in a Spec collides with a trait's instance getter | `cannot have both a static and an instance method` | Groovy restores the prior static/instance resolution | | 11 | `gradle/*-test-config.gradle`, `gradle.properties` | Spock 2.4 is built against Groovy 5 and refuses to run on Groovy 6 | `Executing Spock 2.4.0-groovy-5.0 with NOT compatible Groovy version 6.0.0-beta-2` | Spock ships a `groovy-6.0` build. This is the single largest category and is not a Grails or Groovy defect | | 12 | `grails-common/build.gradle`, `dependencies.gradle` | `groovy-callsite` is a separate module in Groovy 6 | Missing class at runtime | Never - this is a permanent Groovy 6 packaging change | | 13 | `build-logic/.../SbomPlugin.groovy` | Groovy 6 pulls JLine 4, whose POM omits the licence the SBOM validation expects | SBOM licence validation failure | JLine publishes complete licence metadata | | 14 | `settings.gradle` | The Micronaut island is pinned to Groovy 5 and must not join a Groovy 6 build | Version conflict on JDK 25 CI | The Micronaut island supports Groovy 6 | | 15 | `.github/workflows/groovy-joint-workflow.yml` | Groovy 6 development is still on `master`; `GROOVY_6_0_X` does not exist | Joint validation checks out a non-existent branch | Apache Groovy cuts a `GROOVY_6_0_X` branch | ## 3. Workarounds retired at 6.0.0-beta-2 | Workaround | Why it is gone | |---|---| | Gradle `9.6.1` bump (wrappers, `.sdkmanrc`, generated-project template, `gradleToolingApiVersion`) | Never a Groovy 6 requirement. The build is green on `9.6.0` | | `CoreGrailsPlugin` `BeanConfiguration.addProperty` workaround | Obsolete: the `beanRegistrar` rewrite on `9.0.x` removed the BeanBuilder closure entirely | | `GrailsApplicationLifeCycle` rewritten as a Java interface | Groovy 6 handles the interface default method again. Verified by booting a Grails application under `-PgrailsIndy=false`, the classic-callsite mode the original bug required. Its regression test is retained as coverage | | ~30 further snapshot-era compile workarounds | No longer reproduce on `6.0.0-beta-2` | ## 4. Not Groovy 6 issues - `ConfigurationBuilder` nested-map handling is **Spring 7** compatibility and is independent of the Groovy version. It is currently carried by the canary and should be extracted into its own `9.0.x` pull request; trunk has the underlying gap today. - Embedded MongoDB and Testcontainers failures seen locally are environmental (no Docker, or no network to `fastdl.mongodb.org`). ## Verification Root build compiles on `6.0.0-beta-2`. Tests re-run with `--rerun-tasks`, zero failures: `grails-core` 507, `grails-datastore-core` 108, `grails-datamapping-core` 293, `grails-web-url-mappings` 211, `grails-validation` 91, `grails-testing-support-http-client` 105, `grails-test-suite-uber` 566. A Grails application boots and passes integration tests under `-PgrailsIndy=false`. -- 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]
