Repository: GradleUp/shadow
Pull request #1479 (2025-06-23 15:53:57)

Bump the min Gradle requirement to 8.11 and update test matrix
Refs #1480.

---

- [x] [CHANGELOG](https://github.com/GradleUp/shadow/blob/main/docs/changes/README.md)'s "Unreleased" section has been updated, if applicable.


Patch:
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index cab16e3f..790fbacb 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -9,11 +9,18 @@ on:
 
 jobs:
   build:
+    name: OS=${{ matrix.os }}, Java=${{ matrix.java }}, Gradle=${{ matrix.gradle }}
     strategy:
       matrix:
         os: [ ubuntu-latest, windows-latest ]
         # Always test on the latest version and some LTS.
         java: [ 17, 21, 24 ]
+        # Test on the minimum Gradle version and the latest.
+        gradle: [ 8.11, current ]
+        exclude:
+          # Gradle 8.11 doesn't support Java 24.
+          - gradle: 8.11
+            java: 24
     runs-on: ${{ matrix.os }}
     steps:
       - uses: actions/checkout@v4
@@ -22,7 +29,7 @@ jobs:
           distribution: 'zulu'
           java-version: ${{ matrix.java }}
       - uses: gradle/actions/setup-gradle@v4
-      - run: ./gradlew build
+      - run: ./gradlew build "-PtestGradleVersion=${{ matrix.gradle }}"
 
   # There's no need to run the update task frequently; the start scripts are typically updated with Gradle releases.
   update-start-scripts:
diff --git a/README.md b/README.md
index 86223fea..89e94bbd 100644
--- a/README.md
+++ b/README.md
@@ -34,7 +34,7 @@ Gradle plugin for creating fat/uber JARs with support for package relocation.
 | 7.0.0+         | 7.x                | 8                | [`com.github.johnrengelman.shadow`][johnrengelman's] |
 | 8.0.0+         | 8.0                | 8                | [`com.github.johnrengelman.shadow`][johnrengelman's] |
 | 8.3.0+         | 8.3                | 8                | [`com.gradleup.shadow`][gradleup's]                  |
-| 9.0.0+         | 8.3                | 11               | [`com.gradleup.shadow`][gradleup's]                  |
+| 9.0.0+         | 8.11               | 11               | [`com.gradleup.shadow`][gradleup's]                  |
 
 
 
diff --git a/build.gradle.kts b/build.gradle.kts
index 77b144c7..4eb0faa9 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -167,6 +167,12 @@ testing.suites {
     }
     targets.configureEach {
       testTask {
+        val testGradleVersion = providers.gradleProperty("testGradleVersion").orNull.let {
+          if (it == null || it == "current") GradleVersion.current().version else it
+        }
+        logger.info("Using test Gradle version: $testGradleVersion")
+        systemProperty("TEST_GRADLE_VERSION", testGradleVersion)
+
         maxParallelForks = Runtime.getRuntime().availableProcessors()
       }
     }
diff --git a/docs/README.md b/docs/README.md
index 1428492a..fead7c93 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -27,7 +27,7 @@ dependent libraries into the output jar without incurring the I/O overhead of ex
 | 7.0.0+         | 7.x                | 8                | [`com.github.johnrengelman.shadow`][johnrengelman's] |
 | 8.0.0+         | 8.0                | 8                | [`com.github.johnrengelman.shadow`][johnrengelman's] |
 | 8.3.0+         | 8.3                | 8                | [`com.gradleup.shadow`][gradleup's]                  |
-| 9.0.0+         | 8.3                | 11               | [`com.gradleup.shadow`][gradleup's]                  |
+| 9.0.0+         | 8.11               | 11               | [`com.gradleup.shadow`][gradleup's]                  |
 
 ## Benefits of Shadow
 
diff --git a/docs/changes/README.md b/docs/changes/README.md
index d1fc6133..b0205f02 100644
--- a/docs/changes/README.md
+++ b/docs/changes/README.md
@@ -4,6 +4,7 @@
 
 **Changed**
 
+- Bump the min Gradle requirement to 8.11. ([#1479](https://github.com/GradleUp/shadow/pull/1479))
 - Expose Ant as `compile` scope. ([#1488](https://github.com/GradleUp/shadow/pull/1488))
 
 ## [9.0.0-beta17](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta17) - 2025-06-18
diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt
index 77a6c09a..4b8066c9 100644
--- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt
+++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt
@@ -11,8 +11,8 @@ import org.gradle.util.GradleVersion
 public abstract class ShadowBasePlugin : Plugin<Project> {
 
   override fun apply(project: Project): Unit = with(project) {
-    if (GradleVersion.current() < GradleVersion.version("8.3")) {
-      throw GradleException("This version of Shadow supports Gradle 8.3+ only. Please upgrade.")
+    if (GradleVersion.current() < GradleVersion.version("8.11")) {
+      throw GradleException("This version of Shadow supports Gradle 8.11+ only. Please upgrade.")
     }
     @Suppress("DEPRECATION")
     extensions.create(EXTENSION_NAME, ShadowExtension::class.java, project)
diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt
index c77a9779..170668b2 100644
--- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt
+++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt
@@ -2,7 +2,6 @@ package com.github.jengelman.gradle.plugins.shadow.internal
 
 import org.gradle.api.Project
 import org.gradle.api.artifacts.Configuration
-import org.gradle.api.artifacts.ProjectDependency
 import org.gradle.api.distribution.DistributionContainer
 import org.gradle.api.file.ConfigurableFileCollection
 import org.gradle.api.model.ObjectFactory
@@ -18,7 +17,6 @@ import org.gradle.api.tasks.TaskContainer
 import org.gradle.api.tasks.TaskProvider
 import org.gradle.jvm.tasks.Jar
 import org.gradle.jvm.toolchain.JavaToolchainService
-import org.gradle.util.GradleVersion
 
 /**
  * Return `runtimeClasspath` or `runtime` configuration.
@@ -83,27 +81,9 @@ internal inline fun <reified V : Any> ObjectFactory.mapProperty(
   }
 }
 
-/**
- * TODO: this could be removed after bumping the min Gradle requirement to 8.8 or above.
- */
 internal inline fun ObjectFactory.fileCollection(
   path: () -> Any,
 ): ConfigurableFileCollection = fileCollection().apply {
   @Suppress("UnstableApiUsage")
-  if (GradleVersion.current() >= GradleVersion.version("8.8")) {
-    convention(path())
-  } else {
-    setFrom(path())
-  }
-}
-
-/**
- * TODO: this could be removed after bumping the min Gradle requirement to 8.11 or above.
- */
-internal fun ProjectDependency.dependencyProjectCompat(project: Project): Project {
-  return if (GradleVersion.current() >= GradleVersion.version("8.11")) {
-    project.project(path)
-  } else {
-    return this::class.java.getDeclaredMethod("getDependencyProject").invoke(this) as Project
-  }
+  convention(path())
 }
diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt
index f6dfc4ce..b82aaeda 100644
--- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt
+++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt
@@ -48,7 +48,7 @@ internal class UnusedTracker(
       apiDependencies.forEach { dep ->
         when (dep) {
           is ProjectDependency -> {
-            apiJars.addAll(getApiJarsFromProject(dep.dependencyProjectCompat(project)))
+            apiJars.addAll(getApiJarsFromProject(project.project(dep.path)))
             addJar(runtimeConfiguration, dep, apiJars)
           }
           is FileCollectionDependency -> {


Test patch:
diff --git a/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt
index 17130c57..df3d25ef 100644
--- a/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt
+++ b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt
@@ -65,6 +65,7 @@ sealed class SnippetExecutable : Executable {
 
     try {
       GradleRunner.create()
+        .withGradleVersion(testGradleVersion)
         .withProjectDir(projectRoot.toFile())
         .withPluginClasspath()
         .forwardOutput()
@@ -97,6 +98,9 @@ sealed class SnippetExecutable : Executable {
   }
 
   companion object {
+    private val testGradleVersion = System.getProperty("TEST_GRADLE_VERSION")
+      ?: error("TEST_GRADLE_VERSION system property is not set.")
+
     fun create(
       lang: DslLang,
       snippet: String,
diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt
index c3f3fb4e..f5908ae2 100644
--- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt
+++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt
@@ -379,6 +379,7 @@ abstract class BasePluginTest {
     arguments: Iterable<String> = emptyList(),
     projectDir: Path? = projectRoot,
   ): GradleRunner = GradleRunner.create()
+    .withGradleVersion(testGradleVersion)
     .forwardOutput()
     .withPluginClasspath()
     .withTestKitDir(testKitDir.toFile())
@@ -391,6 +392,9 @@ abstract class BasePluginTest {
 
   @Suppress("ConstPropertyName")
   companion object {
+    private val testGradleVersion = System.getProperty("TEST_GRADLE_VERSION")
+      ?: error("TEST_GRADLE_VERSION system property is not set.")
+
     val testKitDir: Path = run {
       var gradleUserHome = System.getenv("GRADLE_USER_HOME")
       if (gradleUserHome == null) {
diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt
index 2acbbeac..2b116ba0 100644
--- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt
+++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt
@@ -38,8 +38,6 @@ import org.gradle.api.plugins.JavaPlugin
 import org.gradle.api.tasks.bundling.Jar
 import org.gradle.testfixtures.ProjectBuilder
 import org.junit.jupiter.api.Test
-import org.junit.jupiter.api.condition.DisabledForJreRange
-import org.junit.jupiter.api.condition.JRE
 import org.junit.jupiter.params.ParameterizedTest
 import org.junit.jupiter.params.provider.ValueSource
 
@@ -92,35 +90,6 @@ class JavaPluginTest : BasePluginTest() {
     assertThat(shadowConfig.artifacts.files).contains(shadowTask.archiveFile.get().asFile)
   }
 
-  @Test
-  @DisabledForJreRange(
-    min = JRE.JAVA_21,
-    disabledReason = "Gradle 8.3 doesn't support Java 21.",
-  )
-  fun compatibleWithMinGradleVersion() {
-    val mainClassEntry = writeClass(withImports = true)
-    projectScriptPath.appendText(
-      """
-        dependencies {
-          implementation 'junit:junit:3.8.2'
-        }
-      """.trimIndent(),
-    )
-
-    run(shadowJarTask) {
-      it.withGradleVersion("8.3")
-    }
-
-    assertThat(outputShadowJar).useAll {
-      containsOnly(
-        "my/",
-        mainClassEntry,
-        *junitEntries,
-        *manifestEntries,
-      )
-    }
-  }
-
   @Test
   fun incompatibleWithLowerMinGradleVersion() {
     runWithFailure(shadowJarTask) {