jamesfredley commented on PR #15558:
URL: https://github.com/apache/grails-core/pull/15558#issuecomment-4445122810
## 2026-05-13 audit pass against Groovy 6.0.0-SNAPSHOT build #645
Pulled `apache/groovy` master to commit `0a04376328` ("try to make JMX tests
more resilient", 2026-05-13 13:20 UTC) and the published `6.0.0-SNAPSHOT`
snapshot at build #645 (`6.0.0-20260513.133635-645`).
### Snapshot audit window (build #609 -> build #645)
Diff'd `apache/groovy` master `bc4caccca6..0a04376328` (audit window
2026-05-08 19:42 UTC -> 2026-05-13 13:20 UTC). **No upstream fixes in this
window map onto a workaround on this canary.** The window contents:
- AI-readiness skills, javadoc/package-info additions
- `GROOVY-12001` jline 4.1.0 (already in last audit baseline)
- `GROOVY-12002` MarkdownSlurper support in groovysh
- `GROOVY-12003` `/img` command in groovysh
- `GROOVY-12004` grape command line maven/ivy shorthands
- `GROOVY-12005` Grape cache-corruption / CDN hardening
- `GROOVY-12006` Gradle 9.5.x bump (build)
- `GROOVY-12007` log4j2 2.26.0 bump (test dependency)
None overlap with the remaining workaround inventory.
### Brought forward from `grails8-groovy5-sb4` (merge `d3384e9395`)
- `0ce8095700` Fix dbmigration GroovyChangeLogSpec: drop env-dependent
log-capture assertions
- `9b048e177a` Restore micronaut-jackson-databind for grails-forge-web-netty
JSON runtime
- The 5.x audit work in `bda52ad1bb` (`8.0.x` merged back into
`grails8-groovy5-sb4`)
Conflict-free merge (`dependencies.gradle` kept `groovy.version:
'6.0.0-SNAPSHOT'` on this canary).
### Standing position
Zero Groovy-6-only workarounds remain on this canary. The five remaining
workarounds in the description are all inherited from #15557 and reproduce
identically on Groovy 5.0.6-SNAPSHOT and on Groovy 6.0.0-SNAPSHOT build #645.
[GROOVY-11985](https://issues.apache.org/jira/browse/GROOVY-11985) is the only
one with an open upstream ticket and now has a candidate fix in
[apache/groovy#2529](https://github.com/apache/groovy/pull/2529) - validated
end-to-end on this canary below.
---
## Validation of
[apache/groovy#2529](https://github.com/apache/groovy/pull/2529) (Paul King,
GROOVY-11985)
Tested in two independent layers:
### Layer 1: standalone reproducer
The standalone reproducer at
[jamesfredley/groovy-trait-static-method-override-bug](https://github.com/jamesfredley/groovy-trait-static-method-override-bug)
(the minimal extract of the `Validateable` site) was run against both versions
side-by-side, JDK 21:
| Build | Test 1 (direct call) | Test 2 (`this.defaultNullable()` from trait
body) | Test 3 (reflection workaround) |
|---|---|---|---|
| Apache snapshot **6.0.0-20260513.133635-645** (master HEAD, NO PR #2529) |
PASS | **FAIL** - override hijacked back to trait helper default | PASS |
| Local build of
[apache/groovy#2529](https://github.com/apache/groovy/pull/2529) HEAD
`9115a3de` rebased on master | PASS | **PASS** - override seen by trait body |
PASS |
```
$ ./gradlew run -PgroovyVersion=6.0.0-20260513.133635-645
Test 2: trait body sees `this.defaultNullable()` from inside the trait
result: false (expected: true if override is honoured, false if hijacked
by trait helper)
FAIL - override hijacked back to trait helper default
$ ./gradlew run -PgroovyVersion=6.0.0-SNAPSHOT # mavenLocal, post-PR-2529
build
Test 2: trait body sees `this.defaultNullable()` from inside the trait
result: true (expected: true if override is honoured, false if hijacked
by trait helper)
PASS - override seen by trait body
```
### Layer 2: real Grails `Validateable` workaround removed, validation tests
run
Removed the `resolveDefaultNullable(Class<?>)` reflection shim from
[`grails-validation/.../Validateable.groovy`](https://github.com/apache/grails-core/blob/grails8-groovy6-canary/grails-validation/src/main/groovy/grails/validation/Validateable.groovy#L301-L318)
and reverted both call sites to direct unqualified `defaultNullable()` calls
(the natural shape the trait was originally written in):
```diff
- boolean isDefaultNullable = resolveDefaultNullable(this)
+ boolean isDefaultNullable = defaultNullable()
...
- boolean isDefaultNullable = resolveDefaultNullable(this.class)
+ boolean isDefaultNullable = defaultNullable()
...
- private static boolean resolveDefaultNullable(Class<?> clazz) {
- ... // 18-line reflective dispatch helper removed
- }
```
Then republished the local Groovy build as the unique version
`6.0.0-PR2529-SNAPSHOT` (so Gradle resolution couldn't fall back to the
published Apache snapshot of plain `6.0.0-SNAPSHOT`) and pointed
`dependencies.gradle` at that version. Ran the full `ValidateableTraitSpec`
suite:
```
./gradlew :grails-validation:compileGroovy :grails-validation:test \
--tests grails.validation.ValidateableTraitSpec --rerun-tasks --no-daemon
```
**Confirmed via build log that the test was executing against
`6.0.0-PR2529-SNAPSHOT`:**
```
Executing Spock 2.4.0-groovy-5.0 with NOT compatible Groovy version
6.0.0-PR2529-SNAPSHOT
```
**Result: 14 of 14 ValidateableTraitSpec tests PASS** (1m 41s, BUILD
SUCCESSFUL), including the two that fail without Paul's fix when the workaround
is removed:
| Test | Without workaround, against build #645 (no fix) | Without
workaround, against PR #2529 |
|---|---|---|
| `Test that constraints are nullable by default if overridden and ensure
nullable:true constraint is not applied when no other constraints were defined
by user` | **FAIL** - `constraints.size() == 3` got `4`, stray
`name:nullable:true` from override-not-seen | **PASS** |
| `Test that properties defined in a class with overridden defaultNullable
which are not explicitly constrained are not accessed during validation` |
**FAIL** - `UnsupportedOperationException: getName() should not have been
called during validation`, override-not-seen forced unconstrained-property
access | **PASS** |
| Other 12 ValidateableTraitSpec cases | PASS | PASS |
### Conclusion
[apache/groovy#2529](https://github.com/apache/groovy/pull/2529) **fully
resolves** the GROOVY-11985 workaround on this canary. Once it merges to
`apache/groovy` master and a snapshot publishes containing the fix, the
`Validateable.resolveDefaultNullable(Class<?>)` reflection shim can be removed
and both call sites can revert to plain `defaultNullable()`. The local
validation edit, the unique-version Groovy republish, and the
`dependencies.gradle` pin were all reverted before this comment; the canary
tree is back at `d3384e9395`.
Thanks @paulk-asert - this clears the only one of the five remaining
inherited workarounds that had an open upstream ticket. The other four
(VariableScopeVisitor canonicalisation NPE, indy=false controller-parameter
scope loss, ConfigurationBuilder + AbstractConstraint static-init, GROOVY-6362
/ GROOVY-11817 `g` taglib regression) still need standalone reproducers filed
against apache/groovy.
--
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]