close
Skip to content

Upgrade to Gradle 9.4.1 and Micronaut 4#15365

Merged
jdaugherty merged 43 commits into
8.0.xfrom
upgrade/gradle-9.3.1
Apr 26, 2026
Merged

Upgrade to Gradle 9.4.1 and Micronaut 4#15365
jdaugherty merged 43 commits into
8.0.xfrom
upgrade/gradle-9.3.1

Conversation

@jamesfredley

@jamesfredley jamesfredley commented Jan 30, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR upgrades the Grails build infrastructure to use Gradle 9.4.1 and upgrades the grails-forge module to Micronaut 4. It also bumps the root project's Micronaut Platform BOM and related library versions to the latest 4.10.x releases.

Issue: #14738

Changes

Gradle 9.4.1 Upgrade

  • Update Gradle wrapper from 8.14.4 to 9.4.1 across all modules (root, build-logic, grails-gradle, grails-forge, profiles, shell-cli)
  • Fix deprecated Gradle APIs:
    • Replace convention with extensions API
    • Replace ConfigureUtil with ClosureBackedAction
    • Replace JavaPluginConvention with JavaPluginExtension
    • Replace outputFile with destinationFile in WriteProperties
  • Add @DisableCachingByDefault to ApplicationContextCommandTask, ApplicationContextScriptTask, and GrailsRunTask to satisfy Gradle 9's requirement that all task types declare their caching intent
  • Add JUnit Platform launcher dependency required by Gradle 9 for test execution
  • Update .sdkmanrc to reference Gradle 9.4.1
Category Change Files
Gradle wrapper 8.14.4 to 9.4.1 across all projects gradle-wrapper.properties (7), .sdkmanrc
convention to extensions Replace removed convention API SbomPlugin, RockerPlugin, RockerSourceSetProperty
ConfigureUtil to ClosureBackedAction Replace removed Gradle API Gradle plugin sources
JavaPluginConvention to JavaPluginExtension Replace removed convention type Build plugins
outputFile to destinationFile Replace deprecated WriteProperties API Build plugins
@DisableCachingByDefault Annotate JavaExec task types for Gradle 9 plugin validation ApplicationContextCommandTask, ApplicationContextScriptTask, GrailsRunTask
Duplicate metadata merge Fix spring-configuration-metadata.json conflict in issue-11767 test plugin (both Java and Groovy annotation processors generate this file) grails-test-examples/plugins/issue-11767/build.gradle

Develocity Plugin Update

  • Update com.gradle.develocity from 4.1.1 to 4.3.2 (latest compatible with Develocity 2025.4.2)
  • Update com.gradle.common-custom-user-data-gradle-plugin from 2.3 to 2.4.0
  • Migrate grails-data-neo4j and grails-data-graphql from deprecated gradle-enterprise plugin to develocity
  • Add reproducible build configuration to grails-data-neo4j and grails-data-graphql settings

Task.project Deprecation Fixes

Fix deprecated Task.project access at execution time (scheduled for removal in Gradle 10) by capturing project properties at configuration time:

  • SbomPlugin: Capture projectName, projectPath, and buildDate at configuration time; refactor pickLicense() to accept these values instead of task reference
  • PublishPlugin: Capture PublishingExtension at configuration time before doLast block
  • GrailsGradlePlugin: Capture buildDir at configuration time for buildProperties task; capture antBuilder at configuration time for native2ascii task
  • GrailsProfileGradlePlugin: Replace project.sync in doLast with proper Sync task type

Micronaut 4 Upgrade (grails-forge)

  • Upgrade Micronaut from 3.10.4 to 4.10.10
  • Upgrade Groovy from 3.x to 4.0.30
  • Upgrade Spock from 2.1-groovy-3.0 to 2.3-groovy-4.0
  • Upgrade Micronaut Application Plugin from 4.5.3 to 4.6.2
  • Migrate javax.* to jakarta.* validation annotations:
    • javax.validation.constraints.* -> jakarta.validation.constraints.*
    • javax.transaction.* -> jakarta.transaction.*
  • Update dependency coordinates:
    • org.codehaus.groovy -> org.apache.groovy
    • io.micronaut:micronaut-bom -> io.micronaut.platform:micronaut-platform
    • javax.inject:javax.inject -> jakarta.inject:jakarta.inject-api
  • Add micronaut-validation-processor for annotation processing
  • Add micronaut-jackson-databind runtime dependency
  • Add @Validated annotation to controllers using validation
  • Remove explicit shadow plugin (now provided by micronaut-application plugin)
Category Change Files
Micronaut (grails-forge) 3.10.4 to 4.10.10; Groovy 3.x to 4.0.30; Spock 2.1 to 2.3 Forge gradle.properties, all forge build.gradle files
Micronaut Application Plugin 4.5.3 to 4.6.2 Forge gradle.properties
javax to jakarta (grails-forge) javax.validation.* to jakarta.validation.*; javax.transaction.* to jakarta.transaction.* Forge controllers, services
Forge dependency coordinates org.codehaus.groovy to org.apache.groovy; io.micronaut:micronaut-bom to io.micronaut.platform:micronaut-platform Forge build files

Micronaut Platform Upgrade (root project)

Dependency Old Version New Version
Micronaut Platform BOM 4.9.2 4.10.10
Micronaut HTTP Client 4.9.9 4.10.18
Micronaut Serde Jackson 2.11.0 2.16.2

The Micronaut Platform 4.10.10 BOM aligns with Spring Boot 3.5.10 and Spring Framework 6.2.16, which are compatible with the Grails 8.0.x target stack.

