Repository: GradleUp/shadow (kotlin)

Issue:
shadowJar task reports UP-TO-DATE with normal exclude.
Similar to the issue with #54, when I add an exclude option to the shadowJar block it doesn't update my JAR appropriately. The below code doesn't update my jar when the shaders folder is present on the root of it.

```
shadowJar {
    classifier = ''
    destinationDir = file("build/distributions")
    dependencies {
        include(dependency('org.obsidianbox.magma:magma:1.0.0-SNAPSHOT'))
        include(dependency('org.spout:caustic-api:1.0.0-SNAPSHOT'))
        include(dependency('org.spout:caustic-lwjgl:1.0.0-SNAPSHOT'))
    }
    exclude 'shaders/'
}
```

Pull request:
- Closes #62.
- Closes #717.

---

- [x] [CHANGELOG](https://github.com/GradleUp/shadow/blob/main/src/docs/changes/README.md)'s "Unreleased" section has been updated, if applicable.

Patch:
diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md
index d5f4afc97..a06b7b552 100644
--- a/src/docs/changes/README.md
+++ b/src/docs/changes/README.md
@@ -14,6 +14,7 @@
 **Fixed**
 
 - Support overriding `mainClass` provided by `JavaApplication`. ([#1182](https://github.com/GradleUp/shadow/pull/1182))
+- Fix `ShadowJar` not being successful after `includes` or `excludes` are changed. ([#1200](https://github.com/GradleUp/shadow/pull/1200))
 
 
 ## [v9.0.0-beta6] (2025-01-23)
diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt
index 0c00f4206..0b5672dbb 100644
--- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt
+++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt
@@ -60,6 +60,11 @@ public abstract class ShadowJar :
     duplicatesStrategy = DuplicatesStrategy.INCLUDE
     manifest = DefaultInheritManifest(services.get(FileResolver::class.java))
 
+    // Ensure includes and excludes are considered as inputs.
+    // TODO: workaround for https://github.com/gradle/gradle/blob/236bdeb129e4d16c100667b677d4315448bc9ede/subprojects/core-api/src/main/java/org/gradle/api/tasks/util/PatternFilterable.java#L70-L84.
+    inputs.property("includes", includes)
+    inputs.property("excludes", excludes)
+
     outputs.doNotCacheIf("Has one or more transforms or relocators that are not cacheable") {
       // TODO: this has been called but the cache is still working fine, need to investigate why.
       transformers.get().any { !isCacheableTransform(it::class.java) } ||


Test patch:
diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt
index 3af4c28ed..e22cafd94 100644
--- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt
+++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt
@@ -1,6 +1,7 @@
 package com.github.jengelman.gradle.plugins.shadow.caching
 
 import assertk.assertThat
+import com.github.jengelman.gradle.plugins.shadow.util.Issue
 import com.github.jengelman.gradle.plugins.shadow.util.containsEntries
 import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries
 import com.github.jengelman.gradle.plugins.shadow.util.isRegular
@@ -56,73 +57,99 @@ class ShadowJarCachingTest : BaseCachingTest() {
     assertThat(jarPath("build/libs/foo-1.0-all.jar")).isRegular()
   }
 
+  @Issue(
+    "https://github.com/GradleUp/shadow/issues/717",
+  )
   @Test
   fun shadowJarIsCachedCorrectlyWhenUsingIncludesExcludes() {
+    writeMainClass(className = "Main")
+    writeMainClass(className = "Main2")
     projectScriptPath.appendText(
       """
         dependencies {
-          implementation 'junit:junit:3.8.2'
-        }
-        $shadowJar {
-          exclude 'junit/*'
+          implementation 'shadow:a:1.0'
+          implementation 'shadow:b:1.0'
         }
-      """.trimIndent(),
+      """.trimIndent() + System.lineSeparator(),
     )
 
-    path("src/main/java/server/Server.java").writeText(
-      """
-        package server;
-        import junit.framework.Test;
-        public class Server {}
-      """.trimIndent(),
-    )
-    path("src/main/java/server/Util.java").writeText(
+    // First run successful with all files.
+    assertFirstExecutionSuccess()
+    assertThat(outputShadowJar).useAll {
+      containsEntries(
+        "shadow/Main.class",
+        "shadow/Main2.class",
+        "a.properties",
+        "a2.properties",
+        "b.properties",
+      )
+    }
+
+    projectScriptPath.appendText(
       """
-        package server;
-        import junit.framework.Test;
-        public class Util {}
-      """.trimIndent(),
+        $shadowJar {
+          exclude '**.properties'
+        }
+      """.trimIndent() + System.lineSeparator(),
     )
-
+    // Second run successful after excludes changed.
     assertFirstExecutionSuccess()
     assertThat(outputShadowJar).useAll {
       containsEntries(
-        "server/Server.class",
-        "server/Util.class",
+        "shadow/Main.class",
+        "shadow/Main2.class",
+      )
+      doesNotContainEntries(
+        "a.properties",
+        "a2.properties",
+        "b.properties",
       )
     }
 
-    val replaced = projectScriptPath.readText().lines().dropLast(3).joinToString(System.lineSeparator())
-    projectScriptPath.writeText(
+    projectScriptPath.appendText(
       """
-        $replaced
         $shadowJar {
-          include 'server/*'
-          exclude '*/Util.*'
+          include 'shadow/Main.class'
         }
-      """.trimIndent(),
+      """.trimIndent() + System.lineSeparator(),
     )
+    // Third run successful after includes changed.
     assertFirstExecutionSuccess()
     assertThat(outputShadowJar).useAll {
       containsEntries(
-        "server/Server.class",
+        "shadow/Main.class",
       )
       doesNotContainEntries(
-        "server/Util.class",
-        "junit/framework/Test.class",
+        "shadow/Main2.class",
+        "a.properties",
+        "a2.properties",
+        "b.properties",
       )
     }
 
-    assertExecutionsAreCachedAndUpToDate()
+    projectScriptPath.appendText(
+      """
+        $shadowJar {
+          include 'shadow/Main2.class'
+        }
+      """.trimIndent() + System.lineSeparator(),
+    )
+    // Forth run successful after includes changed again.
+    assertFirstExecutionSuccess()
     assertThat(outputShadowJar).useAll {
       containsEntries(
-        "server/Server.class",
+        "shadow/Main.class",
+        "shadow/Main2.class",
       )
       doesNotContainEntries(
-        "server/Util.class",
-        "junit/framework/Test.class",
+        "a.properties",
+        "a2.properties",
+        "b.properties",
       )
     }
+
+    // Clean and run 2 more times to ensure the states are cached and up-to-date.
+    assertExecutionsAreCachedAndUpToDate()
   }
 
   @Test