diff --git a/plugin-gradle/CHANGES.md b/plugin-gradle/CHANGES.md index a6492a83f4..dbd8c95491 100644 --- a/plugin-gradle/CHANGES.md +++ b/plugin-gradle/CHANGES.md @@ -4,6 +4,9 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ## [Unreleased] +### Fixed +- Prevent build caches from interfering when executing under the `-PspotlessIdeHook` mode. ([#2365](https://github.com/diffplug/spotless/issues/2365)) + ## [8.6.0] - 2026-05-27 ### Added - Add `cacheDirectory(...)` to `eclipse()`, `eclipseCdt()`, and `greclipse()`; the default P2 cache is `$GRADLE_USER_HOME/caches/p2-data`. ([#2944](https://github.com/diffplug/spotless/pull/2944)) diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessExtensionImpl.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessExtensionImpl.java index 173b5a1113..34dfa13fd4 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessExtensionImpl.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessExtensionImpl.java @@ -66,6 +66,13 @@ protected void createFormatTasks(String name, FormatExtension formatExtension) { task.getIdeHookState().set(ideHook); // clean removes the SpotlessCache, so we have to run after clean task.mustRunAfter(BasePlugin.CLEAN_TASK_NAME); + if (ideHook.paths != null) { + // The IDE hook never writes to the declared outputs so Gradle's UP-TO-DATE check would incorrectly + // skip the task when the user reverts a file or when useStdOut is active. + task.getOutputs().upToDateWhen(t -> false); + // also disable the build cache + task.getOutputs().cacheIf("IDE hook always reruns", t -> false); + } }); project.afterEvaluate(unused -> spotlessTask.configure(task -> { // now that the task is being configured, we execute our actions diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/IdeHookTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/IdeHookTest.java index da24a97d1a..bf481d52a4 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/IdeHookTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/IdeHookTest.java @@ -119,6 +119,33 @@ void dirty(boolean configurationCache) throws IOException { Assertions.assertThat(error).startsWith("IS DIRTY"); } + @ParameterizedTest + @MethodSource("configurationCacheProvider") + void dirtyCalledTwiceWithStdOut(boolean configurationCache) throws IOException { + // Make sure gradle doesn't cache the output of the first call, since it must be written to stdout again + runWith(configurationCache, "spotlessApply", "--quiet", "-PspotlessIdeHook=" + dirty.getAbsolutePath(), "-PspotlessIdeHookUseStdOut"); + Assertions.assertThat(output).isEqualTo("abc"); + Assertions.assertThat(error).startsWith("IS DIRTY"); + + runWith(configurationCache, "spotlessApply", "--quiet", "-PspotlessIdeHook=" + dirty.getAbsolutePath(), "-PspotlessIdeHookUseStdOut"); + Assertions.assertThat(output).isEqualTo("abc"); + Assertions.assertThat(error).startsWith("IS DIRTY"); + } + + @ParameterizedTest + @MethodSource("configurationCacheProvider") + void dirtyCalledTwiceAfterRevert(boolean configurationCache) throws IOException { + // Make sure Gradle doesn't skip writing to the file just because it was already dirty in the previous run + // - the content may have been reverted in between + runWith(configurationCache, "spotlessApply", "--quiet", "-PspotlessIdeHook=" + dirty.getAbsolutePath()); + Assertions.assertThat(error).startsWith("IS DIRTY"); + + Files.write("ABC".getBytes(StandardCharsets.UTF_8), dirty); + + runWith(configurationCache, "spotlessApply", "--quiet", "-PspotlessIdeHook=" + dirty.getAbsolutePath()); + Assertions.assertThat(error).startsWith("IS DIRTY"); + } + @ParameterizedTest @MethodSource("configurationCacheProvider") void clean(boolean configurationCache) throws IOException {