feat(deps): Bump Micronaut platform to 5.0.0 (GA)#15677
Conversation
Micronaut Platform 5.0.0 shipped on Maven Central (io.micronaut.platform:micronaut-platform:5.0.0). Replace the 5.0.0-M2 milestone we were tracking with the released GA. Accompanying alignments required to keep validateDependencyVersions green: - dependencies.gradle: bump the grails-micronaut-bom Groovy override from 5.0.5 to 5.0.6 so the strict pin matches Micronaut 5 GA's managed groovy.version. spock stays at 2.4-groovy-5.0 (unchanged in GA). - grails-bom/micronaut/build.gradle: refresh the comment on the tools.jackson exclude. Micronaut 5.0.0 ships jackson-bom 3.1.3 while Spring Boot 4.0.6 ships 3.1.2 - the two platforms still disagree on the patch, but the direction is now reversed (Micronaut ahead of Spring Boot rather than behind). The exclude itself stays in place so spring-boot-dependencies remains the single source of truth for Jackson 3. This is the first commit of a draft PR; more fallout is expected as downstream modules pick up the new platform. Assisted-by: claude-code:claude-opus-4-7
Micronaut 5.0.0 GA publishes JARs with `org.gradle.jvm.version=25` and bytecode targeting JVM 25. The Grails 8 baseline is JDK 21, so the Java 21 matrix entries in `gradle.yml` cannot resolve or compile the Grails-Micronaut "island" (grails-micronaut, grails-micronaut-bom, the four micronaut-tied test-examples) without UnsupportedClassVersionError. The `-PskipMicronautProjects` mechanism already exists in `settings.gradle` (added in #15613) and is already wired into `groovy-joint-workflow.yml` (line 175). Mirror that pattern in the main `gradle.yml` matrix: pass the flag on Java 21 jobs only and let Java 25 jobs build the full graph including the Micronaut island. Jobs touched: - `build` - conditional flag, was failing on Java 21 - `buildRerunTasks` - Java 21 only, unconditional flag - `functional` - conditional flag - `mongodbFunctional` - conditional flag - `hibernate5Functional` - conditional flag `buildGradle` (grails-gradle composite build) and `buildForge` (grails-forge composite build) are left untouched - both are separate composite builds that do not include the Grails-Micronaut island and were not failing for this reason. Verified locally: ``` JAVA_HOME=corretto-21 ./gradlew validateDependencyVersions \ compileGroovy compileTestGroovy -PskipMicronautProjects # BUILD SUCCESSFUL JAVA_HOME=corretto-25 ./gradlew :grails-micronaut:compileGroovy \ :grails-test-examples-micronaut:compileGroovy ... # BUILD SUCCESSFUL JAVA_HOME=corretto-25 ./gradlew validateDependencyVersions # BUILD SUCCESSFUL ``` The `publish` job (line 388) still pins `java-version: 21` and would fail to publish Micronaut artifacts after this change. That is a release-flow gap that needs a separate follow-up (likely a parallel `publishMicronaut` job on JDK 25, or moving the existing job to JDK 25), tracked in the PR description. Assisted-by: claude-code:claude-opus-4-7
`micronautSerdeJacksonVersion=2.11.0` was pinned in January 2024 and
has missed 15 patch releases on the Micronaut Serde 2.x line. Bump
to the latest 2.x release, 2.16.2 (2025-11-10).
Stays on the 2.x line deliberately. Micronaut Serde 3.0.0 (which
the Micronaut 5 platform manages) has a hard compile dependency on
`micronaut-core:5.0.0` and would drag the Micronaut Core 5 / JVM 25
constraint into the main Grails graph. The consumers of this pin
(`grails-data-graphql/plugin` and the four `grails-test-examples/
graphql/*` apps) live outside the Grails-Micronaut "island" and
build on JDK 21, so they cannot take Serde 3.0.0.
The pin is intentionally floated outside the BOM today (per the
comment in `gradle.properties` line 55: "micronaut libraries not in
the bom due to the potential for spring mismatches"). That stays
the same; just the version moves.
`micronautRxjava2Version=2.9.0` is already the latest published
release on the Micronaut RxJava 2 line and is not bumped here. That
project has not been ported to Micronaut 5 (the platform removed
`io.micronaut.rxjava2` in PR micronaut-platform#1427) and 2.9.x
remains the current default branch.
Verified locally on JDK 21:
```
./gradlew validateDependencyVersions \
:grails-data-graphql:compileGroovy \
:grails-test-examples-graphql-grails-docs-app:compileGroovy \
:grails-test-examples-graphql-grails-docs-app:compileIntegrationTestGroovy \
:grails-test-examples-graphql-grails-multi-datastore-app:compileGroovy \
:grails-test-examples-graphql-grails-multi-datastore-app:compileIntegrationTestGroovy \
:grails-test-examples-graphql-grails-test-app:compileGroovy \
:grails-test-examples-graphql-grails-test-app:compileIntegrationTestGroovy \
:grails-test-examples-graphql-grails-tenant-app:compileGroovy \
:grails-test-examples-graphql-grails-tenant-app:compileIntegrationTestGroovy \
-PskipMicronautProjects
# BUILD SUCCESSFUL
```
Assisted-by: claude-code:claude-opus-4-7
The Forge composite build includes the root grails-core build via
`includeBuild('..')` in `grails-forge/settings.gradle:75`. When the
`buildForge` job in `.github/workflows/gradle.yml` runs `./gradlew
build` on Java 21, that triggers task resolution in the included
grails-core build - which pulls the JVM-25 Micronaut island and
fails with `Could not resolve io.micronaut:micronaut-inject-groovy`
(only compatible with JVM 25+).
Apply the same conditional `-PskipMicronautProjects` flag as the
other Java 21 matrix jobs. Forge itself stays on Micronaut 4.10.10
regardless; the flag only affects the included grails-core build's
Micronaut island, not Forge's own Micronaut 4 modules.
Verified locally on JDK 21:
```
cd grails-forge && JAVA_HOME=corretto-21 \
./gradlew assemble -PgrailsIndy=false -PskipCodeStyle \
-PskipTests -PskipMicronautProjects
# BUILD SUCCESSFUL
```
Assisted-by: claude-code:claude-opus-4-7
… tests
The `GraphQLSpec` test trait in `grails-data-graphql/plugin` shipped a
Micronaut RxJava2 HTTP client (`io.micronaut.rxjava2:micronaut-rxjava2-http-client`)
purely to exercise the GraphQL controller from integration tests. Spring
Boot 4 already provides everything that trait needs via Spring's
`RestClient` (org.springframework.web.client.RestClient) - there is no
reason for a Grails GraphQL plugin to depend on the Micronaut HTTP stack,
and `io.micronaut.rxjava2` has not been ported to Micronaut 5 anyway.
Replace the Micronaut HTTP client with Spring `RestClient`:
- `GraphQLSpec.groovy` is rewritten to use `RestClient.builder()` with a
single `StringHttpMessageConverter` (configured to accept all media
types). JSON encoding/decoding is handled by Groovy's `JsonOutput` and
`JsonSlurper` so the trait does not pull a Jackson or Gson runtime
onto the consuming apps - the graphql test apps use `grails-views-gson`
but do not actually ship the Gson library, and Grails 8 does not ship
Jackson on the default classpath either.
- Helper return type changes from `io.micronaut.http.HttpResponse<Map>`
to `org.springframework.http.ResponseEntity<Map>`. Spring's
`ResponseEntity.getBody()` returns `T` directly (not `Optional<T>`),
so the 109 `resp.body()` call sites collapse to `resp.body` (Groovy
property access) across 17 integration test specs.
- The two-arg `graphql(String, Class<T>)` overload survives but is now
String-only (the one caller, `InheritanceIntegrationSpec`, asserts on
the raw JSON string body). The single remaining `resp.getBody().get()`
call site (from Micronaut's `Optional`-returning getter) becomes
`resp.getBody()`.
The unused-after-removal version pins are dropped from `gradle.properties`:
- `micronautRxjava2Version=2.9.0` - was the rxjava2 client pin.
- `micronautSerdeJacksonVersion=2.16.2` - was only there as the JSON
mapper for the rxjava2 client. No production code uses
`@Serdeable` or any `io.micronaut.serde.*` API; confirmed via grep
across `grails-data-graphql/` and `grails-test-examples/graphql/`.
Each of the 4 graphql test apps loses both `implementation` lines (rxjava2
+ serde-jackson) - they brought no Spring Boot-side value once the Mn
HTTP client is gone.
Verified locally on JDK 21:
```
./gradlew validateDependencyVersions \
:grails-test-examples-graphql-grails-test-app:integrationTest \
:grails-test-examples-graphql-grails-docs-app:integrationTest \
:grails-test-examples-graphql-grails-multi-datastore-app:integrationTest \
:grails-test-examples-graphql-grails-tenant-app:integrationTest \
-PskipMicronautProjects
# BUILD SUCCESSFUL - all integration tests pass across all 4 apps
```
Side note: this closes the long-standing "out-of-band Micronaut pin"
comment in `gradle.properties`: nothing in grails-core now depends on
Micronaut artifacts outside the Grails-Micronaut "island" managed by
`grails-micronaut-bom`.
Assisted-by: claude-code:claude-opus-4-7
…lish
The existing `publish` job runs on JDK 21 and invokes the aggregate
`publish` task across the whole project. With the Micronaut 5 platform
bump that island now requires JVM 25 bytecode, so the JDK 21 publish
would fail trying to compile `grails-micronaut` / `grails-micronaut-bom`.
Mirror the matrix-job split into the publish side:
- Existing `publish` job (Java 21): pass `-PskipMicronautProjects` so
it publishes everything EXCEPT the Micronaut island. Same task list
(`publish aggregateChecksums aggregatePublishedArtifacts`), same
retry harness, same secrets.
- New `publishMicronaut` job (Java 25): publishes the two island
artifacts via the explicit task list
`:grails-micronaut:publish :grails-micronaut-bom:publish`. The three
test-example projects in the island are not published. Same retry
harness and secrets as the sibling job. Same `needs:` / `if:` gates
so both jobs fire from the same successful upstream matrix runs.
Going with the explicit task-list approach instead of adding a
complementary `-PonlyMicronautProjects` flag to `settings.gradle` -
the island only publishes two artifacts and the symmetric flag would
require inverting the gating logic in `settings.gradle:88`, which is
more moving parts than the situation warrants.
Verified locally:
```
# JDK 25 - publishes the Micronaut island
JAVA_HOME=corretto-25 ./gradlew :grails-micronaut:publishToMavenLocal \
:grails-micronaut-bom:publishToMavenLocal --no-build-cache --rerun-tasks
# BUILD SUCCESSFUL
# Artifacts confirmed:
# ~/.m2/.../grails-micronaut/8.0.0-SNAPSHOT/grails-micronaut-8.0.0-SNAPSHOT.pom
# ~/.m2/.../grails-micronaut-bom/8.0.0-SNAPSHOT/grails-micronaut-bom-8.0.0-SNAPSHOT.pom
# JDK 21 + -PskipMicronautProjects - the parallel non-Micronaut publish path
JAVA_HOME=corretto-21 ./gradlew :grails-core:publishToMavenLocal \
-PskipMicronautProjects --no-build-cache --rerun-tasks
# BUILD SUCCESSFUL
```
Also verified that the JDK-25 Micronaut island runs full tests (not
just compile) cleanly: `./gradlew :grails-micronaut:test
:grails-test-examples-micronaut:test
:grails-test-examples-micronaut-groovy-only:test
:grails-test-examples-plugins-micronaut-singleton:test` -> BUILD
SUCCESSFUL.
Assisted-by: claude-code:claude-opus-4-7
The Micronaut 5 platform GA targets JVM 25 bytecode, so the `grails-micronaut` and `grails-micronaut-bom` artifacts cannot be assembled or staged on the existing JDK 21.0.7 reproducibility pin. Apply the same JDK-split pattern already in place for the CI matrix (see `.github/workflows/gradle.yml`) to the Apache release flow: - New `JAVA_VERSION_MICRONAUT=25.0.3` env var alongside the existing `JAVA_VERSION=21.0.7`. This is the SECOND reproducibility pin for this repository - any verifier wanting to reproduce the Micronaut artifacts must use this exact JDK (Liberica, version `25.0.3+11`). - `release.yml`'s `publish` job now runs the existing pre-publish smoke checks and the three sibling publishToSonatype blocks (Gradle plugins, Grails Core, Grails Forge) on JDK 21 with `-PskipMicronautProjects`. The `assemble`, `grails-doc:build`, and forge composite builds all need the flag - the Forge composite includes `..` so it transitively pulls the Micronaut island. - A new `Publish Grails-Micronaut to Staging Repository` step switches to JDK 25 via `setup-java@v4` and stages the two island artifacts via the explicit task list `:grails-micronaut:publishToSonatype :grails-micronaut-bom:publishToSonatype`. It uses `findSonatypeStagingRepository` to land in the same staging repository that the JDK 21 blocks already populated (matched by `NEXUS_PUBLISH_DESCRIPTION`). - A trailing `setup-java@v4` step restores JDK 21 before the `closeSonatypeStagingRepository` and checksum/artifact-list combination steps. Symmetric, keeps any future steps that touch the repository's own Gradle config on the documented JDK. - `release-publish-docs.yml` and the `docs` job in `release.yml` both build `grails-doc` from the root project; they now pass `-PskipMicronautProjects` to keep Gradle from resolving the island's JVM 25 variants on the JDK 21 runner. The SDKMAN release steps (`sdkMinorRelease` / `sdkMajorRelease`, run from the Forge composite) also get the flag - they do not compile anything, but the include-build dependency on the Micronaut island makes the flag defensive. `.sdkmanrc` is documented as the primary JDK source; a comment block now explicitly tells local committers that the Micronaut island requires a separate `JAVA_VERSION_MICRONAUT` JDK installed via `sdk install java <version>-librca`, and points to the relevant RELEASE.md verification section (to be written in a follow-up commit on this PR). Downstream consumer impact (documented in the PR description): the published `grails-micronaut:8.0.0` artifact will carry `org.gradle.jvm.version=25` in its Gradle module metadata, so Grails 8 apps running on JDK 21 cannot depend on it. JDK-25 consumers can use the Micronaut integration; JDK-21 consumers use the rest of Grails 8. Phase 1 of 3 for the dual-JDK release flow. Follow-up commits update `etc/bin/Dockerfile`, the verify scripts, and `RELEASE.md` so verifiers can actually reproduce the JDK 25 artifacts. Assisted-by: claude-code:claude-opus-4-7
Verifiers reproduce release artifacts by running etc/bin/verify-reproducible.sh
inside the etc/bin/Dockerfile container. With Micronaut 5 now staged from
JDK 25, the verifier needs the matching Liberica JDK on disk and the
verify script needs to switch into it for the island only.
`etc/bin/Dockerfile`:
- Adds a second Liberica JDK install, version pinned via `JDK_25_VERSION`
and exposed via `JDK_25_HOME=/opt/liberica-jdk25`. Keep
`JDK_25_VERSION` synced with `JAVA_VERSION_MICRONAUT` in
`.github/workflows/release.yml`. Liberica's amd64 + arm64 tarballs are
downloaded from `download.bell-sw.com` and verified against the
SHA-512 checksums baked into the Dockerfile (multi-arch via
`dpkg --print-architecture`). The primary JDK 21.0.7 stays the
default - everything in `PATH`, `JAVA_HOME` is unchanged. The
secondary JDK is opt-in via `JDK_25_HOME`.
`etc/bin/verify-reproducible.sh`:
- The single-pass build (`grails-gradle → root → grails-forge`) splits
into two passes:
1. Default JDK 21: builds `grails-gradle` composite as-is, then
`./gradlew publishToMavenLocal -PskipMicronautProjects` in root,
then `grails-forge` composite (also with the skip flag because
Forge does `includeBuild('..')` and would otherwise pull the
Micronaut island through the transitive evaluation).
2. JDK 25 via `JDK_25_HOME` override: builds the two Micronaut
island artifacts only -
`:grails-micronaut:publishToMavenLocal :grails-micronaut-bom:publishToMavenLocal`.
- Script fails fast with a clear error if `JDK_25_HOME` is not set
(the container exports it; manual verifiers outside the container
must install Liberica JDK matching `$JAVA_VERSION_MICRONAUT` from
`release.yml` and export the env var).
`etc/bin/test-reproducible-builds.sh`:
- Same dual-pass structure, extracted into a `build_all()` helper so
both reproducibility passes (`first` and `second`) execute the same
sequence. The Micronaut island gets the same dual-build treatment
as the rest of the artifacts so any JDK-25-specific reproducibility
hazards (timestamp ordering in class files, constant pool ordering,
stack map frame layout) surface locally before they reach a
release vote.
Phase 2 of 3. Phase 3 updates RELEASE.md to teach maintainers and
ASF voters the dual-JDK requirement.
Assisted-by: claude-code:claude-opus-4-7
Three sections of RELEASE.md teach maintainers and ASF voters the new dual-JDK requirement that this PR introduces: 1. Section 2's "Manual Verification: Reproducible Jar Files" gets a new "Dual-JDK requirement for the Grails-Micronaut island" sub-section. Explains that Grails 8 release artifacts now come from two reproducibility pins ($JAVA_VERSION for everything except the Micronaut island, $JAVA_VERSION_MICRONAUT for grails-micronaut + grails-micronaut-bom) and tells outside-the- container verifiers how to install the secondary JDK and export $JDK_25_HOME before running `verify-reproducible.sh`. 2. The "Appendix: Verification from a Container" section gains a paragraph noting that the container now ships both JDKs and that `verify-reproducible.sh` switches between them automatically - no manual JDK juggling inside the container. 3. The "Step 1: Ensuring we are reproducible" appendix gets a note above the existing reproducibility gotchas list explaining the dual-JDK split, where both pins live, and the cross-file sync requirement: bumping either pin requires bumping the matching one in etc/bin/Dockerfile so the verification container can reproduce the new artifacts. Phase 3 of 3 - completes the dual-JDK release-flow work in this PR. Assisted-by: claude-code:claude-opus-4-7
✅ All tests passed ✅🏷️ Commit: 72a3c0a Learn more about TestLens at testlens.app. |
Micronaut-leak audit (post-
|
build.gradle |
Project | In the island gating? |
|---|---|---|
grails-bom/micronaut/build.gradle |
:grails-micronaut-bom |
✅ |
grails-micronaut/build.gradle |
:grails-micronaut |
✅ |
grails-test-examples/micronaut/build.gradle |
:grails-test-examples-micronaut |
✅ |
grails-test-examples/plugins/issue-11767/build.gradle |
:grails-test-examples-plugins-issue-11767 |
✅ |
grails-test-examples/plugins/micronaut-singleton/build.gradle |
:grails-test-examples-plugins-micronaut-singleton |
✅ |
Plus the two transitive consumers that depend on :grails-micronaut without naming io.micronaut directly:
:grails-test-examples-issue-11767(grails-test-examples/issue-11767/):grails-test-examples-micronaut-groovy-only(grails-test-examples/micronaut-groovy-only/)
All seven live inside the if (!skipMicronautProjects) { ... } block at settings.gradle:511-520.
Heads up: island is 7 projects, not 5
I'd been referring to "5 island projects" in earlier commits and the PR description. The correct count from settings.gradle is 7 build-graph projects in the island (the two missing in my count were :grails-test-examples-issue-11767 and :grails-test-examples-plugins-issue-11767). The two issue-11767 projects exist to repro apache/grails-core#11767 and depend on :grails-micronaut + :grails-micronaut-bom, so they correctly belong in the island gating.
The PUBLISHED subset is still just 2 (grails-micronaut plugin + grails-micronaut-bom), which is what publishMicronaut in gradle.yml and the new JDK 25 publish step in release.yml stage. The three test-examples + two issue-11767 projects build inside the island but aren't published.
grails-data-graphql was the only real leak; it's fixed
grails-data-graphql/plugin was the only non-island main-build project with a direct io.micronaut.* dependency, via the rxjava2 HTTP client compileOnly in GraphQLSpec.groovy. Removed in 228f9a52ce (replaced with Spring RestClient + Groovy JsonSlurper).
grails-forge/ is intentionally separate
The 50+ io.micronaut.* references in grails-forge/*/build.gradle are all in the separate composite build which is itself a Micronaut 4.10.10 app (grails-forge/gradle.properties:27). Out of scope - the Forge stays on Micronaut 4.x regardless of what the main grails-core multi-project does with Micronaut 5.
Net
Nothing in grails-core outside the explicitly-gated 7-project Micronaut island pulls Micronaut 5 / JDK 25 onto the JDK 21 classpath. The Forge composite is its own Micronaut 4.x universe. Audit clean.
Draft - stacked on top of #15676. This PR uses the Spring Boot 4.0.6 branch (
deps/spring-boot-4.0.6) as its base so the dependency-version fallout from that bump does not muddy this one.Bumps
micronautPlatformVersionfrom5.0.0-M2to5.0.0(GA, published to Maven Central on 2026-05-20).Release notes / changelog: https://github.com/micronaut-projects/micronaut-platform/releases/tag/v5.0.0
Maven Central: https://repo1.maven.org/maven2/io/micronaut/platform/micronaut-platform/5.0.0/
JDK toolchain split (the headline structural change)
Micronaut 5.0.0 GA publishes JARs with
org.gradle.jvm.version=25and bytecode targeting JVM 25 (verified against the publishedmicronaut-core-5.0.0.moduleandmicronaut-inject-groovy-5.0.0.module). Grails 8 baseline stays at JDK 21. To resolve the conflict:-PskipMicronautProjects(the mechanism added in CI - Groovy Joint Validation Build is broken: spock-core:2.4-groovy-5.0 incompatible with Groovy 4.x snapshot #15613 and already used bygroovy-joint-workflow.yml). They build and test the entire framework EXCEPT the Grails-Micronaut "island" (grails-micronaut,grails-micronaut-bom,grails-test-examples-micronaut*,grails-test-examples-plugins-micronaut-singleton).Affected jobs in
.github/workflows/gradle.yml:build,buildRerunTasks(Java 21 only - unconditional flag),functional,mongodbFunctional,hibernate5Functional. ThebuildGradle(grails-gradle) andbuildForge(grails-forge) composite builds were left untouched - they do not include the Grails-Micronaut island.Snapshot publish split (
gradle.yml, commitd6bfccbc7a)Mirrors the matrix-job split into the snapshot publish side:
publishjob (Java 21) gets-PskipMicronautProjectsso it publishes everything EXCEPT the Micronaut island. Same task list (publish aggregateChecksums aggregatePublishedArtifacts), same retry harness.publishMicronautjob (Java 25) publishes the two island artifacts via the explicit task list:grails-micronaut:publish :grails-micronaut-bom:publish. Same retry harness, secrets, andneeds:/if:gates.Went with the explicit task-list approach rather than adding a complementary
-PonlyMicronautProjectsflag tosettings.gradle- the island only publishes two artifacts (the three test-examples are not published), and the symmetric flag would require inverting the gating logic insettings.gradle:88for negligible benefit.Apache release flow + reproducibility (commits
79b8260986,e7552a729e,72a3c0a514)Apache releases require byte-for-byte reproducible verification by ASF voters. The existing flow pins
JAVA_VERSION=21.0.7acrossrelease.yml,.sdkmanrc, andetc/bin/Dockerfilefor exactly this reason - any verifier must use the same JDK to reproduce. Micronaut 5's JVM 25 bytecode breaks that assumption: the island artifacts cannot be built on JDK 21 and cannot be reproduced on a different JDK 25 micro-version/vendor.The PR introduces a SECOND reproducibility pin and threads it through the whole release flow:
release.yml: newJAVA_VERSION_MICRONAUT=25.0.3env var. The existing publish steps (Grails Core, Grails Forge, smoke-check assemble, docs build, SDKMAN release) all gain-PskipMicronautProjectsso they keep working on JDK 21. A new "Publish Grails-Micronaut to Staging Repository" step usesactions/setup-java@v4to switch to JDK 25, runs:grails-micronaut:publishToSonatype :grails-micronaut-bom:publishToSonatypeagainst the same staging repository (viafindSonatypeStagingRepositorymatching the sharedNEXUS_PUBLISH_DESCRIPTION), then a trailingsetup-javastep restores JDK 21 for thecloseSonatypeStagingRepositorystep and downstream checksum aggregation.etc/bin/Dockerfile: the verification container now installs a second Liberica JDK alongside the primary 21.0.7. Pinned version + SHA-512 (multi-arch: amd64 + arm64 fromdownload.bell-sw.com) baked into the Dockerfile so reproducing the verification container is itself deterministic. Exposed as$JDK_25_HOME. Primary JDK 21 stays the default onPATH/JAVA_HOME.etc/bin/verify-reproducible.sh+etc/bin/test-reproducible-builds.sh: each splits its single-pass build into two passes - default JDK 21 for everything outside the island (-PskipMicronautProjectsto keep the root + forge composites from evaluating the JVM 25 deps), then a JDK 25 pass viaJAVA_HOME=$JDK_25_HOMEfor the two island artifacts only. Fails fast with a clear error when$JDK_25_HOMEis unset (manual verifiers outside the container mustsdk install java <version>-librcaand export the env var first)..sdkmanrc: documents the secondary-JDK side channel and points atRELEASE.mdfor the manual install steps. sdkman can only manage one JDK at a time, so the secondary pin lives inrelease.yml+Dockerfile.RELEASE.md: three sections updated. Section 2 "Manual Verification: Reproducible Jar Files" gains a "Dual-JDK requirement for the Grails-Micronaut island" sub-section. The "Verification from a Container" appendix notes that both JDKs ship in the container. The "Step 1: Ensuring we are reproducible" appendix gets a note above the existing gotchas list calling out the two pins and the Dockerfile sync requirement.Consumer-side impact
The published
grails-micronaut:8.0.0artifact carriesorg.gradle.jvm.version=25in its Gradle module metadata. Grails 8 apps running on JDK 21 will see Gradle variant resolution REJECT the dependency - this artifact is consumable only by Grails apps running JDK 25+. JDK 21 users get the rest of Grails 8 without the Micronaut integration. The Apache release ships both audiences; choosing the right artifact set is on the consumer'sgradle.properties/pom.xml.What's in the platform bump commit
The minimum change set that keeps
validateDependencyVersionsgreen after the bump:gradle.properties:micronautPlatformVersion=5.0.0-M2->5.0.0.dependencies.gradle: bump thegrails-micronaut-bomGroovy override from5.0.5to5.0.6so our strict pin matches Micronaut 5 GA's managedgroovy.version. Spock stays at2.4-groovy-5.0(unchanged in GA).grails-bom/micronaut/build.gradle: refresh the comment on thetools.jacksonexclude. Micronaut 5.0.0 shipsjackson-bom 3.1.3while Spring Boot 4.0.6 ships3.1.2- the platforms still disagree on the patch, just in the opposite direction now. The exclude itself stays in place sospring-boot-dependenciesremains the single source of truth for Jackson 3.Notable upstream version moves (5.0.0-M2 -> 5.0.0)
Read directly from the published
micronaut-platform-5.0.0.pom:micronaut.core.version5.0.0-M235.0.0micronaut.data.version5.0.0-M55.0.1micronaut.serde.version3.0.0-M73.0.0micronaut.mongo.version/micronaut.mongodb.version6.0.1-M16.0.1micronaut.langchain4j.version2.0.0-M12.0.0jackson.version3.1.03.1.3groovy.version5.0.45.0.6spock.version2.4-groovy-5.02.4-groovy-5.0spring.boot.version4.0.34.0.6(matches our pin)spring.version7.0.67.0.7Known platform-level breaking changes
From the v5.0.0 release notes - flagged here so reviewers know what to watch for in subsequent commits:
io.reactivex:rxjavaandio.micronaut.rxjava2:*are no longer managed by the platform.micronaut-rxjava2has never been ported to Micronaut 5 and the 2.9.x branch is the project's current default. This PR removes the dependency entirely - see commit228f9a52cebelow.micronaut.eclipsestore.version=2.0.0).jaxrs.api.version3.1.0 -> 4.0.0 (Jakarta RESTful Web Services 4).kafka.version3.9.1 -> 4.2.0 - major Kafka client jump.The kafka/jaxrs/microstream items do not affect anything
grails-corecurrently consumes, but they are landmines for downstream Grails-on-Micronaut apps.Micronaut HTTP client purge in
grails-data-graphqlThe
GraphQLSpectest trait used to pullio.micronaut.rxjava2:micronaut-rxjava2-http-client+io.micronaut.serde:micronaut-serde-jacksonpurely to talk to the running Grails app from integration tests. Spring Boot 4 shipsorg.springframework.web.client.RestClientwhich does exactly the same thing, andio.micronaut.rxjava2is a dead-end for Micronaut 5 anyway. Commit228f9a52cereplaces it:GraphQLSpec.groovyis rewritten to useRestClient.builder()with a singleStringHttpMessageConverter. JSON is handled by Groovy'sJsonOutput/JsonSlurperso no Jackson or Gson runtime is dragged onto the test app classpath (Grails 8 ships neither by default).io.micronaut.http.HttpResponse<Map>toorg.springframework.http.ResponseEntity<Map>. The 109resp.body()call sites collapse toresp.body(Groovy property access) across 17 integration test specs.gradle.propertiesloses bothmicronautRxjava2VersionandmicronautSerdeJacksonVersionpins - both are now unused.implementationlines for rxjava2 + serde-jackson.Net effect: nothing in
grails-corenow depends on Micronaut artifacts outside the Grails-Micronaut "island" managed bygrails-micronaut-bom.Verified locally on JDK 21 (with
-PskipMicronautProjects): all integration tests pass across all 4 graphql test apps.What is NOT addressed in this PR
grails-forgestays onmicronautVersion=4.10.10(the Forge IS a Micronaut 4.x app, per the comment atgrails-forge/gradle.properties:27). Out of scope for the Micronaut 5 platform bump.Everything else that surfaced during this PR's review - the JDK 21/25 CI matrix split, the Forge composite include-build fallout, the Serde + RxJava2 out-of-band pins, the GraphQL-side Micronaut HTTP client removal, the snapshot
publishMicronautjob ingradle.yml, the dual-JDK Apache release flow inrelease.yml, and the dual-JDK reproducibility infrastructure (Dockerfile + verify scripts + RELEASE.md) - is RESOLVED in commits on this branch. See the commit stack:Verification