Test Infrastructure Updates

  • Add ByteBuddy dependency for Spock mocking on Java 17+ (CGLIB doesn't support Java 17 class files)
  • Add --add-opens java.base/java.lang=ALL-UNNAMED JVM arg for test execution
  • Update Coherence BOM license mapping for version 25.03.2

Testing

Application Testing

  • grails-forge-web-netty starts successfully and displays Micronaut 4 banner
  • Application starts on configured port

Plugin Output Verification

Verified output before/after Task.project deprecation fixes:

Plugin Task Result
SbomPlugin cyclonedxBom Identical (except expected timestamp/UUID differences)
PublishPlugin savePublishedArtifacts Identical
GrailsProfileGradlePlugin processProfileResources Identical
GrailsGradlePlugin buildProperties Functionally identical (minor formatting: one less blank line from switching Ant to Java Properties API)
grails-doc docs Identical (ref/Versions/Grails BOM.html verified)

Workarounds

None - all changes in this PR are proper fixes, not workarounds.

Known Deprecation Warnings

The following deprecation warning remains (scheduled for Gradle 10, not blocking):

Warning Source Resolution
StartParameter.isConfigurationCacheRequested org.asciidoctor.jvm.convert plugin 4.0.5 Fix in asciidoctor-gradle-plugin 5.0.0+ (currently alpha). Tracked: asciidoctor#756

Open Decisions

Decision Context Options
Asciidoctor plugin upgrade 4.0.5 has Gradle 10 deprecation warning; 5.0.0 is alpha (a) Stay on 4.0.5 until 5.0.0 GA, (b) Accept deprecation warning
Coherence BOM license Updated mapping for 25.03.2 Verify license compatibility with ASF policy

Breaking Changes

None expected for end users. This is an internal build infrastructure upgrade.

Related Issues

- Update Gradle wrapper to 9.3.1 across grails-core, grails-gradle, build-logic, and profiles
- Migrate Groovy dependencies from org.codehaus.groovy to org.apache.groovy (Groovy 4+)
- Update Spock version for grails-gradle tests to 2.3-groovy-4.0
- Add explicit groovy.ant.AntBuilder import (moved in Groovy 4)
- Make task classes abstract for Gradle 9 compatibility (JavaExec, AbstractCompile)
- Add @Inject annotation to task constructors where required
- Fix documentation configuration attributes for groovydoc tasks
- Update IOUtilsSpec test expectation for Groovy 4 Spock jar name

Note: grails-forge remains on Gradle 8.x pending Micronaut 4 upgrade
- Upgrade Micronaut from 3.10.4 to 4.10.7
- Upgrade Groovy from 3.x to 4.0.30
- Upgrade Spock from 2.1-groovy-3.0 to 2.3-groovy-4.0
- Migrate javax.* to jakarta.* validation annotations
- Update dependency coordinates from org.codehaus.groovy to org.apache.groovy
- Update micronaut-bom to micronaut-platform
- Add micronaut-validation-processor for annotation processing
- Add micronaut-jackson-databind runtime dependency
- Fix deprecated Gradle APIs (conventions -> extensions)
- Add ByteBuddy for Spock mocking on Java 17+
- Add JUnit Platform launcher for Gradle 9 test execution
- Remove shadow plugin (now provided by micronaut-application plugin)
Copilot AI review requested due to automatic review settings January 30, 2026 15:24
@jdaugherty

Copy link
Copy Markdown
Contributor

@jamesfredley did Gradle 9 not remove the testClassDir as they said they would? It was my understanding the integration plugin + TCK had to be rewritten similar to the grails-publish project to work.

@jdaugherty

Copy link
Copy Markdown
Contributor
  • The SBOM plugin needs updated as part of upgrading to Gradle 9.
  • How are you working around the configuration cache? Are you just disabling it? It's my understanding plugins like asciidoctor cause issues with it unless you disable. If we update, it should work.

Replace artifacts { archives ... } with direct task dependencies on
assemble task as recommended by Gradle 9 deprecation warnings.

The remaining StartParameter.isConfigurationCacheRequested deprecation
is from the Asciidoctor Gradle plugin (grolifant library) and cannot
be fixed in this project.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Upgrades the build/tooling stack to Gradle 9.3.1 and updates the grails-forge module family to Micronaut 4 / Groovy 4 / Spock 2.3, including related Gradle API migrations and test-runtime adjustments.

Changes:

  • Bump Gradle wrapper/tooling versions to 9.3.1 across the repo and update Gradle plugin/task implementations for Gradle 9 APIs.
  • Upgrade grails-forge to Micronaut 4 (Groovy 4, Spock 2.3) and migrate javax.* validation/transaction usage to jakarta.*.
  • Update test infrastructure for Gradle 9 / Java 17+ (JUnit Platform launcher, Byte Buddy, and JVM --add-opens).

Reviewed changes

Copilot reviewed 55 out of 55 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
grails-profiles/profile/skeleton/gradle/wrapper/gradle-wrapper.properties Update profile skeleton Gradle wrapper to 9.3.1
grails-profiles/base/skeleton/gradle/wrapper/gradle-wrapper.properties Update base skeleton Gradle wrapper to 9.3.1
grails-gradle/plugins/src/main/groovy/org/grails/gradle/plugin/views/markup/MarkupViewCompilerTask.groovy Make task type abstract for Gradle 9 task instantiation model
grails-gradle/plugins/src/main/groovy/org/grails/gradle/plugin/views/json/GsonViewCompilerTask.groovy Make task type abstract for Gradle 9 task instantiation model
grails-gradle/plugins/src/main/groovy/org/grails/gradle/plugin/run/GrailsRunTask.groovy Make task type abstract for Gradle 9 task instantiation model
grails-gradle/plugins/src/main/groovy/org/grails/gradle/plugin/profiles/tasks/ProfileCompilerTask.groovy Make task type abstract for Gradle 9 task instantiation model
grails-gradle/plugins/src/main/groovy/org/grails/gradle/plugin/commands/ApplicationContextScriptTask.groovy Make task abstract and add @Inject constructor for Gradle services
grails-gradle/plugins/src/main/groovy/org/grails/gradle/plugin/commands/ApplicationContextCommandTask.groovy Make task abstract and add @Inject constructor for Gradle services
grails-gradle/plugins/build.gradle Switch Groovy dependency coordinates to org.apache.groovy
grails-gradle/model/src/test/groovy/grails/io/IOUtilsSpec.groovy Update test expectation for Spock Groovy 4 artifact name
grails-gradle/model/build.gradle Switch Groovy deps to org.apache.groovy coordinates (including tests)
grails-gradle/gradle/wrapper/gradle-wrapper.properties Update Gradle wrapper to 9.3.1
grails-gradle/common/build.gradle Switch Groovy deps to org.apache.groovy coordinates (including tests)
grails-gradle/bom/build.gradle Use Gradle-embedded Groovy BOM via org.apache.groovy:groovy-bom
grails-forge/test-core/build.gradle Switch Groovy dependency coordinates to org.apache.groovy
grails-forge/grails-forge-web-netty/build.gradle Remove explicit Shadow plugin; add Micronaut Jackson databind runtime dep
grails-forge/grails-forge-core/src/main/java/org/grails/forge/util/GitHubUtil.java Migrate validation annotations to jakarta.validation
grails-forge/grails-forge-core/src/main/java/org/grails/forge/feature/build/gradle/templates/gradleWrapperProperties.rocker.raw Update generated wrapper template to Gradle 9.3.1
grails-forge/grails-forge-core/src/main/java/org/grails/forge/client/github/oauth/AccessToken.java Migrate validation annotations to jakarta.validation
grails-forge/grails-forge-core/build.gradle Switch to Micronaut platform BOM; update Groovy coords; add validation/Jackson APIs
grails-forge/grails-forge-cli/build.gradle Switch to Micronaut platform BOM; update Groovy coords and resolution strategy
grails-forge/grails-forge-api/src/test/groovy/org/grails/forge/api/DiffClient.groovy Migrate validation annotations to jakarta.validation
grails-forge/grails-forge-api/src/main/java/org/grails/forge/api/diff/DiffOperations.java Migrate validation annotations to jakarta.validation
grails-forge/grails-forge-api/src/main/java/org/grails/forge/api/diff/DiffController.java Migrate validation annotations to jakarta.validation
grails-forge/grails-forge-api/src/main/java/org/grails/forge/api/create/zip/ZipCreateOperation.java Migrate validation annotations to jakarta.validation
grails-forge/grails-forge-api/src/main/java/org/grails/forge/api/create/zip/ZipCreateController.java Add @Validated; migrate validation annotations to jakarta.validation
grails-forge/grails-forge-api/src/main/java/org/grails/forge/api/create/github/GitHubRedirectService.java Migrate validation annotations to jakarta.validation
grails-forge/grails-forge-api/src/main/java/org/grails/forge/api/create/github/GitHubCreateService.java Migrate validation annotations to jakarta.validation
grails-forge/grails-forge-api/src/main/java/org/grails/forge/api/create/github/GitHubCreateOperation.java Migrate validation annotations to jakarta.validation
grails-forge/grails-forge-api/src/main/java/org/grails/forge/api/create/github/GitHubCreateController.java Migrate validation annotations to jakarta.validation
grails-forge/grails-forge-api/src/main/java/org/grails/forge/api/create/AbstractCreateController.java Migrate validation annotations to jakarta.validation
grails-forge/grails-forge-api/build.gradle Switch to Micronaut platform BOM; add validation processor; jakarta inject; Jackson runtime
grails-forge/grails-forge-analytics-postgres/src/main/java/org/grails/forge/analytics/postgres/Application.java Migrate validation annotations to jakarta.validation
grails-forge/grails-forge-analytics-postgres/src/main/java/org/grails/forge/analytics/postgres/AnalyticsController.java Migrate transactions to jakarta.transaction
grails-forge/grails-forge-analytics-postgres/build.gradle Remove explicit Shadow plugin; add Micronaut Jackson databind runtime dep
grails-forge/gradle/wrapper/gradle-wrapper.properties Update Gradle wrapper to 9.3.1
grails-forge/gradle/test-config.gradle Add Gradle 9 test runtime deps + JVM args for Java 17+
grails-forge/gradle/micronaut-openapi-config.gradle Replace deprecated WriteProperties.outputFile with destinationFile
grails-forge/gradle/doc-config.gradle Switch Groovy documentation deps to org.apache.groovy
grails-forge/gradle.properties Bump Micronaut/Groovy/Spock versions; add Byte Buddy version; update Jakarta inject version
grails-forge/buildSrc/src/main/java/org/grails/forge/rocker/plugin/RockerSourceSetProperty.java Replace deprecated Gradle configure util; add DSL delegation helpers
grails-forge/buildSrc/src/main/java/org/grails/forge/rocker/plugin/RockerPlugin.java Replace deprecated convention/DSL object usage with JavaPluginExtension + ExtensionAware
grails-data-neo4j/docs/build.gradle Update docs configuration attributes + switch Groovy coords to org.apache.groovy
grails-data-mongodb/docs/build.gradle Update docs configuration attributes for Gradle 9 variant-aware resolution
grails-data-hibernate5/docs/build.gradle Update docs configuration attributes for Gradle 9 variant-aware resolution
gradle/wrapper/gradle-wrapper.properties Update root Gradle wrapper to 9.3.1
gradle.properties Bump gradleToolingApiVersion to 9.3.1
dependencies.gradle Bump gradle-spock BOM version to Groovy 4 compatible variant
build-logic/plugins/src/main/groovy/org/apache/grails/buildsrc/SbomPlugin.groovy Add license mapping for Coherence BOM 25.03.2
build-logic/gradle/wrapper/gradle-wrapper.properties Update build-logic Gradle wrapper to 9.3.1
build-logic/docs-core/src/main/groovy/grails/doc/ant/DocPublisherTask.groovy Add missing AntBuilder import for Groovy/Gradle compatibility
build-logic/docs-core/build.gradle Switch Groovy coords to org.apache.groovy and update Groovy test artifact
.sdkmanrc Update SDKMAN Gradle version to 9.3.1

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread grails-forge/gradle/test-config.gradle Outdated
Update asciidoctor plugin version for consistency across modules.

Note: The StartParameter.isConfigurationCacheRequested deprecation
warning remains as it's caused by the grolifant library used by
asciidoctor-gradle-plugin. This will be fixed in version 5.0.0
(currently in alpha) which upgrades to Grolifant 5.
@jdaugherty

Copy link
Copy Markdown
Contributor
  • Have you tested the extractConstraints & bom plugins? It's my understanding that cross project resolution doesn't work in Gradle 9. Have you diffed the versions to see that it's working?

@jamesfredley

Copy link
Copy Markdown
Contributor Author

I did not touch SBOM in this PR. Grails BOM Dependencies still generates during grails-doc build.

Fix CodeNarc violations by removing unused Gradle API imports that
were left over from refactoring.
@jamesfredley

Copy link
Copy Markdown
Contributor Author

asciidoctor still results in a deprecation warning, which will be resolved in 5 https://github.com/asciidoctor/asciidoctor-gradle-plugin/releases

Add explicit dependency on groovydoc task from dist task to satisfy
Gradle 9's stricter validation of implicit task dependencies.

The dist task uses outputDir (build/docs) which includes the output
from the groovydoc task (build/docs/groovydoc), so an explicit
dependency is required.
@jamesfredley

Copy link
Copy Markdown
Contributor Author

All the BOM and build-logic plugins are working correctly:

  • ✅ grails-bom:extractConstraints - SUCCESS
  • ✅ grails-bom:build - SUCCESS
  • ✅ grails-gradle-bom:build - SUCCESS
  • ✅ build-logic:build - SUCCESS

@jdaugherty

Copy link
Copy Markdown
Contributor

The reports in those other tickets claim that they execute but the wrong datasets are returned with gradle 9. I would suggest diffing the output of those files and clearing any local cache before calling this a victory.

@jamesfredley

Copy link
Copy Markdown
Contributor Author

grails-bom and /ref/Versions/Grails%20BOM.html have the correct dependencies and versions.

- Update com.gradle.develocity from 4.1.1 to 4.3.2 (latest compatible with Develocity 2025.4.2)
- Update com.gradle.common-custom-user-data-gradle-plugin from 2.3 to 2.4.0
- Migrate grails-data-neo4j and grails-data-graphql from deprecated gradle-enterprise plugin to develocity
- Add reproducible build configuration to grails-data-neo4j and grails-data-graphql settings
- Add comment documenting known Asciidoctor plugin deprecation (StartParameter.isConfigurationCacheRequested)
Fix deprecated Task.project access at execution time (scheduled for removal in Gradle 10)
by capturing project properties at configuration time.

Changes:
- SbomPlugin: Capture projectName, projectPath, and buildDate at configuration time;
  refactor pickLicense() to accept these values instead of task reference
- PublishPlugin: Capture PublishingExtension at configuration time before doLast block
- GrailsGradlePlugin: Capture buildDir at configuration time for buildProperties task;
  capture antBuilder at configuration time for native2ascii task
- GrailsProfileGradlePlugin: Replace project.sync in doLast with Sync task type

Tested output before/after changes:
- cyclonedxBom: Identical (except expected timestamp/UUID differences)
- savePublishedArtifacts: Identical
- processProfileResources: Identical
- buildProperties: Functionally identical (minor formatting: one less blank line)
- grails-doc docs: Identical (verified Grails BOM.html)
Replace deprecated leftShift operator (<<) with modern tasks.register and doLast
syntax for Gradle 9+ compatibility.
Update gradle-wrapper.jar, gradlew, and gradlew.bat across all locations:
- Main project (gradle/)
- build-logic/
- grails-gradle/
- grails-forge/
- grails-forge/grails-forge-core/src/main/resources/gradle/ (template)
- grails-profiles/base/skeleton/ (template)
- grails-profiles/profile/skeleton/ (template)
- grails-shell-cli/src/test/resources/gradle-sample/
@jamesfredley jamesfredley added the relates-to:v8 Grails 8 label Feb 3, 2026
@jamesfredley jamesfredley linked an issue Feb 5, 2026 that may be closed by this pull request
@jamesfredley jamesfredley moved this to In Progress in Apache Grails Feb 5, 2026
@jamesfredley jamesfredley added this to the grails:8.0.0-M1 milestone Feb 5, 2026
@jamesfredley

Copy link
Copy Markdown
Contributor Author

Companion PR for the Gradle 9.4.1 upgrade in grails-gradle-publish: apache/grails-gradle-publish#25

matrei and others added 3 commits April 24, 2026 12:08
…ublish plugin bump

Review feedback from #15365:

* OpenTelemetry version (replace BOM import with explicit constraints):
  Selenium 4.41+ needs OpenTelemetry 1.59+, which is newer than the 1.55.x
  that Spring Boot 4.0.5 manages. The previous fix relied on importing the
  opentelemetry-bom AFTER the spring-boot-bom so ordering would favour the
  newer version, but BOM import order is not guaranteed by Gradle or Maven
  resolution (same class of issue as the hibernate PR). Replace the BOM
  import with an explicit list of opentelemetry module constraints in the
  grails-bom constraints block; explicit constraints always win over any
  imported BOM's transitively-managed version, regardless of ordering.

* grails-publish 0.0.4 -> 1.0.0-SNAPSHOT: adopt the Gradle 9 / Java 21
  compatible build of the publish plugin. Add the apache snapshots repo to
  root, grails-gradle, and grails-gradle/buildSrc pluginManagement blocks
  so the composite build classpath resolves the snapshot before the
  org.apache.grails.buildsrc.repo settings plugin itself is available.

* grails-forge: bump groovyVersion 4.0.30 -> 4.0.31 to match grails-core;
  drop ':$micronautVersion' suffix from micronaut-inject-groovy references
  in grails-forge-api, grails-forge-cli, grails-forge-core, grails-forge-
  web-netty, grails-forge-analytics-postgres, and test-core so the
  micronaut-platform BOM (already applied in each file) manages the
  version. Apply the micronaut-platform BOM in test-core which was using
  direct versions only.

* grails-forge-web-netty: runtimeOnly io.micronaut:micronaut-jackson-
  databind -> io.micronaut.serde:micronaut-serde-jackson. The forge stack
  is a Micronaut 4 serde-jackson application; the legacy jackson-databind
  runtime was a copy-paste from an earlier generator template.

* grails-test-examples-micronaut: import the micronaut-platform BOM and
  drop the per-artifact micronautHttpClientVersion / micronautSerde-
  JacksonVersion references. Delete the now-unused properties from the
  root gradle.properties.

* grails-bom/build.gradle: drop the java-platform plugin guard on the
  extractConstraints task. The task is only consumed internally, so the
  external-use guard no longer needs to exist.

* grails-doc/build.gradle: disable the per-project groovydoc' task
  instead of adding a dependency from dist'. grails-doc has no real
  source to document - the groovy plugin is only applied for version
  resolution, so the default groovydoc output is empty/unused. The user-
  facing groovydoc is produced by the aggregateGroovydoc task, which is
  already wired into the docs task.

* Drop grails-doc/src/en/ref/Configuration/Application Properties.adoc.
  This 866-line new documentation file is unrelated to the Gradle 9 /
  Micronaut 4 upgrade and will be submitted separately.

Assisted-by: claude-code:claude-opus-4-7
@matrei

matrei commented Apr 24, 2026

Copy link
Copy Markdown
Contributor

Related #15597

@jamesfredley

Copy link
Copy Markdown
Contributor Author

Round of review feedback addressed

Telemetry override (your top-level comment): swapped the opentelemetry-bom platform import for an explicit list of constraints on the individual io.opentelemetry:opentelemetry-* modules in dependencies.gradle / grails-bom/build.gradle. Explicit constraints win over any imported BOM's transitively-managed version, so this no longer relies on platform import ordering (same class of issue you hit on the hibernate PR).

grails-publish 0.0.4 -> 1.0.0-SNAPSHOT: consume the new Gradle 9 / Java 21 build of the publish plugin. Added the apache snapshots repo to the root, grails-gradle, and grails-gradle/buildSrc pluginManagement blocks so the composite build classpath can resolve the snapshot before org.apache.grails.buildsrc.repo itself is loaded (chicken-and-egg with the existing includeBuild-only pluginManagement). Companion PR: apache/grails-gradle-publish#25.

Inline review threads (all 20 resolved): see individual replies on each thread. Every prior unresolved item is now either confirmed already in place or fixed in the new commit:

  • grails-forge micronaut-inject-groovy and jackson-databind -> serde-jackson via the BOM (5 forge build files + test-core + analytics-postgres)
  • grails-forge/gradle.properties groovyVersion 4.0.30 -> 4.0.31
  • grails-bom/build.gradle java-platform plugin guard removed (per your guidance the task is internal-only)
  • grails-doc/build.gradle per-project groovydoc task disabled (no real source to document) instead of adding a dist dependency on it
  • gradle.properties micronautHttpClientVersion / micronautSerdeJacksonVersion removed; grails-test-examples-micronaut now uses the micronaut-platform BOM
  • Application Properties.adoc reverted out of this PR (will submit separately)

Latest 8.0.x merged in: picked up the Jansi/JLine upgrade (#15367), forge GraalVM image fixes (#15579, #15580), pull request template, and 7.1.0 release. One conflict in dependencies.gradle resolved by keeping the gradle-9 versions (gradle-spock 2.3-groovy-4.0, gradle-groovy 4.0.31, grails-publish 1.0.0-SNAPSHOT) and 8.0.x's jansi 2.4.2.

Locally verified ./gradlew :grails-bom:extractConstraints and ./gradlew :grails-test-examples-scaffolding:validateDependencyVersions both pass on Java 21+ after the merge.

… reference

Two follow-up CI issues from the previous review-feedback commit:

* Add the apache snapshots repo to the pluginManagement blocks in
  buildSrc/settings.gradle, grails-forge/settings.gradle, and grails-
  forge/buildSrc/settings.gradle. Same chicken-and-egg pattern as the
  root and grails-gradle settings - the org.apache.grails.buildsrc.repo
  plugin cannot register the snapshot repo until its own classpath
  (which transitively pulls grails-publish:1.0.0-SNAPSHOT) has been
  resolved.

* grails-gradle/plugins/build.gradle: org.codehaus.groovy:groovy-test-
  junit5 -> org.apache.groovy:groovy-test-junit5. The codehaus reference
  was inherited from the 8.0.x merge (toolchain test rewrite commit
  fec38f4) and resolves to an empty version because no BOM declares
  the codehaus coordinates.

Verified ./gradlew :grails-bom:extractConstraints :grails-test-examples-
scaffolding:validateDependencyVersions still passes locally.

Assisted-by: claude-code:claude-opus-4-7
@jamesfredley jamesfredley requested a review from jdaugherty April 24, 2026 14:50
Comment thread buildSrc/settings.gradle Outdated
repositories {
mavenCentral()
gradlePluginPortal()
// Needed to resolve grails-publish snapshots that the build-logic composite

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should take #15597 instead of these changes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in fe527d1. Merged matrei's gradle-plugin-bootstrap branch from #15597 (preserving authorship through the merge commit) and removed the inline pluginManagement.repositories { ... } block I had here. All seven settings files (root, build-logic, buildSrc, grails-forge, grails-forge/buildSrc, grails-gradle, grails-gradle/buildSrc) now route plugin repos through the shared gradle/plugin-repositories.gradle script.

Comment thread dependencies.gradle
Comment thread dependencies.gradle Outdated
'rxjava2.version' : '2.2.21',
'rxjava3.version' : '3.1.12',
'selenium.version' : '4.38.0',
'selenium.version' : '4.41.0',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We had pinned selenium to .38 due to the geb issues. Do we know if these are resolved?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Investigated; resolved in ccafe2b.

Spring Boot 4.0.5's BOM declares selenium 4.37.0 (spring-boot-dependencies 4.0.5). I rolled selenium back from 4.41.0 to 4.37.0 to track Spring Boot's choice. The OpenTelemetry 1.59.0 override and the 24-module constraint list that the 4.41 bump previously required are no longer needed - Spring Boot's managed 1.55.x line is sufficient for selenium 4.37.

Verified:

  • gradle validateDependencyVersions passes across all 157 reporting tasks (no resolved-vs-BOM mismatches).
  • gradle :grails-test-examples-{geb,geb-context-path,geb-gebconfig}:compileTestGroovy succeeds.
  • The geb test apps now resolve org.seleniumhq.selenium:selenium-bom:4.37.0 with no transitive opentelemetry conflict.

Net diff: dependencies.gradle shrinks by 40 lines.

Comment thread dependencies.gradle Outdated
'jquery.version' : '3.7.1',
'liquibase-hibernate5.version': '4.27.0',
'mongodb.version' : '5.6.4',
// Selenium 4.41+ requires OpenTelemetry 1.59+, which is newer than the

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this upgrade just to prevent conflicts with forge's version of micronaut?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The opentelemetry pin existed only because selenium 4.41+ pulled in opentelemetry-* modules newer than the 1.55.x line that Spring Boot 4.0.5 manages, which the dependency-version validator flagged. Not related to forge / micronaut.

Now that ccafe2b rolls selenium back to 4.37.0 (Spring Boot's managed version), there is no more transitive conflict and the entire opentelemetry block - the opentelemetry.version constant plus all 24 opentelemetry-* constraints - is gone.

Comment thread gradle.properties Outdated
gradleChecksumPluginVersion=1.4.0
# note: the cyclonedx 3 requires Gradle 9
gradleCycloneDxPluginVersion=2.4.1
# CycloneDX Gradle plugin version

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 5d21fc3.

it.inputs.dir(src)
it.outputs.dir(dest)

// Capture ant builder at configuration time to avoid Task.project access at execution time

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's remove the comment, it's obvious once we're on Gradle 9 why we did this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 5d21fc3 (also dropped the second // See: ... doc-link line).


TaskProvider<Task> processProfileResourcesTask = project.tasks.register('processProfileResources')
processProfileResourcesTask.configure { Task task ->
// Use Sync task type instead of project.sync in doLast to avoid Task.project access at execution time

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's remove the comment, it's unnecessary

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 5d21fc3 (also dropped the second // See: ... doc-link line).

Comment thread grails-gradle/settings.gradle Outdated
import org.gradle.api.initialization.resolve.RepositoriesMode

pluginManagement {
repositories {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should use #15597 instead

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in fe527d1. Merged matrei's gradle-plugin-bootstrap branch from #15597 (preserving authorship through the merge commit) and removed the inline pluginManagement.repositories { ... } block I had here. All seven settings files (root, build-logic, buildSrc, grails-forge, grails-forge/buildSrc, grails-gradle, grails-gradle/buildSrc) now route plugin repos through the shared gradle/plugin-repositories.gradle script.

// Re-run whenever either compile step emits new metadata; the task mutates files
// inside the compile output directories so we cannot rely on standard up-to-date
// tracking. The work is cheap (two small JSON files) so always running is acceptable.
outputs.upToDateWhen { false }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the incorrect way to do this. we should be able to cache these merges, otherwise it will always run this in our build.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactored properly in d9a2181.

The merge task now:

  • Writes to a new build/merged-spring-configuration-metadata/ directory instead of mutating compile outputs.
  • Declares inputs.files(provider { ... }) filtered to existing files at execution time, plus an outputs.file(...) and outputs.cacheIf { true } so Gradle can cache it.
  • dependsOn compileJava, compileGroovy so the compile output dirs always populate before the merge runs.
  • The jar task uses an eachFile filter to drop the per-compiler copies but keep the merged copy (a global exclude would have discarded the merged document too).

Verified:

  • A second gradle :grails-test-examples-plugins-issue-11767:jar invocation reports compileJava, compileGroovy, mergeMicronautConfigMetadata and jar all UP-TO-DATE - the always-rerun behavior is gone.
  • The produced jar contains exactly one META-INF/spring-configuration-metadata.json with both PluginJavaMicronautBean and PluginGroovyMicronautBean entries merged (groups + properties).
  • Consumer app (:grails-test-examples-issue-11767:compileGroovy) still compiles cleanly.

Also dropped the long block comment - the inline 7-line summary above the task is enough.

Comment thread settings.gradle Outdated
import org.gradle.api.initialization.resolve.RepositoriesMode

pluginManagement {
repositories {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should use #15597 instead

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in fe527d1. Merged matrei's gradle-plugin-bootstrap branch from #15597 (preserving authorship through the merge commit) and removed the inline pluginManagement.repositories { ... } block I had here. All seven settings files (root, build-logic, buildSrc, grails-forge, grails-forge/buildSrc, grails-gradle, grails-gradle/buildSrc) now route plugin repos through the shared gradle/plugin-repositories.gradle script.

…nt repos

Adopt the shared gradle/plugin-repositories.gradle bootstrap script
introduced in #15597 (matrei) and remove the inline repositories blocks
previously added to settings.gradle, buildSrc/settings.gradle,
grails-forge/settings.gradle, grails-forge/buildSrc/settings.gradle,
grails-gradle/settings.gradle, and grails-gradle/buildSrc/settings.gradle.

All seven settings files now resolve plugin repositories through the same
configurePluginRepositories closure, eliminating the copy-paste drift
flagged in PR #15365 review (jdaugherty) and gaining the additional repos
PR #15365's inline blocks did not cover (mavenLocal env hook, sonatype
snapshots for cloud.wondrify.*, ASF staging for grails-publish releases
and org.apache.groovy:groovy.*).

PR #15597 targets 7.0.x; this merge brings the same fix to 8.0.x while
preserving the original commit (Mattias Reichel) so the bootstrap fix is
attributed correctly across both branches.

Closes gh-15596

Assisted-by: claude-code:claude-opus-4-7
Remove the four comments jdaugherty flagged as restating obvious build
mechanics:

* gradle.properties: '# CycloneDX Gradle plugin version' above
  gradleCycloneDxPluginVersion - the property name already states this.
* grails-bom/build.gradle (extractConstraints task): comment about
  capturing services at configuration time - the captureProjectServices
  call is self-explanatory once the team is on Gradle 9.
* GrailsGradlePlugin.createNative2AsciiTask: 'Capture ant builder at
  configuration time to avoid Task.project access at execution time'
  plus its config-cache doc URL - obvious once Gradle 9 is the baseline.
* GrailsProfileGradlePlugin.processProfileResources: 'Use Sync task type
  instead of project.sync in doLast to avoid Task.project access at
  execution time' plus its config-cache doc URL - same rationale.

No behavior change.

Assisted-by: claude-code:claude-opus-4-7
…anage it

Removes the explicit ':2.0.1' coordinate from the jakarta-inject declarations
in grails-forge-api/build.gradle (api) and grails-forge-cli/build.gradle
(implementation) and deletes jakartaInjectVersion=2.0.1 from
grails-forge/gradle.properties.

Both modules already import the micronaut-platform 4.10.10 BOM via
'platform("io.micronaut.platform:micronaut-platform:$micronautVersion")',
which manages jakarta.inject:jakarta.inject-api transitively through
io.micronaut:micronaut-core:4.10.18 (verified with gradle :grails-forge-api:
dependencies --configuration compileClasspath -> resolves to 2.0.1, matching
the previously hard-coded value).

Letting the BOM drive the version eliminates the version-mismatch concern
jdaugherty raised in PR #15365 review: future Micronaut platform bumps will
keep jakarta-inject in sync automatically rather than requiring a separate
property update here.

Compilation verified: ./gradlew :grails-forge-api:compileJava
:grails-forge-cli:compileJava (exit 0).

Assisted-by: claude-code:claude-opus-4-7
Removes the explicit ':$groovyVersion' coordinate from every forge build
script and deletes groovyVersion=4.0.31 from grails-forge/gradle.properties.
Also drops the eachDependency { details.useVersion(groovyVersion) }
resolutionStrategy override in grails-forge-api, grails-forge-cli, and
grails-forge-core (the spock-core override is preserved - that one is still
needed because the micronaut-platform BOM's spock version differs from ours).

Files touched:
* grails-forge/gradle.properties
* grails-forge/gradle/doc-config.gradle (4 documentation entries)
* grails-forge/test-core/build.gradle
* grails-forge/grails-forge-api/build.gradle
* grails-forge/grails-forge-cli/build.gradle
* grails-forge/grails-forge-core/build.gradle

The forge composite build already imports the micronaut-platform 4.10.10
BOM in every consumer module, which transitively manages org.apache.groovy
artifacts via groovy-bom. With the override removed, Gradle's conflict
resolution lets the BOM-managed version win for forge:

  org.apache.groovy:groovy-bom -> 4.0.28
  org.apache.groovy:groovy     -> 4.0.28
  org.apache.groovy:groovy-test-> 4.0.28

(Verified with gradle :grails-forge-api:dependencies --configuration
testRuntimeClasspath.) Forge now tracks the Groovy version Micronaut
4.10.10 was built against (4.0.28) instead of the artificially-pinned
4.0.31 that was being forced. grails-core itself stays on 4.0.31 - the
two builds are independent composite projects with their own platform
choices, exactly as jdaugherty noted in the PR #15365 review thread on
test-core/build.gradle line 62.

Verified: gradle :grails-forge-api:compileTestGroovy
:grails-forge-cli:compileTestGroovy :grails-forge-core:compileTestGroovy
:test-core:compileTestGroovy succeeds (exit 0).

Assisted-by: claude-code:claude-opus-4-7
Replaces the hard-coded '4.0.31' literal in dependencies.gradle with
GroovySystem.version, the static accessor for the Groovy version
currently loaded by the running Gradle JVM.

This 'gradle-groovy.version' entry feeds the grails-gradle-bom (the BOM
consumed by Grails Gradle plugin authors), where it controls which
groovy-bom platform is imported. Gradle plugin code is compiled against
and runs in Gradle's Groovy classpath, so pinning this to a value other
than what Gradle ships only adds drift risk - exactly the concern
jdaugherty raised in the PR #15365 review thread on dependencies.gradle
line 32.

With Gradle 9.4.1 the published gradle-groovy.version becomes 4.0.29
(matching gradle -v output), verified in
grails-gradle/bom/build/publications/maven/pom-default.xml after
running gradle :grails-gradle-bom:generatePomFileForMavenPublication.
The application-side groovy.version (4.0.31, used by grails-bom for
runtime Groovy) is unchanged.

Future Gradle wrapper bumps will flow through automatically without a
parallel constants update here.

Assisted-by: claude-code:claude-opus-4-7
Replaces the previous outputs.upToDateWhen { false } / mutate-in-place
implementation flagged by jdaugherty in PR #15365 review with a
properly-declared incremental task that Gradle can mark UP-TO-DATE
between builds.

Changes:
* Merge writes to a new build/merged-spring-configuration-metadata/
  directory instead of mutating build/classes/{java,groovy}/main.
* Inputs are declared as a lazy FileCollection that filters to existing
  files at execution time, so Gradle no longer fails the validation
  when one or both compilers are NO-SOURCE.
* outputs.cacheIf { true } enables Gradle build-cache reuse.
* The task explicitly dependsOn compileJava and compileGroovy so the
  compile output dirs always populate before the merge runs.
* The jar task uses an eachFile filter to drop the per-compiler copies
  but keep the merged copy, instead of a global exclude that would have
  also discarded the merged document.
* Removed the long block comment - the inline 7-line summary above the
  task is enough context for future readers.

Verified:
* gradle :grails-test-examples-plugins-issue-11767:clean :...:jar
  produces build/libs/...jar containing exactly one
  META-INF/spring-configuration-metadata.json with both
  PluginJavaMicronautBean (groups + properties) and
  PluginGroovyMicronautBean entries merged.
* A second jar invocation reports compileJava, compileGroovy,
  mergeMicronautConfigMetadata, and jar all UP-TO-DATE - the previous
  always-rerun behavior is gone.
* The downstream consumer (:grails-test-examples-issue-11767) compiles
  cleanly, confirming the merged metadata still satisfies its
  @ConfigurationProperties('my') consumer code.

Assisted-by: claude-code:claude-opus-4-7
…ride

Roll selenium back from 4.41.0 to 4.37.0 - the same version managed by
Spring Boot 4.0.5's spring-boot-dependencies BOM - and remove the
explicit OpenTelemetry 1.59.0 pin plus the 24-module constraint list
that the 4.41 bump previously required.

Background (jdaugherty review thread on dependencies.gradle line 95):
* selenium had been pinned at 4.38 in main due to a geb compatibility
  concern. The PR #15365 bump to 4.41 forced OpenTelemetry to 1.59+
  because Selenium 4.41+ depends on opentelemetry-* modules newer than
  the 1.55.x that Spring Boot 4.0.5 manages, which the dependency-version
  validator flagged.
* Spring Boot 4.0.5 BOM declares selenium 4.37 (verified via
  spring-boot-dependencies 4.0.5 dependencyManagement). Adopting the
  same version makes the OpenTelemetry override unnecessary because
  Selenium 4.37 still works with the 1.55.x line that Spring Boot
  manages.

Verified:
* gradle :grails-test-examples-scaffolding:validateDependencyVersions
  succeeds (resolved versions match BOM-declared versions across
  every project in the build).
* gradle validateDependencyVersions (whole-project) succeeds for all
  157 reporting tasks.
* gradle :grails-test-examples-geb:compileTestGroovy
  :grails-test-examples-geb-context-path:compileTestGroovy
  :grails-test-examples-geb-gebconfig:compileTestGroovy succeeds.
* The geb test apps now resolve org.seleniumhq.selenium:selenium-bom:4.37.0
  with no transitive opentelemetry conflict.

Net diff: -40 lines from dependencies.gradle. The selenium-version
constant moves to 4.37.0; the opentelemetry.version constant and the 24
opentelemetry-* module entries that propped up the 4.41 path go away.

Reviewer note: This is option (a) from the four selenium options
explored in the review thread. The 4.41 path is preserved on main if a
later bump is needed, but tracking Spring Boot's choice keeps the
override surface zero, which is what the validator wants.

Assisted-by: claude-code:claude-opus-4-7
@jamesfredley

Copy link
Copy Markdown
Contributor Author

Review feedback addressed (commits ccafe2b .. fe527d1)

Pushed 8 atomic commits resolving every line comment from the latest review. Net effect vs prior PR head: +153 / -241 across 20 files. All commits are independently revertable.

Commit ledger

Commit Concern
0196bd2dd0 (matrei, via merge) #15597 build: bootstrap Gradle plugin repositories - shared gradle/plugin-repositories.gradle script (preserves authorship)
fe527d1419 Merge of #15597 + drop the inline pluginManagement.repositories blocks I had added across 6 settings files. Also adopts matrei's refactor for build-logic/settings.gradle (deletes its 33-line inline block in favor of the shared script). All 7 settings files now route through the same configurePluginRepositories(delegate)
5d21fc3a39 Drop 4 redundant explanatory comments flagged by @jdaugherty (gradle.properties, grails-bom/build.gradle, GrailsGradlePlugin.groovy, GrailsProfileGradlePlugin.groovy)
dae431d4a1 Drop forge jakartaInjectVersion override - micronaut-platform 4.10.10 BOM manages it transitively (verified resolves to 2.0.1)
014355310b Drop forge groovyVersion override and the eachDependency { useVersion(groovyVersion) } resolutionStrategy block - BOM-managed (resolves to 4.0.28, the version Micronaut 4.10.10 was built against). spock-core override preserved
cc2f85fe07 gradle-groovy.version derived from GroovySystem.version instead of hard-coded '4.0.31'. Published BOM now contains <gradle-groovy.version>4.0.29</gradle-groovy.version> (Gradle 9.4.1's bundled Groovy)
d9a218175a Make issue-11767 mergeMicronautConfigMetadata properly cacheable - writes to separate build/merged-spring-configuration-metadata/ dir, declares lazy inputs/outputs, jar uses eachFile filter. Second invocation now reports UP-TO-DATE
ccafe2bd86 Roll selenium 4.41.0 → 4.37.0 (Spring Boot 4.0.5 BOM aligned) and drop the OpenTelemetry 1.59.0 override + 24-module constraint list. The override existed only to bridge the selenium-4.41 ↔ spring-boot-4.0.5 opentelemetry conflict; aligning with Spring Boot's choice eliminates it. Net diff in dependencies.gradle: -40 lines

Verification matrix

Check Result
./gradlew help (root + grails-gradle + grails-forge composites, JDK 21, Gradle 9.4.1) PASS
./gradlew validateDependencyVersions (whole project, 157 tasks) PASS
./gradlew :grails-bom:generatePomFileForMavenPublication PASS
./gradlew :grails-gradle-bom:generatePomFileForMavenPublication PASS (publishes gradle-groovy.version=4.0.29)
./gradlew :grails-forge-{api,cli,core,test-core}:compileTestGroovy PASS (groovy 4.0.28 from BOM)
./gradlew :grails-forge-api:dependencies --configuration compileClasspath jakarta-inject -> 2.0.1 via BOM
./gradlew :grails-test-examples-plugins-issue-11767:clean :...:jar PASS - jar contains 1 merged META-INF/spring-configuration-metadata.json with both Java + Groovy beans
Second invocation of :grails-test-examples-plugins-issue-11767:jar All UP-TO-DATE (caching works)
./gradlew :grails-test-examples-issue-11767:compileGroovy (consumer) PASS
./gradlew :grails-forge-analytics-postgres:assemble :...:dockerfile :...:dockerfileNative PASS (no shadow plugin needed - CI uses dockerBuildNative)
./gradlew :grails-test-examples-{geb,geb-context-path,geb-gebconfig}:compileTestGroovy PASS (Selenium 4.37)

Resolves all open review threads

  • 6× "use build: bootstrap Gradle plugin repositories #15597 instead" on settings files → fe527d1419
  • dependencies.gradle:32 "GroovySystem version instead of hard coding" → cc2f85fe07
  • dependencies.gradle:86 opentelemetry rationale → ccafe2bd86 (deleted, no longer needed)
  • dependencies.gradle:95 selenium pinning vs geb issues → ccafe2bd86 (rolled back to Spring Boot's 4.37)
  • gradle.properties:53 "Remove the comment" → 5d21fc3a39
  • grails-bom/build.gradle:110 "this is bridge, remove it" → 5d21fc3a39
  • grails-forge-analytics-postgres/build.gradle:21 shadow plugin removal → verified safe (dockerBuildNative is the deploy path)
  • grails-forge-api/build.gradle:52 jakarta-inject mismatch concern → dae431d4a1
  • test-core/build.gradle:62 followup on groovyVersion → 014355310b
  • GrailsGradlePlugin.groovy:981 "remove the comment" → 5d21fc3a39
  • GrailsProfileGradlePlugin.groovy:90 "remove the comment" → 5d21fc3a39
  • issue-11767/build.gradle:77 cacheable merge → d9a218175a

Ready for re-review @jdaugherty @matrei.

Assisted-by: claude-code:claude-opus-4-7

Reverts cc2f85f. `GroovySystem.version` returns Gradle's bundled
Groovy, which can lag the version grails-gradle code resolves
transitively via grails-core. The `validateDependencyVersions` task
in CI failed because the BOM advertised Gradle's bundled version while
the modules actually resolved grails-core's:

  org.apache.groovy:groovy           - resolved <core>, expected <gradle-bundled>
  org.apache.groovy:groovy-ant       - resolved <core>, expected <gradle-bundled>
  org.apache.groovy:groovy-groovydoc - resolved <core>, expected <gradle-bundled>
  org.apache.groovy:groovy-templates - resolved <core>, expected <gradle-bundled>
  ... etc

Going back to the hard-coded literal so the BOM matches what we
actually publish. Added an inline comment explaining why
`GroovySystem.version` is not viable here (without specific version
numbers, since those drift every Gradle/Groovy bump).

Verified: `./gradlew :grails-gradle-plugins:validateDependencyVersions
:grails-gradle-tasks:validateDependencyVersions` and
`./gradlew validateDependencyVersions` (whole project, 157 tasks)
both pass.

See #15365 (comment)
for the discussion thread.

Assisted-by: claude-code:claude-opus-4-7
Fixes the `Build Grails Forge` CI jobs that 0143553 broke. The
`documentation` configuration is registered by the buildsrc.groovydoc
plugin and is independent of compileClasspath / testRuntimeClasspath, so
the micronaut-platform BOM applied in each forge module's main
`dependencies { }` block does not reach it. After 0143553 dropped
the explicit `:$groovyVersion` from the documentation entries, the
groovy-* artifacts had no version source and groovydoc failed with
`Could not find org.apache.groovy:groovy-templates:.`

Apply the same BOM to the documentation configuration here so the
unversioned coordinates resolve through groovy-bom, matching the
approach used in the other forge configurations.

Verified: `./gradlew :grails-forge-cli:groovydoc :grails-forge-api:groovydoc
:grails-forge-core:groovydoc` succeeds.

Assisted-by: claude-code:claude-opus-4-7
@jamesfredley

Copy link
Copy Markdown
Contributor Author

CI fixes (commits 735ce32, 0314d5f)

Pushed two follow-up commits resolving the CI failures introduced by the previous batch.

Failures and fixes

Failing job(s) Root cause Fix Commit
Validate Dependency Versions GroovySystem.version returns Gradle's bundled Groovy (4.0.29 for Gradle 9.4.1), but grails-gradle plugin code transitively resolves 4.0.31 via grails-core. The validator flagged every org.apache.groovy:* artifact as resolved 4.0.31, expected 4.0.29 and failed. Revert gradle-groovy.version to hard-coded '4.0.31'. Added an inline comment (no specific version numbers - those drift) explaining why GroovySystem.version is not viable here. 735ce324df
Build Grails Forge (Java 21, indy=false), (Java 21, indy=true), (Java 25, indy=false) After 0143553 dropped :$groovyVersion from the documentation entries in grails-forge/gradle/doc-config.gradle, the unversioned org.apache.groovy:groovy-* artifacts had no version source. The documentation configuration is registered by the buildsrc.groovydoc plugin and is independent of compileClasspath / testRuntimeClasspath, so the micronaut-platform BOM applied in each forge module's main dependencies { } block does not reach it. groovydoc failed with Could not find org.apache.groovy:groovy-templates:. Apply platform("io.micronaut.platform:micronaut-platform:$micronautVersion") to the documentation configuration in doc-config.gradle so unversioned coordinates resolve through groovy-bom. Same BOM-driven approach we use elsewhere in forge. 0314d5fd96
Build Grails-Core (windows-latest, 25) Transient Maven Central 403 while resolving org.cyclonedx:cyclonedx-core-java:11.0.0: Received status code 403 from server: Forbidden from both repo.maven.apache.org/maven2 and plugins.gradle.org/m2. Not a code issue. None - should clear on retry. The same job passes on Linux runners and on JDK 21 Windows runs. n/a

Verification

Check Result
./gradlew :grails-gradle-plugins:validateDependencyVersions :grails-gradle-tasks:validateDependencyVersions PASS
./gradlew validateDependencyVersions (whole project, 157 tasks) PASS
./gradlew :grails-forge-cli:groovydoc :grails-forge-api:groovydoc :grails-forge-core:groovydoc PASS

Note on GroovySystem.version (re #15365 (comment 3141965549))

@jdaugherty - your original suggestion to derive gradle-groovy.version from GroovySystem.version was reasonable in principle but breaks validateDependencyVersions whenever Gradle's bundled Groovy lags grails-core's chosen Groovy. They're independently versioned and currently differ by one patch. I went back to the literal so the BOM matches what we actually publish/resolve. Discussion in #15365 (comment) - happy to revisit if you'd prefer a different formulation.

Assisted-by: claude-code:claude-opus-4-7

@jdaugherty

Copy link
Copy Markdown
Contributor

So we did say we would pick the newer versions, and since we have a mismatch the change makes sense. I will review the PR next

@jdaugherty jdaugherty left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks so much better, lets fix the jackson variable usage (use the bom instead) and then merge

Comment thread grails-forge/grails-forge-core/build.gradle Outdated
…latform manage it

Removes the explicit `:$jacksonDatabindVersion` coordinate flagged
by jdaugherty in PR #15365 review on grails-forge-core/build.gradle:51.
The micronaut-platform 4.10.10 BOM (already imported as a platform on
the line above) imports jackson-bom transitively, which manages
jackson-databind. Verified with `gradle :grails-forge-core:dependencies
--configuration compileClasspath` -> `com.fasterxml.jackson.core:
jackson-databind -> 2.19.2` (one minor newer than the previously
hard-coded 2.18.3).

The `jacksonDatabindVersion` property is left in
grails-forge/gradle.properties because grails-forge/buildSrc/build.gradle
still references it on its own gradle-plugin compile classpath, where
no BOM is imported.

Verified: `gradle :grails-forge-core:compileJava
:grails-forge-core:compileTestGroovy` passes.

Assisted-by: claude-code:claude-opus-4-7
@jdaugherty jdaugherty merged commit 8057f74 into 8.0.x Apr 26, 2026
60 of 61 checks passed
@github-project-automation github-project-automation Bot moved this from In Progress to Done in Apache Grails Apr 26, 2026
@jdaugherty jdaugherty deleted the upgrade/gradle-9.3.1 branch April 26, 2026 14:21
jamesfredley added a commit that referenced this pull request May 8, 2026
…ntime

After PR #15647 + #15648 fixed dockerBuildNative + Cloud Run startup
for the analytics service, both Forge deploy workflows finally
finished green and pushed `next.grails.org` (8.0.0-M1 hotfix branch)
and `prev-snapshot.grails.org` (8.0.x) revisions live. start.grails.org
still does not list 8.0.0-M1 / 8.0-SNAPSHOT in the Grails Version
selector, though, because both backends return:

  $ curl -i -H 'Origin: https://start.grails.org' https://next.grails.org/versions
  HTTP/1.1 500 Internal Server Error
  Content-Type: application/json
  ...
  {"_embedded":{"errors":[{"message":"Internal Server Error: Error
   encoding object [org.grails.forge.api.VersionDTO@..] to JSON: No
   serializable introspection present for type VersionDTO. Consider
   adding Serdeable.Serializable annotate to type VersionDTO."}]},
   "message":"Internal Server Error"}

Reproduced locally (`docker run grails-forge-local:web-netty` against
the GraalVM Community 21.0.2-ol9 native image with CORS_ALLOWED_ORIGIN
set, then `curl -H 'Origin: https://start.grails.org' /versions`) -
identical 500.

Root cause: PR #15365 review feedback (commit e1f3013,
"Address PR review feedback: telemetry override, forge BOM
adoption, publish plugin bump") swapped grails-forge-web-netty's
runtime JSON dependency:

  -    runtimeOnly 'io.micronaut:micronaut-jackson-databind'
  +    runtimeOnly 'io.micronaut.serde:micronaut-serde-jackson'

with the rationale that "the forge stack is a Micronaut 4 serde-
jackson application; the legacy jackson-databind runtime was a
copy-paste from an earlier generator template". That is not yet
true: every DTO returned by the Forge controllers
(VersionDTO, ApplicationTypeDTO, FeatureDTO, FeatureList,
SelectOptionsDTO, GormImplDTO, JdkVersionDTO, LanguageDTO,
ServletImplDTO, DevelopmentReloadingDTO, GitHubCreateDTO,
PreviewDTO, ApplicationTypeList, LinkDTO, the abstract Linkable
parent, ...) carries `@Introspected` only - none of them are
`@Serdeable` or registered via `@SerdeImport`. Micronaut Serde
correctly rejects @Introspected-only classes at runtime, while
Micronaut Jackson Databind serialises them via Jackson + bean
introspection. The mismatch turns every JSON endpoint behind
next.grails.org and prev-snapshot.grails.org into a 500, which
is why Forge UI's `versions` fetch is empty for those backends
and 8.0.0-M1 is missing from the start.grails.org Grails Version
selector.

The other Forge modules (forge-api tests, forge-core, analytics-
postgres) all still use micronaut-jackson-databind; web-netty
was the only inconsistency. Restore web-netty to
micronaut-jackson-databind so the runtime matches the rest of the
forge stack and the existing @introspected DTOs serialise. A
proper end-to-end Serde migration belongs in a separate PR that
also annotates every Forge DTO with @Serdeable / @SerdeImport.

Verified locally on Docker 29.3.0 with the same Java 21 GraalVM
Community 21.0.2-ol9 native-image container the deploy workflows
use:

  $ curl -i -H 'Origin: https://start.grails.org' http://localhost:18080/versions
  HTTP/1.1 200 OK
  Access-Control-Allow-Origin: https://start.grails.org
  Vary: Origin
  Access-Control-Allow-Credentials: true
  content-type: application/json
  ...
  {"_links":{"self":{"href":"http://.../versions","templated":false}},
   "versions":{...,"grails.version":"8.0.0-M1",...}}

  $ curl -i -H 'Origin: https://start.grails.org' http://localhost:18080/select-options
  HTTP/1.1 200 OK
  Access-Control-Allow-Origin: https://start.grails.org
  ...

Endpoints serve JSON cleanly; CORS allow-origin header is present;
the start.grails.org Grails Version selector will pick up 8.0.0-M1
once next.grails.org is redeployed from this commit.

Assisted-by: claude-code:claude-4.7-opus
jamesfredley added a commit that referenced this pull request May 8, 2026
…ntime

After PR #15647 + #15648 fixed dockerBuildNative + Cloud Run startup
for the analytics service, both Forge deploy workflows finally
finished green and pushed `next.grails.org` (8.0.0-M1 hotfix branch)
and `prev-snapshot.grails.org` (8.0.x) revisions live. start.grails.org
still does not list 8.0.0-M1 / 8.0-SNAPSHOT in the Grails Version
selector, though, because both backends return:

  $ curl -i -H 'Origin: https://start.grails.org' https://next.grails.org/versions
  HTTP/1.1 500 Internal Server Error
  Content-Type: application/json
  ...
  {"_embedded":{"errors":[{"message":"Internal Server Error: Error
   encoding object [org.grails.forge.api.VersionDTO@..] to JSON: No
   serializable introspection present for type VersionDTO. Consider
   adding Serdeable.Serializable annotate to type VersionDTO."}]},
   "message":"Internal Server Error"}

Reproduced locally (`docker run grails-forge-local:web-netty` against
the GraalVM Community 21.0.2-ol9 native image with CORS_ALLOWED_ORIGIN
set, then `curl -H 'Origin: https://start.grails.org' /versions`) -
identical 500.

Root cause: PR #15365 review feedback (commit e1f3013,
"Address PR review feedback: telemetry override, forge BOM
adoption, publish plugin bump") swapped grails-forge-web-netty's
runtime JSON dependency:

  -    runtimeOnly 'io.micronaut:micronaut-jackson-databind'
  +    runtimeOnly 'io.micronaut.serde:micronaut-serde-jackson'

with the rationale that "the forge stack is a Micronaut 4 serde-
jackson application; the legacy jackson-databind runtime was a
copy-paste from an earlier generator template". That is not yet
true: every DTO returned by the Forge controllers
(VersionDTO, ApplicationTypeDTO, FeatureDTO, FeatureList,
SelectOptionsDTO, GormImplDTO, JdkVersionDTO, LanguageDTO,
ServletImplDTO, DevelopmentReloadingDTO, GitHubCreateDTO,
PreviewDTO, ApplicationTypeList, LinkDTO, the abstract Linkable
parent, ...) carries `@Introspected` only - none of them are
`@Serdeable` or registered via `@SerdeImport`. Micronaut Serde
correctly rejects @Introspected-only classes at runtime, while
Micronaut Jackson Databind serialises them via Jackson + bean
introspection. The mismatch turns every JSON endpoint behind
next.grails.org and prev-snapshot.grails.org into a 500, which
is why Forge UI's `versions` fetch is empty for those backends
and 8.0.0-M1 is missing from the start.grails.org Grails Version
selector.

The other Forge modules (forge-api tests, forge-core, analytics-
postgres) all still use micronaut-jackson-databind; web-netty
was the only inconsistency. Restore web-netty to
micronaut-jackson-databind so the runtime matches the rest of the
forge stack and the existing @introspected DTOs serialise. A
proper end-to-end Serde migration belongs in a separate PR that
also annotates every Forge DTO with @Serdeable / @SerdeImport.

Verified locally on Docker 29.3.0 with the same Java 21 GraalVM
Community 21.0.2-ol9 native-image container the deploy workflows
use:

  $ curl -i -H 'Origin: https://start.grails.org' http://localhost:18080/versions
  HTTP/1.1 200 OK
  Access-Control-Allow-Origin: https://start.grails.org
  Vary: Origin
  Access-Control-Allow-Credentials: true
  content-type: application/json
  ...
  {"_links":{"self":{"href":"http://.../versions","templated":false}},
   "versions":{...,"grails.version":"8.0.0-M1",...}}

  $ curl -i -H 'Origin: https://start.grails.org' http://localhost:18080/select-options
  HTTP/1.1 200 OK
  Access-Control-Allow-Origin: https://start.grails.org
  ...

Endpoints serve JSON cleanly; CORS allow-origin header is present;
the start.grails.org Grails Version selector will pick up 8.0.0-M1
once next.grails.org is redeployed from this commit.

Assisted-by: claude-code:claude-4.7-opus
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Gradle 9 Update grails-forge to Micronaut 4.x.x or Migrate to Grails Application Grails 8.x.x: Gradle 9 support

5 participants