Add an end-to-end module verifying message bundles resolve - #16180
codeconsole wants to merge 4 commits into
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## 9.0.x #16180 +/- ##
==================================================
- Coverage 60.2724% 60.2669% -0.0055%
+ Complexity 25692 25691 -1
==================================================
Files 2208 2208
Lines 109030 109030
Branches 19755 19755
==================================================
- Hits 65715 65709 -6
- Misses 34561 34565 +4
- Partials 8754 8756 +2 🚀 New features to boost your workflow:
|
Review FindingsHead CI on this head is green, including "End to End Tests (end-to-end build only)", which runs I also ran the native half, which the PR says nobody has. Setup followed the README exactly: Oracle GraalVM for JDK 21 (21.0.12) as The PR is no longer marked draft, but the description still says "the reason this is a draft" and "the native half has not been executed yet". The second statement is accurate and should stay; the draft flag should come back until the first finding is resolved. FindingsHigh: the native half cannot pass on 8.0.x as pinned, and the README recipe does not produce a working imageWhat happens when the README is followed:
The first failure is GraalVM's bundled Groovy substitution targeting a method Groovy 4+ no longer has (oracle/graal #10200, #13096; fixed on master in April 2026 and present in the 25 line, not in 21). So the README's "a GraalVM JDK" has to say "GraalVM for JDK 25 or later", and the JDK-matching advice in the same section needs rewording, since the framework is then published on 21 and the image built on 25. The second failure is the real blocker. Line 247 passes Groovy knows about it.
The same hello-world against 6.0.0-RC-1 fails at that guard with Grails 8.0.x pins Consequences for this PR:
What would make the native half useful sooner: the snapshot canary workflow already rebuilds Grails against a Groovy branch by rewriting Medium: the "bundle never registered" diagnostic can never fire under this configurationReferences:
With Suggested fix: keep the property (the unknown-code assertion needs it), but make Low: leftover debugging in the build scriptReference: // Applied the legacy way: the Spring Boot plugin is on this build's classpath through the
// Grails plugin, so the id resolves, but it carries no version for the plugins block.
// TEMP probe: disabled
//apply plugin: 'org.springframework.boot.aot'Should go. It is also unnecessary: Spring Boot's Gradle plugin applies Low:
|
|
Thank you for actually building the image — that is the half nobody had run, and it turned a "not executed yet" into a diagnosis. Everything below is fixed or accepted. High — the native half cannot pass, and the README recipe does not produce a working image. Correct, and the README was worse than incomplete: it was missing the precondition that made it work the one time it did. That run used an unreleased build of GROOVY-12234 (apache/groovy#2766) forced onto The "Native image verification" section is rewritten. It now leads with the fact that Medium — the dead Low — commented-out AOT probe. Removed. Low — Low — the JDK justification. Fixed. It now says Low — GraalVM plugin version. 1.1.7 → 1.1.12, with a comment on why it is pinned in Nits. All taken: explicit exit code on failure via a single On the draft flag — the description claimed this "lets a missing hint actually be falsified", which is not true yet, and still referred to being a draft. Rewritten to say what the module does and does not currently prove. On where this belongs. Leaving it on 8.0.x rather than moving it to 9.0.x. The JVM half stands on its own here — it guards the descriptor → base name → hint pipeline, the multi-word plugin normalisation and the dotted base name, and it is what caught #16179 — and the native half is inert behind |
… native image AOT processing on a JVM cannot falsify a resource hint: every bundle on a JVM classpath is readable whether it was registered or not, so a missing hint shows up only in a compiled image. The application resolves one code from each way a bundle reaches the message source - the application's own bundle, a namespaced plugin bundle, and a dotted base name the application configured - in English and in a locale variant, plus an unknown code to prove MessageSourceProperties bound as a whole. This belongs on 9.0.x rather than 8.0.x. Dynamic Groovy in an image needs both GROOVY-12234's AOT link mode, which ships in the 6.0.0-beta-2 this branch pins and in no 5.x release, and invokedynamic, which this branch compiles with by default and which 8.0.x deliberately forces off for published artifacts. Either one missing is fatal to the image, so native image is not a Grails 8 capability. The JVM half runs in check and needs nothing special. The native half is opt-in behind -PnativeTests because it needs a GraalVM toolchain and minutes of CPU.
|
@matrei I am just moving this to 9.0.x so it can be feature complete |
655879b to
0760a7c
Compare
🚨 All tests passed but jobs failed 🚨Failed Jobs without Test Failures❌ Build Health / Core Projects 🏷️ Commit: 767c950 Rerun ControlsClick the checkbox to trigger a rerun:
Learn more about TestLens at testlens.app/docs. |
per request of @borinquenkid
Adds the end-to-end module #16176 asks for: an application that resolves messages from each way a bundle reaches the message source.
What it checks
messagesnative-messagesmessages*patterns never reach it. The plugin name is multi-word, so it also covers the descriptor's hyphenated spelling being matched against the plugin's camel-case one.config.i18n.customgrails-app/i18n, dotted, so it proves the base name is converted toconfig/i18n/custombefore use.Each is resolved in English and in a locale variant. An unknown code is resolved too, under
use-code-as-default-message, because base names alone would not proveMessageSourcePropertieswas bound — the whole object binds at once or not at all.Running it
The JVM half runs in
checkand needs nothing special:cd end-to-end ./gradlew :native-i18n:checkThe native half is opt-in, and does not pass today:
Limitations
UnsupportedFeatureError: Tried to define class), and with invokedynamic the bootstrap method runs beforeIndyInterface.<clinit>and NPEs atmakeBootHandle. What closes it is GROOVY-12234, an AOT link mode on the Groovy 6 line — 5.1.2, which the BOM pins, carries neitheraotDispatchnor theensureInitializedguard. Building the image also needs GraalVM 25 or later; on the 21 linenativeCompilefails during "Initializing" because GraalVM's bundled Groovy substitution targets a method Groovy 4+ removed (oracle/graal#10200). The module has been run successfully exactly once, against an unreleased build of GROOVY-12234 forced ontoruntimeClasspath. The README says all of this; the wiring is here so that it starts passing the day those land rather than having to be written then.CompilePlugindefaultsgrailsIndytofalsebecause Groovy 5's indy default is a large runtime regression for dynamic Groovy (Groovy - invoke dynamic performance problems #15293); its own comment records Grails 9 / Groovy 6 as where that can flip. So a released Grails 8 could not produce an image even if Groovy were fixed.nativeCompileis minutes of CPU and needs a toolchain no other job uses.Thanks to @matrei for running the native half and working out both upstream blockers.