Fix forge dockerBuildNative builder stage to use Java 21 GraalVM image#15580
Merged
jamesfredley merged 2 commits into8.0.xfrom Apr 16, 2026
Merged
Fix forge dockerBuildNative builder stage to use Java 21 GraalVM image#15580jamesfredley merged 2 commits into8.0.xfrom
jamesfredley merged 2 commits into8.0.xfrom
Conversation
PR #15579 updated the `dockerfileNative` baseImage to `ghcr.io/graalvm/native-image-community:21.0.2-ol9`, but the Forge prev-snapshot GCP deploy workflow still fails with `UnsupportedClassVersionError: class file version 65.0` because the generated Dockerfile's builder stage continues to emit: Step 1/13 : FROM ghcr.io/graalvm/native-image:ol7-java17-22.3.2 AS graalvm On Micronaut Gradle Plugin 3.7.10 (our pinned micronautApplicationPluginVersion), `baseImage(...)` on the `dockerfileNative` task only affects the final runtime stage of the multi-stage Dockerfile. The builder stage (`FROM ... AS graalvm`), where the `native-image` tool actually runs against our Java 21 bytecode, is controlled by the separate `graalImage` property. `graalImage`'s convention is derived from `jdkVersion` via `toGraalVMBaseImageName(graalVersion, jdkVersion)` -> `ghcr.io/graalvm/native-image:ol7-${jdkVersion}-${graalVersion}`, and `jdkVersion` is clamped by `NativeImageDockerfile.toSupportedJavaVersion` against `SUPPORTED_JAVA_VERSIONS = [17, 11]`. That silently rounds Java 21 down to Java 17, so even with the toolchain set to 21 the builder image still resolves to a Java 17 container that rejects class file version 65.0. Explicitly set `graalImage` to the same Java 21 community image we use for the runtime stage so both stages of the generated Dockerfile run on Java 21. This unblocks the failing job `grails-forge-web-netty:dockerBuildNative` (and the analytics module) on 8.0.x: https://github.com/apache/grails-core/actions/runs/24518601227 A longer-term fix is to upgrade `micronautApplicationPluginVersion` from 3.7.10 to 4.x, which adds first-class Java 21 and `native-image-community` support, but that is a larger change; this two-line override unblocks CI now. Assisted-by: claude-code:claude-opus-4-7
Contributor
There was a problem hiding this comment.
Pull request overview
Updates Forge native-image Docker build configuration so the builder stage (GraalVM/native-image compilation) uses a Java 21 GraalVM image, fixing CI failures caused by Java 17 builder images rejecting Java 21 bytecode.
Changes:
- Set
graalImagefordockerfileNativetoghcr.io/graalvm/native-image-community:21.0.2-ol9to control the Dockerfile builder stage image. - Keep
baseImage(...)pinned to the same Java 21 image for the final runtime stage. - Add inline documentation explaining Micronaut Gradle Plugin 3.7.10 behavior and why both properties are needed.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| grails-forge/grails-forge-web-netty/build.gradle | Pins the native-image builder stage via graalImage to match the Java 21 runtime image. |
| grails-forge/grails-forge-analytics-postgres/build.gradle | Same graalImage override to ensure Java 21 is used during native compilation. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Address Copilot review feedback on PR #15580: the `ghcr.io/graalvm/native-image-community:21.0.2-ol9` tag was duplicated across `graalImage` and `baseImage(...)` in both `grails-forge-web-netty/build.gradle` and `grails-forge-analytics-postgres/build.gradle`. If only one of the two were updated in a future bump, the builder and runtime stages would drift apart and the class-file-version mismatch this fix prevents could silently reappear. Assign the image string once to a `nativeImageContainer` local and reuse it for both properties so the two stages can never disagree on the GraalVM version. Assisted-by: claude-code:claude-opus-4-7
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
:grails-forge-web-netty:dockerBuildNative(and the analytics variant) withUnsupportedClassVersionError: class file version 65.0.3.7.10(our pinnedmicronautApplicationPluginVersion),baseImage(...)ondockerfileNativeonly controls the final runtime stage of the generated multi-stage Dockerfile. The builder stage (FROM ... AS graalvm), wherenative-imageactually compiles our Java 21 bytecode, is controlled by the separategraalImageproperty. Its default clampsjdkVersionagainstSUPPORTED_JAVA_VERSIONS = [17, 11]and falls back toghcr.io/graalvm/native-image:ol7-java17-22.3.2.graalImageondockerfileNativeto the same Java 21 image in bothgrails-forge-web-netty/build.gradleandgrails-forge-analytics-postgres/build.gradle. Both stages of the generated Dockerfile now run on Java 21.Failure excerpt
Full log: https://github.com/apache/grails-core/actions/runs/24518601227/job/71673053922
Why the previous fix was insufficient
Reading
NativeImageDockerfile.javaat tagv3.7.10:from(new From(getGraalImage().get()).withStage("graalvm")).graalImageconvention:getGraalVersion().zip(getJdkVersion(), NativeImageDockerfile::toGraalVMBaseImageName)->ghcr.io/graalvm/native-image:ol7-${jdkVersion}-${graalVersion}.jdkVersionauto-derives from the toolchain but passes throughtoSupportedJavaVersion, which is backed bySUPPORTED_JAVA_VERSIONS = Arrays.asList(17, 11). Java 21 therefore silently becomesjava17.baseImage(...)only callsgetBaseImage().set(imageName), andBaseImageForBuildStrategyResolver.resolve()returns that value for the finalfrom(baseImageProvider)(runtime stage) only.So the PR #15579 override reaches only the runtime
FROM. The builderFROM ... AS graalvmwas stillghcr.io/graalvm/native-image:ol7-java17-22.3.2, which rejects class file 65.0.Verification
dockerBuildNativelocally in this environment (no Docker), but the change is a pure DSL override that takes precedence over the plugin's Java-17-only convention. The image tag matches the one already chosen for the runtime stage in Fix forge dockerBuildNative by using a Java 21 GraalVM base image #15579.Longer-term follow-up
micronautApplicationPluginVersion=3.7.10(March 2023) is the root reason we need this manual override: plugin 4.x adds first-class Java 21 andnative-image-communitysupport, sograalImagewould be derived correctly without a hard-coded string. Bumping the plugin would be a broader change though (DSL surface shifts across 3.x -> 4.x), so this PR is intentionally minimal - just unblocking the current CI workflow.Notes for reviewers
8.0.xbecause that's where the deploy workflow was bumped to Java 21 ind1362be7("Minimum requirement for Java 21 & update to asset-pipeline to fix NPE").7.1.xstill runsjava-version: '17'in the same workflow (last run #24475218726 succeeded), so it is not affected.fix/branch prefix should auto-label this PR asbugper.github/release-drafter.yml.