Upgrade to Gradle 9.4.1 and Micronaut 4#15365
Conversation
- 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)
|
@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. |
|
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.
There was a problem hiding this comment.
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-forgeto Micronaut 4 (Groovy 4, Spock 2.3) and migratejavax.*validation/transaction usage tojakarta.*. - 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.
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.
|
|
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.
|
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.
|
All the BOM and build-logic plugins are working correctly:
|
|
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. |
|
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)
3d4c6ea to
480ad17
Compare
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/
Add @DisableCachingByDefault to ApplicationContextCommandTask, ApplicationContextScriptTask, and GrailsRunTask to satisfy Gradle 9's requirement that all task types declare their caching intent. Assisted-by: Claude Code <Claude@Claude.ai>
286059d to
b5a833a
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
# Conflicts: # build-logic/plugins/src/main/groovy/org/apache/grails/buildsrc/SbomPlugin.groovy
This comment has been minimized.
This comment has been minimized.
matrei
left a comment
There was a problem hiding this comment.
Nice, I think we should merge this ASAP!
|
I would like to see the Spring Boot 4 PR merged first and would like to finish reviewing this |
There was a problem hiding this comment.
In addition to these comments, I have these 3 issues:
1. Grails Apps:
We use our gradle wrapper files to stage files into the forge app for it to share, this means that we will ship gradle 9 for end grails apps. I don't see any significant changes to support gradle 9 in the grails gradle plugins. Have you investigated this? Looking with claude thinks this:
Blockers (removed in Gradle 9)
| File:Line | Issue | Fix |
|---|---|---|
plugins/src/main/groovy/org/grails/gradle/plugin/core/GrailsExtension.groovy:26,95 |
import org.gradle.util.internal.ConfigureUtil + ConfigureUtil.configure(configureClosure, plugins) — class removed |
|
| in Gradle 9 | project.configure(plugins, configureClosure) |
|
plugins/src/main/groovy/org/grails/gradle/plugin/core/GrailsGradlePlugin.groovy:339 |
project.buildDir.mkdirs() inside doLast — Project.getBuildDir() removed |
|
project.layout.buildDirectory.get().asFile |
||
plugins/src/main/groovy/org/grails/gradle/plugin/core/IntegrationTestGradlePlugin.groovy:122-124 |
project.files("$project.buildDir/test-results/...") (×2) |
|
project.layout.buildDirectory.dir("test-results/...") providers |
High — Configuration Cache / Project Isolation Violations
Gradle 9 enforces stricter project isolation; calling project.* from task actions fails.
| File:Line | Issue | Fix |
|---|---|---|
plugins/src/main/groovy/org/grails/gradle/plugin/profiles/GrailsProfileGradlePlugin.groovy:110-131 |
task.doLast { project.sync { ... } } |
Convert processProfileResources to a typed Sync task |
plugins/src/main/groovy/org/grails/gradle/plugin/core/GrailsGradlePlugin.groovy:328-346 |
buildProperties task uses tasks.create + doLast { ant.mkdir / ant.propertyfile } referencing outer |
|
project.ant |
Convert to a typed WriteProperties task |
Medium — Eager Task APIs
Still works in Gradle 9 but discouraged and configuration-cache-hostile.
plugins/src/main/groovy/org/grails/gradle/plugin/core/GrailsGradlePlugin.groovy:328—project.tasks.create('buildProperties')→tasks.registerplugins/src/main/groovy/org/grails/gradle/plugin/publishing/GrailsPluginGradlePlugin.groovy:249—project.task(type: Copy, 'copyCommands')→tasks.register('copyCommands', Copy)plugins/src/main/groovy/org/grails/gradle/plugin/publishing/GrailsPluginGradlePlugin.groovy:254—project.task(type: Copy, 'copyTemplates')→tasks.register('copyTemplates', Copy)
2. Configuration Cache Issues
There are known configuration cache issues with our project structure (see ticket we opened after discussing with the gradle team). Shouldn't we disable the configuration cache until we can prove it works?
3. Extract Bom Dependencies
The ExtractDependenciesTask I think is broken on Gradle 9. Below is the output from Claude on this, but I think the BuildWorker solution is the best solution. It may be working if we disable the configuration cache so we may be able to punt this work initially:
File: build-logic/docs-core/src/main/groovy/org/apache/grails/gradle/tasks/bom/ExtractDependenciesTask.groovy
Hard Violations
1. project access from doFirst (lines 91-97)
ExtractDependenciesTask() {
doFirst {
if (!project.pluginManager.hasPlugin('java-platform')) { ... }
}
}Task actions cannot reference project under configuration cache. Move this validation to configuration time (the plugin that registers the task), or capture the boolean into an @Internal Property<Boolean>
provider at config time.
2. project.configurations accessed in @TaskAction (line 118)
Configuration configuration = project.configurations.named(configurationName.get()).get() Forbidden in Gradle 9. The task must not look up configurations at execution time. The Configuration (or its resolved result) has to be wired in as a Property / file collection at configuration time.
3. Configuration resolved inside the task action (lines 123, 125, 126, 153, 172, 185)
configuration.getAllDependencyConstraints().all { ... }
configuration.allDependencies
configuration.incoming.resolutionResult.allDependenciesTriggering resolution from inside @TaskAction is incompatible with configuration cache. The ResolutionResult graph must be exposed as a Property<ResolvedComponentResult> (via
configuration.incoming.resolutionResult.rootComponent — a Provider<ResolvedComponentResult>) and wired into the task at configuration time. Same for dependency constraints — they need to be captured into
typed inputs (e.g. a ListProperty<...> or serializable holder) at config time.
4. Detached configurations created and resolved at execution time (lines 210-213)
Properties populatePlatformDependencies(...) {
Dependency bomDependency = project.dependencies.create("${bomCoordinates.coordinates}@pom")
Configuration dependencyConfiguration = project.configurations.detachedConfiguration(bomDependency)
File bomPomFile = dependencyConfiguration.singleFile
...
} Three separate Gradle 9 violations in one block:
project.dependencies.create(...)from a task actionproject.configurations.detachedConfiguration(...)from a task action- Dependency resolution (
.singleFile) at execution time
This is the deepest problem because the method is recursive — each parent BOM and each import-scoped BOM creates yet another detached configuration on the fly. There is no way to enumerate the BOM POMs
up front from the task body alone.
Possible Redesign
Two options, in order of preference:
Option A — Pre-resolve everything at configuration time
In the plugin that registers ExtractDependenciesTask:
- Take the source
Configurationand callincoming.resolutionResult.rootComponent— aProvider<ResolvedComponentResult>— and bind it to a new@Internal Property<ResolvedComponentResult>on the task.
Walk the graph in the task action against that provider (noprojectaccess). - For BOM POMs, build a sibling
Configurationwhose dependencies are the same set of BOM coordinates with@pomartifact type, plustransitive = false, then useArtifactViewto expose its files as a
ConfigurableFileCollectioninput on the task. The task body parses the POMs from that file set instead of creating detached configurations. - Because parent/imported BOMs are only discoverable after parsing the first level of POMs, you may need to either:
- run BOM resolution in two passes at configuration time (resolve top-level BOMs, parse them eagerly to discover transitive BOM coordinates, then add them to the input file collection), or
- use the
ArtifactResolutionQueryAPI (still supported in Gradle 9) executed at config time inside a doFirst-equivalent that usesBuildServices rather thanproject.
Option B — Use a BuildService + worker API
Move the dependency resolution into a BuildService (or a worker action) that holds a reference to a DependencyResolutionServices / ArtifactResolutionQuery. Build services can be invoked from task actions
safely under Gradle 9, although the recursive POM-walking still wants the POMs declared as inputs for cache correctness.
Other Smaller Issues in the Same File
setConfiguration(NamedDomainObjectProvider<Configuration> config)(lines 86-89) — fine at registration time, but the storedconfigurationNameis then used to re-look up the configuration at execution
time (issue #2). After redesign, this setter should wiredependencyArtifacts.from(config)and the resolved-result provider, and theconfigurationNameProperty can stay only as an@Inputfor cache key
purposes.- The task is
@CacheableTaskbut cannot actually be cached correctly today because much of its real input (the transitively discovered BOM POMs) is not declared as an input. After redesign this annotation
will be honest.
Summary
ExtractDependenciesTask is not Gradle 9 compatible and is likely the single biggest item on a Gradle 9 migration for this repo. The task action currently:
- Looks up
project.configurationsat execution time - Triggers full dependency-graph resolution at execution time
- Recursively creates and resolves detached configurations at execution time via
project.dependencies/project.configurations - References
projectfrom adoFirstblock
All four patterns are removed/forbidden in Gradle 9 with the configuration cache enforced. The fix requires moving dependency resolution out of the @TaskAction body — either by pre-resolving at configuration
time and exposing Provider<ResolvedComponentResult> + ConfigurableFileCollection inputs, or by routing resolution through a BuildService.
| @@ -2,5 +2,5 @@ | |||
| java=17.0.18-librca | |||
| # Keep gradle version synced with gradle.properties (gradleToolingApiVersion), all gradle-wrapper.properties files, | |||
There was a problem hiding this comment.
We should update this comment, you just have to update the bootstrap project and it will copy these to the right locations, only the api update is needed.
There was a problem hiding this comment.
Fixed. Updated the comment to reference the gradle-bootstrap project for propagating the version to all wrapper files.
| api platform("org.codehaus.groovy:groovy-bom:${GroovySystem.version}") | ||
| // Use Gradle's embedded Groovy version for the grails-gradle-bom | ||
| // Groovy 4+ uses org.apache.groovy coordinates | ||
| api platform("org.apache.groovy:groovy-bom:${GroovySystem.version}") |
There was a problem hiding this comment.
We are setting it to the gradle groovy version in some places and then the grails-gradle-bom in others. I'd suggest we extract the groovy version into the gradle bom map in dependencies.gradle then reference that variable here so we're consistent on the same version.
There was a problem hiding this comment.
Fixed. Extracted the Groovy version into \gradleBomDependencyVersions\ as \gradle-groovy.version: 4.0.31\ and added a \gradle-groovy-bom\ entry to \gradleBomPlatformDependencies. The bom/build.gradle now references \gradleBomPlatformDependencies['gradle-groovy-bom']\ instead of \GroovySystem.version.
| from rootProject.layout.projectDirectory.file('gradle/grails-extension-gradle-config.gradle') | ||
| } | ||
|
|
||
| // Both compileJava and compileGroovy run the Micronaut annotation processor, each generating |
There was a problem hiding this comment.
Isn't compileJava supposed to be disabled? We don't even have java code in this test. This seems like something else is wrong.
There was a problem hiding this comment.
Fixed. Replaced the JSON metadata merge hack with \compileJava.enabled = false\ since there's no Java source code in this test plugin.
| @@ -54,15 +54,15 @@ gradleChecksumPluginVersion=1.4.0 | |||
| gradleCycloneDxPluginVersion=2.4.1 | |||
There was a problem hiding this comment.
2.4.1 does not officially support gradle 9.x. 3.0.0 is a requirement to upgrade to Gradle 9.
There was a problem hiding this comment.
Fixed. Upgraded CycloneDX from 2.4.1 to 3.0.0. Rewrote SbomPlugin to use the new \CyclonedxDirectTask\ API with property-based configuration, and updated task name references from \cyclonedxBom\ to \cyclonedxDirectBom.
| def projectName = project.name | ||
| def projectPath = project.path | ||
| boolean isReproducibleBuild = lookupProperty(project, 'isReproducibleBuild') | ||
| ZonedDateTime buildDate = lookupProperty(project, 'buildDate') |
There was a problem hiding this comment.
This should be a provider that's still looked up at execution time.
There was a problem hiding this comment.
Fixed. Converted \isReproducibleBuild\ to a \Provider\ that is evaluated lazily at execution time via \project.provider { lookupProperty(project, 'isReproducibleBuild') as boolean }.
| // See: https://docs.gradle.org/current/userguide/configuration_cache.html#config_cache:requirements:use_project_during_execution | ||
| def projectName = project.name | ||
| def projectPath = project.path | ||
| boolean isReproducibleBuild = lookupProperty(project, 'isReproducibleBuild') |
There was a problem hiding this comment.
This should be a provider, that's still looked up at execution time.
There was a problem hiding this comment.
Fixed. Converted \�uildDate\ to a \Provider\ that is evaluated lazily at execution time via \project.provider { lookupProperty(project, 'buildDate') as ZonedDateTime }.
|
Addressed all review comments and the three big issues from the review body: Inline comments (all resolved):
Big Issue 1 - Gradle 9 blockers in grails-gradle plugins:
Big Issue 2 - Configuration cache:
Big Issue 3 - ExtractDependenciesTask:
|
- Upgrade CycloneDX plugin from 2.4.1 to 3.0.0 and rewrite SbomPlugin for CyclonedxDirectTask API - Replace ConfigureUtil (removed in Gradle 9) with ClosureBackedAction in GrailsExtension - Migrate eager task creation to tasks.register() in GrailsPluginGradlePlugin and GrailsGradlePlugin - Remove Task.project access from ExtractDependenciesTask execution time via captureProjectServices() - Convert SbomPlugin isReproducibleBuild and buildDate to lazy providers - Restore skipWhenEmpty inputs in GrailsProfileGradlePlugin Sync task - Extract Groovy version to gradleBomDependencyVersions for consistency - Disable compileJava in issue-11767 test plugin (no Java source) - Explicitly disable configuration cache pending #15497 - Add junit-platform-launcher version, update .sdkmanrc comment Assisted-by: Claude Code <Claude@Claude.ai>
Replace ClosureBackedAction (removed in Gradle 9) with plain Groovy closure delegation in GrailsExtension, fixing the compileGroovy failure across all CI jobs. Bump patch-level dependencies: - byte-buddy 1.17.8 -> 1.18.8 - commons-text 1.14.0 -> 1.15.0 - ant 1.10.15 -> 1.10.17 - commons-codec 1.19.0 -> 1.21.0 - selenium-bom 4.38.0 -> 4.41.0 - mockito-core 5.20.0 -> 5.23.0 Assisted-by: Claude Code <Claude@Claude.ai>
|
CI Fix + Dependency Updates All CI jobs were failing on a single compilation error: Also bumped patch-level dependencies:
Skipped (too risky for this PR):
|
Comprehensive Summary of All ChangesEvery item from @jdaugherty's review has been addressed across two commits ( Inline Review Comments (9/9 resolved)
Big Issue 1 - Gradle 9 Blockers in grails-gradle Plugins (all fixed)Blockers (removed in Gradle 9):
Config Cache / Project Isolation:
Eager Task APIs:
Big Issue 2 - Configuration Cache (fixed)Explicitly set Big Issue 3 - ExtractDependenciesTask (fixed for current scope)
Note: The task still creates detached configurations at execution time, which is incompatible with configuration cache. Since configuration cache is explicitly disabled, this works correctly. A full redesign (BuildService or pre-resolve approach per the review) is deferred until configuration cache is re-enabled. CI FixAll CI jobs were failing on Dependency Bumps
Remaining / Deferred
|
…rtiesTask The parent class GrailsGradlePlugin changed createBuildPropertiesTask from returning Task to void when migrating to tasks.register(). Update the no-op override in GrailsPluginGradlePlugin to match. Assisted-by: Claude Code <Claude@Claude.ai>
…iolation Gradle 9 removed DependencyHandler.project(String). The PluginDefiner invokeMethod now converts project(String) to project(Map) form before delegating to the target DependencyHandler. Also fix consecutive blank lines codenarc violation in GrailsExtension. Assisted-by: Claude Code <Claude@Claude.ai>
|
@jamesfredley Seems to be a compile failure. |
|
|
||
| ExtractDependenciesTask() { | ||
| doFirst { | ||
| if (!project.pluginManager.hasPlugin('java-platform')) { |
There was a problem hiding this comment.
I agree this is the right short term solution, but this task will ship publicly again. I was speaking to Vampire in Gradle chat, eventually I'll probably add this is as it's own gradle plugin where the doFirst { } throws an error. Then the plugin removes the action so it passes. Something like this (from Vampire's chat):
val foo = Action<Task> {
error("FOO")
}
tasks.help {
doFirst("foo", foo)
}
pluginManager.withPlugin("java-platform") {
tasks.help {
actions.remove(foo)
}
}
But before doing any of this, did you see if you can store a reference to the pluginManager and still have this validation? Is that allowed?
There was a problem hiding this comment.
AI seems to agree:
public abstract class MyTask extends DefaultTask {
@Input
public abstract Property<Boolean> getRequiredPluginApplied();
@TaskAction
public void run() {
if (!getRequiredPluginApplied().get()) {
throw new GradleException(
"Task '" + getPath() + "' requires plugin 'com.example.required' to be applied.");
}
// real task work
}
}
public class MyPlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
TaskProvider<MyTask> myTask = project.getTasks().register("myTask", MyTask.class, task -> {
task.getRequiredPluginApplied().convention(false);
});
project.getPluginManager().withPlugin("com.example.required", applied ->
myTask.configure(task -> task.getRequiredPluginApplied().set(true))
);
}
}
| task.doLast { | ||
| Map<String, String> artifacts = [:] | ||
| project.extensions.getByType(PublishingExtension).publications.withType(MavenPublication).each { MavenPublication publication -> | ||
| publishingExtension.publications.withType(MavenPublication).each { MavenPublication publication -> |
There was a problem hiding this comment.
We use the publish plugin and it definitely needs updated to support Gradle 9, but I don't see any PR's on https://github.com/apache/grails-gradle-publish
Shouldn't we update this plugin prior to merging this PR since it's also incompatible with 9?
|
|
||
| private static void configureSbomTask(Project project, Provider<RegularFile> sbomOutputLocation) { | ||
| project.tasks.withType(CycloneDxTask).configureEach { CycloneDxTask task -> | ||
| project.tasks.withType(CyclonedxDirectTask).configureEach { CyclonedxDirectTask task -> |
There was a problem hiding this comment.
Have you compared the new vs old task output and confirmed our doLast hack still works?
| # Note: we do not import the micronaut bom in our tests to avoid spring version mismatches | ||
| micronautHttpClientVersion=4.9.9 | ||
| micronautSerdeJacksonVersion=2.11.0 | ||
| micronautHttpClientVersion=4.10.18 |
There was a problem hiding this comment.
Can we just update the test example that uses these 2 micronaut values to use the micronaut bom & not refer to specific versions? Mattias already removed all other usages other than the test app for micronaut.
| tasks.register('extractConstraints', ExtractDependenciesTask).configure { ExtractDependenciesTask it -> | ||
| // Capture project services at configuration time so the task avoids the deprecated Task.project at execution time | ||
| it.captureProjectServices(project.dependencies, project.configurations) | ||
| if (!project.pluginManager.hasPlugin('java-platform')) { |
There was a problem hiding this comment.
The only reason this existed before was in case external parties used our task. Let's remove it from here.
| annotationProcessor platform("io.micronaut:micronaut-bom:$micronautVersion") | ||
| implementation platform("io.micronaut:micronaut-bom:$micronautVersion") | ||
| annotationProcessor platform("io.micronaut.platform:micronaut-platform:$micronautVersion") | ||
| implementation platform("io.micronaut.platform:micronaut-platform:$micronautVersion") |
There was a problem hiding this comment.
Can't we adopt the micronaut bom to set the versions below?
| dependencies { | ||
| annotationProcessor platform("io.micronaut:micronaut-bom:$micronautVersion") | ||
| implementation platform("io.micronaut:micronaut-bom:$micronautVersion") | ||
| annotationProcessor platform("io.micronaut.platform:micronaut-platform:$micronautVersion") |
There was a problem hiding this comment.
Can't we adopt the micronaut bom to set the versions below?
| implementation 'io.micronaut.gcp:micronaut-gcp-http-client' | ||
|
|
||
| runtimeOnly 'ch.qos.logback:logback-classic' | ||
| runtimeOnly 'io.micronaut:micronaut-jackson-databind' |
There was a problem hiding this comment.
I thought we used serde instead of jackson?
| } | ||
|
|
||
| testImplementation "org.codehaus.groovy:groovy:$groovyVersion" | ||
| testImplementation "org.apache.groovy:groovy:$groovyVersion" |
There was a problem hiding this comment.
Can't we use the micronaut bom?
| } | ||
| processResources.setDuplicatesStrategy(DuplicatesStrategy.INCLUDE) | ||
| processResources.dependsOn(*processResourcesDependencies) | ||
| processResources.dependsOn(copyCommands, copyTemplates) |
There was a problem hiding this comment.
Was this an intentional change or an AI changing the style?
…sion The build_grails and Build Grails-Core jobs were failing because a recent attempt to work around the duplicate META-INF/spring-configuration-metadata.json in issue-11767 disabled compileJava outright. That broke the consumer app (grails-test-examples/issue-11767/AppController.groovy) which imports issue11767.plugin.PluginJavaMicronautBean - a real Java class - so compileGroovy could no longer resolve the type. Root-cause fix for the metadata duplicate: both the micronaut-inject-java annotation processor (compileJava) and the micronaut-inject-groovy AST transform (compileGroovy, transitively via grails-micronaut) correctly emit non-overlapping metadata describing the beans visible to each compiler. The duplicate is intentional for this test plugin because it exercises Java + Groovy beans sharing the @ConfigurationProperties('my') prefix. Rather than silently dropping one document (DuplicatesStrategy.EXCLUDE) or disabling a compile step, a new mergeMicronautConfigMetadata task combines the two JSON files into a single document (groups + properties + hints) and removes the now-merged source so the jar sees exactly one file. The shared CompilePlugin convention's jar.duplicatesStrategy = FAIL stays in effect for other modules. Also fix the Validate Dependency Versions check: the Selenium 4.41.0 upgrade transitively requires io.opentelemetry:* 1.59.x, but Spring Boot 4.0.5's BOM manages 1.55.x, so the validator flagged the mismatch in grails-test-examples-scaffolding. Import io.opentelemetry:opentelemetry-bom:1.59.0 from grails-bom (after spring-boot-bom) so the grails-bom advertises the version that is actually resolved. Verified locally with JDK 21: - ./gradlew :grails-test-examples-plugins-issue-11767:jar produces a jar with a single merged META-INF/spring-configuration-metadata.json containing both PluginJavaMicronautBean and PluginGroovyMicronautBean entries. - ./gradlew :grails-test-examples-issue-11767:compileGroovy succeeds (the task that failed in CI). - ./gradlew validateDependencyVersions succeeds for every project. Assisted-by: Claude Code <Claude@Claude.ai>
CI Fix Pushed (237527b)Two root-cause fixes for the failing checks: 1.
|
✅ All tests passed ✅🏷️ Commit: 237527b Learn more about TestLens at testlens.app. |
|
@jamesfredley on the telemetry version, why not just override the version in our bom and bump it? I don't think there is a guarantee that ordering of the guarantees dependency order - I had issues with this on the hibernate PR. |
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
conventionwithextensionsAPIConfigureUtilwithClosureBackedActionJavaPluginConventionwithJavaPluginExtensionoutputFilewithdestinationFileinWriteProperties@DisableCachingByDefaulttoApplicationContextCommandTask,ApplicationContextScriptTask, andGrailsRunTaskto satisfy Gradle 9's requirement that all task types declare their caching intent.sdkmanrcto reference Gradle 9.4.1gradle-wrapper.properties(7),.sdkmanrcconventionto extensionsConfigureUtiltoClosureBackedActionJavaPluginConventiontoJavaPluginExtensionoutputFiletodestinationFile@DisableCachingByDefaultspring-configuration-metadata.jsonconflict in issue-11767 test plugin (both Java and Groovy annotation processors generate this file)grails-test-examples/plugins/issue-11767/build.gradleDevelocity Plugin Update
com.gradle.develocityfrom 4.1.1 to 4.3.2 (latest compatible with Develocity 2025.4.2)com.gradle.common-custom-user-data-gradle-pluginfrom 2.3 to 2.4.0grails-data-neo4jandgrails-data-graphqlfrom deprecatedgradle-enterpriseplugin todevelocitygrails-data-neo4jandgrails-data-graphqlsettingsTask.project Deprecation Fixes
Fix deprecated
Task.projectaccess at execution time (scheduled for removal in Gradle 10) by capturing project properties at configuration time:projectName,projectPath, andbuildDateat configuration time; refactorpickLicense()to accept these values instead of task referencePublishingExtensionat configuration time beforedoLastblockbuildDirat configuration time forbuildPropertiestask; captureantBuilderat configuration time fornative2asciitaskproject.syncindoLastwith properSynctask typeMicronaut 4 Upgrade (grails-forge)
javax.*tojakarta.*validation annotations:javax.validation.constraints.*->jakarta.validation.constraints.*javax.transaction.*->jakarta.transaction.*org.codehaus.groovy->org.apache.groovyio.micronaut:micronaut-bom->io.micronaut.platform:micronaut-platformjavax.inject:javax.inject->jakarta.inject:jakarta.inject-apimicronaut-validation-processorfor annotation processingmicronaut-jackson-databindruntime dependency@Validatedannotation to controllers using validationgradle.properties, all forgebuild.gradlefilesgradle.propertiesjavaxtojakarta(grails-forge)javax.validation.*tojakarta.validation.*;javax.transaction.*tojakarta.transaction.*org.codehaus.groovytoorg.apache.groovy;io.micronaut:micronaut-bomtoio.micronaut.platform:micronaut-platformMicronaut Platform Upgrade (root project)
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-opens java.base/java.lang=ALL-UNNAMEDJVM arg for test executionTesting
Application Testing
Plugin Output Verification
Verified output before/after Task.project deprecation fixes:
cyclonedxBomsavePublishedArtifactsprocessProfileResourcesbuildPropertiesdocsref/Versions/Grails BOM.htmlverified)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):
StartParameter.isConfigurationCacheRequestedorg.asciidoctor.jvm.convertplugin 4.0.5Open Decisions
Breaking Changes
None expected for end users. This is an internal build infrastructure upgrade.
Related Issues