From b6ea81f8924524d468f9be2ea6041aaf2790e008 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Sat, 17 Oct 2026 21:08:24 +0200 Subject: [PATCH 1/2] Make the build compatible with Gradle 9 Gradle 9 no longer allows changing the usage of the `api` dependency scope, which broke the nightly wrapper update. Resolve `api` through a dedicated resolvable configuration instead, and replace other APIs removed in Gradle 9 (JavaExec.main via gradle-lombok's DelombokTask, ProjectDependency.dependencyProject, LenientConfiguration.getFiles, IdeaModule.testSourceDirs). Also disable the new failOnNoDiscoveredTests check for the testMatrix no-op executer. Co-Authored-By: Claude Opus 5.5 --- build.gradle | 6 +++++- core/build.gradle | 2 +- gradle/ci-support.gradle | 4 ++++ gradle/japicmp.gradle | 2 +- gradle/publishing.gradle | 2 +- gradle/shading.gradle | 11 +++++++---- 6 files changed, 19 insertions(+), 8 deletions(-) diff --git a/build.gradle b/build.gradle index f405ea2b92c..83072b8e20d 100644 --- a/build.gradle +++ b/build.gradle @@ -65,7 +65,11 @@ subprojects { } - task delombok(type: io.franzbecker.gradle.lombok.task.DelombokTask) { + // gradle-lombok's DelombokTask relies on JavaExec.main, which was removed in Gradle 9 + task delombok(type: JavaExec) { + mainClass = 'lombok.launch.Main' + classpath = configurations.compileClasspath + args 'delombok' outputs.cacheIf { true } diff --git a/core/build.gradle b/core/build.gradle index d7a54a26afe..07aed2296d9 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -8,7 +8,7 @@ sourceSets { test.maxParallelForks = 4 -idea.module.testSourceDirs += sourceSets.jarFileTest.allSource.srcDirs +idea.module.testSources.from(sourceSets.jarFileTest.allSource.srcDirs) jar { manifest { diff --git a/gradle/ci-support.gradle b/gradle/ci-support.gradle index 5b85837361e..0d919f9da4c 100644 --- a/gradle/ci-support.gradle +++ b/gradle/ci-support.gradle @@ -24,6 +24,10 @@ gradle.taskGraph.whenReady { for (subproject in subprojects) { subproject.tasks.withType(Test).all { testExecuter([execute: { spec, processor -> }, stopNow:{}] as org.gradle.api.internal.tasks.testing.TestExecuter) + // Gradle 9+ fails test tasks that discover no tests, which is always the case with the no-op executer above + if (GradleVersion.current() >= GradleVersion.version("9.0")) { + failOnNoDiscoveredTests = false + } } subproject.tasks.findByName("shadowJar")?.enabled = false subproject.tasks.findByName("javadoc")?.enabled = false diff --git a/gradle/japicmp.gradle b/gradle/japicmp.gradle index a7342c3ba2c..7cef0e8b8fa 100644 --- a/gradle/japicmp.gradle +++ b/gradle/japicmp.gradle @@ -13,7 +13,7 @@ tasks.japicmp { // Disable if baseline dependencies cannot be resolved - such as when developing a new module that doesn't // have an existing published version. - enabled = ! configurations.baseline.copy().resolvedConfiguration.lenientConfiguration.getFiles().empty + enabled = ! configurations.baseline.copy().incoming.artifactView { lenient = true }.files.empty oldClasspath.from(configurations.baseline) newClasspath.from(shadowJar.outputs.files) diff --git a/gradle/publishing.gradle b/gradle/publishing.gradle index 3e3faaf08ce..028ee8a7d55 100644 --- a/gradle/publishing.gradle +++ b/gradle/publishing.gradle @@ -58,7 +58,7 @@ publishing { def dependenciesNode = rootNode.appendNode('dependencies') - def apiDeps= project.configurations.api.resolvedConfiguration.firstLevelModuleDependencies + def apiDeps= project.configurations.resolvableApi.resolvedConfiguration.firstLevelModuleDependencies def providedDeps = project.configurations.provided.resolvedConfiguration.firstLevelModuleDependencies def newApiDeps = apiDeps - providedDeps diff --git a/gradle/shading.gradle b/gradle/shading.gradle index 88f25cc3f22..55f81593177 100644 --- a/gradle/shading.gradle +++ b/gradle/shading.gradle @@ -6,7 +6,10 @@ configurations { shaded [apiElements, implementation]*.extendsFrom shaded } -configurations.api.canBeResolved = true +// `api` is a dependency scope and cannot be resolved directly +configurations.resolvable('resolvableApi') { + extendsFrom(configurations.api) +} shadowJar { configurations = [project.configurations.shaded] @@ -16,7 +19,7 @@ shadowJar { } project.afterEvaluate { - Set apiDependencies = project.configurations.api.resolvedConfiguration.resolvedArtifacts*.moduleVersion*.id*.module + Set apiDependencies = project.configurations.resolvableApi.resolvedConfiguration.resolvedArtifacts*.moduleVersion*.id*.module // Exclude `api` dependencies, to prevent them being included into the final JAR shadowJar.dependencies { @@ -27,7 +30,7 @@ project.afterEvaluate { // Inherit dependencies' relocators shadowJar.relocators = configurations.api.dependencies.withType(ProjectDependency).collectMany { - return it.dependencyProject.tasks.findByName("shadowJar")?.relocators ?: [] + return project.project(it.path).tasks.findByName("shadowJar")?.relocators ?: [] } // See https://github.com/GradleUp/shadow/blob/5.0.0/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/ConfigureShadowRelocation.groovy @@ -64,7 +67,7 @@ project.afterEvaluate { } static Set findShadedProjects(Project project) { - Set dependencies = project.configurations.api.dependencies.withType(ProjectDependency)*.dependencyProject + Set dependencies = project.configurations.api.dependencies.withType(ProjectDependency).collect { project.project(it.path) } return dependencies + dependencies.collectMany { it -> findShadedProjects(it) } } From 93bcee7958c0ea053ea7ea3b18a4f5f189e0eda9 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Sat, 17 Oct 2026 21:08:47 +0200 Subject: [PATCH 2/2] Upgrade Spring Boot examples to Spring Boot 3.5 for Gradle 9 The Spring Boot 2.7 and Kotlin 1.8 Gradle plugins use APIs removed in Gradle 9. Co-Authored-By: Claude Opus 5.5 --- .../spring-boot-kotlin-redis/build.gradle.kts | 20 +++++++++---------- .../example/redis/AbstractIntegrationTest.kt | 4 ++-- examples/spring-boot/build.gradle | 4 ++-- .../src/main/java/com/example/DemoEntity.java | 9 ++++----- .../com/example/AbstractIntegrationTest.java | 4 ++-- 5 files changed, 20 insertions(+), 21 deletions(-) diff --git a/examples/spring-boot-kotlin-redis/build.gradle.kts b/examples/spring-boot-kotlin-redis/build.gradle.kts index 2d5d6bf9640..6cc50fc0326 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle.kts +++ b/examples/spring-boot-kotlin-redis/build.gradle.kts @@ -1,12 +1,12 @@ -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +import org.jetbrains.kotlin.gradle.dsl.JvmTarget plugins { - id("org.springframework.boot") version "2.7.18" - kotlin("jvm") version "1.8.22" - kotlin("plugin.spring") version "1.8.22" + id("org.springframework.boot") version "3.5.16" + kotlin("jvm") version "2.4.21" + kotlin("plugin.spring") version "2.4.21" } -java.sourceCompatibility = JavaVersion.VERSION_1_8 +java.sourceCompatibility = JavaVersion.VERSION_17 repositories { mavenCentral() @@ -21,13 +21,13 @@ dependencies { testImplementation("org.springframework.boot:spring-boot-starter-test") testImplementation("org.testcontainers:testcontainers") - testRuntimeOnly("org.junit.platform:junit-platform-launcher:1.8.2") + testRuntimeOnly("org.junit.platform:junit-platform-launcher") } -tasks.withType { - kotlinOptions { - freeCompilerArgs = listOf("-Xjsr305=strict") - jvmTarget = "1.8" +kotlin { + compilerOptions { + freeCompilerArgs.add("-Xjsr305=strict") + jvmTarget = JvmTarget.JVM_17 } } diff --git a/examples/spring-boot-kotlin-redis/src/test/kotlin/com/example/redis/AbstractIntegrationTest.kt b/examples/spring-boot-kotlin-redis/src/test/kotlin/com/example/redis/AbstractIntegrationTest.kt index 6df81f56262..312b6023d36 100644 --- a/examples/spring-boot-kotlin-redis/src/test/kotlin/com/example/redis/AbstractIntegrationTest.kt +++ b/examples/spring-boot-kotlin-redis/src/test/kotlin/com/example/redis/AbstractIntegrationTest.kt @@ -25,8 +25,8 @@ abstract class AbstractIntegrationTest { redisContainer.start() TestPropertyValues.of( - "spring.redis.host=${redisContainer.host}", - "spring.redis.port=${redisContainer.firstMappedPort}" + "spring.data.redis.host=${redisContainer.host}", + "spring.data.redis.port=${redisContainer.firstMappedPort}" ).applyTo(configurableApplicationContext.environment) } } diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index e3a40d27f06..3b4474a7705 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.7.18' + id 'org.springframework.boot' version '3.5.16' } apply plugin: 'io.spring.dependency-management' @@ -17,7 +17,7 @@ dependencies { runtimeOnly 'org.postgresql:postgresql' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.testcontainers:testcontainers-postgresql' - testRuntimeOnly "org.junit.platform:junit-platform-launcher:1.8.2" + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/examples/spring-boot/src/main/java/com/example/DemoEntity.java b/examples/spring-boot/src/main/java/com/example/DemoEntity.java index 71fceb2c8e8..c365f4f1424 100644 --- a/examples/spring-boot/src/main/java/com/example/DemoEntity.java +++ b/examples/spring-boot/src/main/java/com/example/DemoEntity.java @@ -1,12 +1,11 @@ package com.example; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; import lombok.Data; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; - @Data @Entity public class DemoEntity { diff --git a/examples/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java b/examples/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java index 806e63ed930..4fa49b8122e 100644 --- a/examples/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java +++ b/examples/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java @@ -22,7 +22,7 @@ abstract class AbstractIntegrationTest { @DynamicPropertySource static void redisProperties(DynamicPropertyRegistry registry) { redis.start(); - registry.add("spring.redis.host", redis::getHost); - registry.add("spring.redis.port", redis::getFirstMappedPort); + registry.add("spring.data.redis.host", redis::getHost); + registry.add("spring.data.redis.port", redis::getFirstMappedPort); } }