From ca5c0673d89056e0dfc56b562b32a17207e81467 Mon Sep 17 00:00:00 2001 From: Simon Le Bras Date: Thu, 13 Nov 2025 15:28:11 +0100 Subject: [PATCH 1/5] Use Provider API for Gradle properties --- CHANGES.md | 3 +- .../gradle/spotless/FormatExtension.java | 7 ++-- .../com/diffplug/gradle/spotless/IdeHook.java | 34 ++++++++++--------- .../gradle/spotless/SpotlessPlugin.java | 2 +- 4 files changed, 26 insertions(+), 20 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index ef02491061..15323a58ac 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -13,7 +13,8 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ### Changes * Bump default `ktfmt` version to latest `0.58` -> `0.59`. ([#2681](https://github.com/diffplug/spotless/pull/2681) ### Fixed -- palantirJavaFormat is no longer arbitrarily set to outdated versions on Java 17, latest available version is always used ([#2686](https://github.com/diffplug/spotless/pull/2686) fixes [#2685](https://github.com/diffplug/spotless/issues/2685)) +* palantirJavaFormat is no longer arbitrarily set to outdated versions on Java 17, latest available version is always used ([#2686](https://github.com/diffplug/spotless/pull/2686) fixes [#2685](https://github.com/diffplug/spotless/issues/2685)) +* Use Provider API for Gradle properties. ([#2718](https://github.com/diffplug/spotless/pull/2718) ### Removed * **BREAKING** Drop support for older Ktlint versions. ([#2711](https://github.com/diffplug/spotless/pull/2711)) diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java index 448cf4b506..0024703b12 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java @@ -630,8 +630,11 @@ public LicenseHeaderConfig updateYearWithLatest(boolean updateYearWithLatest) { FormatterStep createStep() { return builder.withYearModeLazy(() -> { - if ("true".equals(spotless.project - .findProperty(LicenseHeaderStep.FLAG_SET_LICENSE_HEADER_YEARS_FROM_GIT_HISTORY()))) { + if (spotless.project + .getProviders() + .gradleProperty(LicenseHeaderStep.FLAG_SET_LICENSE_HEADER_YEARS_FROM_GIT_HISTORY()) + .map(Boolean::parseBoolean) + .getOrElse(false)) { return YearMode.SET_FROM_GIT; } else { boolean updateYear = updateYearWithLatest == null ? getRatchetFrom() != null : updateYearWithLatest; diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/IdeHook.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/IdeHook.java index ffa1a18efb..9e05c30960 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/IdeHook.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/IdeHook.java @@ -19,9 +19,8 @@ import java.io.IOException; import java.nio.file.Files; -import javax.annotation.Nullable; - import org.gradle.api.Project; +import org.gradle.api.provider.Provider; import com.diffplug.common.base.Errors; import com.diffplug.common.io.ByteStreams; @@ -31,19 +30,22 @@ final class IdeHook { static class State extends NoLambda.EqualityBasedOnSerialization { - final @Nullable String path; - final boolean useStdIn; - final boolean useStdOut; + final Provider path; + final Provider useStdIn; + final Provider useStdOut; State(Project project) { - path = (String) project.findProperty(PROPERTY); - if (path != null) { - useStdIn = project.hasProperty(USE_STD_IN); - useStdOut = project.hasProperty(USE_STD_OUT); - } else { - useStdIn = false; - useStdOut = false; - } + this.path = project.getProviders().gradleProperty(PROPERTY); + + this.useStdIn = path.map(path -> project.getProviders() + .gradleProperty(USE_STD_IN) + .isPresent()) + .orElse(false); + + this.useStdOut = path.map(path -> project.getProviders() + .gradleProperty(USE_STD_OUT) + .isPresent()) + .orElse(false); } } @@ -56,7 +58,7 @@ private static void dumpIsClean() { } static void performHook(SpotlessTaskImpl spotlessTask, IdeHook.State state) { - File file = new File(state.path); + File file = new File(state.path.get()); if (!file.isAbsolute()) { System.err.println("Argument passed to " + PROPERTY + " must be an absolute path"); return; @@ -71,7 +73,7 @@ static void performHook(SpotlessTaskImpl spotlessTask, IdeHook.State state) { } } byte[] bytes; - if (state.useStdIn) { + if (state.useStdIn.get()) { bytes = ByteStreams.toByteArray(System.in); } else { bytes = Files.readAllBytes(file.toPath()); @@ -84,7 +86,7 @@ static void performHook(SpotlessTaskImpl spotlessTask, IdeHook.State state) { System.err.println("Run 'spotlessDiagnose' for details https://github.com/diffplug/spotless/blob/main/PADDEDCELL.md"); } else { System.err.println("IS DIRTY"); - if (state.useStdOut) { + if (state.useStdOut.get()) { dirty.writeCanonicalTo(System.out); } else { dirty.writeCanonicalTo(file); diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessPlugin.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessPlugin.java index bb8c36ad02..4739a4e60f 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessPlugin.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessPlugin.java @@ -42,7 +42,7 @@ public void apply(Project project) { + "https://docs.gradle.org/current/userguide/building_java_projects.html#sec:java_cross_compilation"); } // if -PspotlessModern=true, then use the modern stuff instead of the legacy stuff - if (project.hasProperty(SPOTLESS_MODERN)) { + if (project.getProviders().gradleProperty(SPOTLESS_MODERN).isPresent()) { project.getLogger().warn("'spotlessModern' has no effect as of Spotless 5.0, recommend removing it."); } // make sure there's a `clean` and a `check` From dec96fea0f685eab1c8d943e20881ba01a921b1a Mon Sep 17 00:00:00 2001 From: Simon Le Bras Date: Thu, 13 Nov 2025 16:32:08 +0100 Subject: [PATCH 2/5] Eagerly resolve IDE hook properties --- .../com/diffplug/gradle/spotless/IdeHook.java | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/IdeHook.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/IdeHook.java index 9e05c30960..d7ae1568be 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/IdeHook.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/IdeHook.java @@ -19,8 +19,10 @@ import java.io.IOException; import java.nio.file.Files; +import javax.annotation.Nullable; + import org.gradle.api.Project; -import org.gradle.api.provider.Provider; +import org.gradle.api.provider.ProviderFactory; import com.diffplug.common.base.Errors; import com.diffplug.common.io.ByteStreams; @@ -30,22 +32,20 @@ final class IdeHook { static class State extends NoLambda.EqualityBasedOnSerialization { - final Provider path; - final Provider useStdIn; - final Provider useStdOut; + final @Nullable String path; + final boolean useStdIn; + final boolean useStdOut; State(Project project) { - this.path = project.getProviders().gradleProperty(PROPERTY); - - this.useStdIn = path.map(path -> project.getProviders() - .gradleProperty(USE_STD_IN) - .isPresent()) - .orElse(false); - - this.useStdOut = path.map(path -> project.getProviders() - .gradleProperty(USE_STD_OUT) - .isPresent()) - .orElse(false); + ProviderFactory providers = project.getProviders(); + path = providers.gradleProperty(PROPERTY).getOrNull(); + if (path != null) { + useStdIn = providers.gradleProperty(USE_STD_IN).isPresent(); + useStdOut = providers.gradleProperty(USE_STD_OUT).isPresent(); + } else { + useStdIn = false; + useStdOut = false; + } } } @@ -58,7 +58,7 @@ private static void dumpIsClean() { } static void performHook(SpotlessTaskImpl spotlessTask, IdeHook.State state) { - File file = new File(state.path.get()); + File file = new File(state.path); if (!file.isAbsolute()) { System.err.println("Argument passed to " + PROPERTY + " must be an absolute path"); return; @@ -73,7 +73,7 @@ static void performHook(SpotlessTaskImpl spotlessTask, IdeHook.State state) { } } byte[] bytes; - if (state.useStdIn.get()) { + if (state.useStdIn) { bytes = ByteStreams.toByteArray(System.in); } else { bytes = Files.readAllBytes(file.toPath()); @@ -86,7 +86,7 @@ static void performHook(SpotlessTaskImpl spotlessTask, IdeHook.State state) { System.err.println("Run 'spotlessDiagnose' for details https://github.com/diffplug/spotless/blob/main/PADDEDCELL.md"); } else { System.err.println("IS DIRTY"); - if (state.useStdOut.get()) { + if (state.useStdOut) { dirty.writeCanonicalTo(System.out); } else { dirty.writeCanonicalTo(file); From 55dda5fb0b38b8529b28ddaa2182d30f7dc03a89 Mon Sep 17 00:00:00 2001 From: Simon Le Bras Date: Thu, 13 Nov 2025 17:28:49 +0100 Subject: [PATCH 3/5] Add forUseAtConfigurationTime compatibility helper --- .../gradle/spotless/SpotlessPlugin.java | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessPlugin.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessPlugin.java index 4739a4e60f..fa49e288d6 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessPlugin.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessPlugin.java @@ -15,11 +15,16 @@ */ package com.diffplug.gradle.spotless; +import java.lang.reflect.Method; + +import javax.annotation.Nonnull; + import org.gradle.api.GradleException; import org.gradle.api.JavaVersion; import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.plugins.BasePlugin; +import org.gradle.api.provider.Provider; import org.gradle.util.GradleVersion; import com.diffplug.spotless.Jvm; @@ -42,7 +47,9 @@ public void apply(Project project) { + "https://docs.gradle.org/current/userguide/building_java_projects.html#sec:java_cross_compilation"); } // if -PspotlessModern=true, then use the modern stuff instead of the legacy stuff - if (project.getProviders().gradleProperty(SPOTLESS_MODERN).isPresent()) { + Provider spotlessModernProperty = safeForUseAtConfigurationTime( + project.getProviders().gradleProperty(SPOTLESS_MODERN)); + if (spotlessModernProperty.isPresent()) { project.getLogger().warn("'spotlessModern' has no effect as of Spotless 5.0, recommend removing it."); } // make sure there's a `clean` and a `check` @@ -64,4 +71,15 @@ public void apply(Project project) { static String capitalize(String input) { return Character.toUpperCase(input.charAt(0)) + input.substring(1); } + + static Provider safeForUseAtConfigurationTime(@Nonnull Provider provider) { + try { + Method method = Provider.class.getMethod("forUseAtConfigurationTime"); + @SuppressWarnings("unchecked") + Provider configuredProvider = (Provider) method.invoke(provider); + return configuredProvider; + } catch (ReflectiveOperationException e) { + return provider; + } + } } From 3c6baefd10373c8650cf1e37e00b012dcded3f39 Mon Sep 17 00:00:00 2001 From: Simon Le Bras Date: Thu, 13 Nov 2025 18:05:00 +0100 Subject: [PATCH 4/5] Fix forUseAtConfigurationTime --- .../gradle/spotless/SpotlessPlugin.java | 32 +++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessPlugin.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessPlugin.java index fa49e288d6..477214d3a1 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessPlugin.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessPlugin.java @@ -47,8 +47,7 @@ public void apply(Project project) { + "https://docs.gradle.org/current/userguide/building_java_projects.html#sec:java_cross_compilation"); } // if -PspotlessModern=true, then use the modern stuff instead of the legacy stuff - Provider spotlessModernProperty = safeForUseAtConfigurationTime( - project.getProviders().gradleProperty(SPOTLESS_MODERN)); + Provider spotlessModernProperty = forUseAtConfigurationTime(project.getProviders().gradleProperty(SPOTLESS_MODERN)); if (spotlessModernProperty.isPresent()) { project.getLogger().warn("'spotlessModern' has no effect as of Spotless 5.0, recommend removing it."); } @@ -72,14 +71,29 @@ static String capitalize(String input) { return Character.toUpperCase(input.charAt(0)) + input.substring(1); } - static Provider safeForUseAtConfigurationTime(@Nonnull Provider provider) { - try { - Method method = Provider.class.getMethod("forUseAtConfigurationTime"); - @SuppressWarnings("unchecked") - Provider configuredProvider = (Provider) method.invoke(provider); - return configuredProvider; - } catch (ReflectiveOperationException e) { + static Provider forUseAtConfigurationTime(@Nonnull Provider provider) { + if (isGradle73OrNewer() && !isGradle74OrNewer()) { + try { + // Use reflection to access the forUseAtConfigurationTime method as it was removed in Gradle 9. + Method method = Provider.class.getMethod("forUseAtConfigurationTime"); + return (Provider) method.invoke(provider); + } catch (Exception e) { + throw new RuntimeException("Failed to invoke forUseAtConfigurationTime via reflection", e); + } + } else { return provider; } } + + static boolean isGradle73OrNewer() { + return isGradleNewerThan("7.3"); + } + + static boolean isGradle74OrNewer() { + return isGradleNewerThan("7.4"); + } + + private static boolean isGradleNewerThan(String version) { + return GradleVersion.current().compareTo(GradleVersion.version(version)) >= 0; + } } From 1443d80cd2fa8fdcf66e549558ea9f07c03bec2f Mon Sep 17 00:00:00 2001 From: Simon Le Bras Date: Fri, 14 Nov 2025 13:51:22 +0100 Subject: [PATCH 5/5] Add compatibility utils for Gradle properties --- .../gradle/spotless/FormatExtension.java | 6 +-- .../gradle/spotless/GradleCompat.java | 41 +++++++++++++++++++ .../com/diffplug/gradle/spotless/IdeHook.java | 8 ++-- .../gradle/spotless/SpotlessPlugin.java | 34 +-------------- 4 files changed, 46 insertions(+), 43 deletions(-) create mode 100644 plugin-gradle/src/main/java/com/diffplug/gradle/spotless/GradleCompat.java diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java index 0024703b12..ff22f7716f 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java @@ -630,11 +630,7 @@ public LicenseHeaderConfig updateYearWithLatest(boolean updateYearWithLatest) { FormatterStep createStep() { return builder.withYearModeLazy(() -> { - if (spotless.project - .getProviders() - .gradleProperty(LicenseHeaderStep.FLAG_SET_LICENSE_HEADER_YEARS_FROM_GIT_HISTORY()) - .map(Boolean::parseBoolean) - .getOrElse(false)) { + if (Boolean.parseBoolean(GradleCompat.findOptionalProperty(spotless.project, LicenseHeaderStep.FLAG_SET_LICENSE_HEADER_YEARS_FROM_GIT_HISTORY()))) { return YearMode.SET_FROM_GIT; } else { boolean updateYear = updateYearWithLatest == null ? getRatchetFrom() != null : updateYearWithLatest; diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/GradleCompat.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/GradleCompat.java new file mode 100644 index 0000000000..16d02b75b8 --- /dev/null +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/GradleCompat.java @@ -0,0 +1,41 @@ +/* + * Copyright 2025 DiffPlug + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.diffplug.gradle.spotless; + +import javax.annotation.Nullable; + +import org.gradle.api.Project; + +public final class GradleCompat { + private GradleCompat() {} + + @Nullable public static String findOptionalProperty(Project project, String propertyName) { + @Nullable String value = project.getProviders().gradleProperty(propertyName).getOrNull(); + if (value != null) { + return value; + } + @Nullable Object property = project.findProperty(propertyName); + if (property != null) { + return property.toString(); + } + return null; + } + + public static boolean isPropertyPresent(Project project, String propertyName) { + return project.getProviders().gradleProperty(propertyName).isPresent() || + project.hasProperty(propertyName); + } +} diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/IdeHook.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/IdeHook.java index d7ae1568be..98bb35069c 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/IdeHook.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/IdeHook.java @@ -22,7 +22,6 @@ import javax.annotation.Nullable; import org.gradle.api.Project; -import org.gradle.api.provider.ProviderFactory; import com.diffplug.common.base.Errors; import com.diffplug.common.io.ByteStreams; @@ -37,11 +36,10 @@ static class State extends NoLambda.EqualityBasedOnSerialization { final boolean useStdOut; State(Project project) { - ProviderFactory providers = project.getProviders(); - path = providers.gradleProperty(PROPERTY).getOrNull(); + path = GradleCompat.findOptionalProperty(project, PROPERTY); if (path != null) { - useStdIn = providers.gradleProperty(USE_STD_IN).isPresent(); - useStdOut = providers.gradleProperty(USE_STD_OUT).isPresent(); + useStdIn = GradleCompat.isPropertyPresent(project, USE_STD_IN); + useStdOut = GradleCompat.isPropertyPresent(project, USE_STD_OUT); } else { useStdIn = false; useStdOut = false; diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessPlugin.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessPlugin.java index 477214d3a1..cc55056c24 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessPlugin.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessPlugin.java @@ -15,16 +15,11 @@ */ package com.diffplug.gradle.spotless; -import java.lang.reflect.Method; - -import javax.annotation.Nonnull; - import org.gradle.api.GradleException; import org.gradle.api.JavaVersion; import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.plugins.BasePlugin; -import org.gradle.api.provider.Provider; import org.gradle.util.GradleVersion; import com.diffplug.spotless.Jvm; @@ -47,8 +42,7 @@ public void apply(Project project) { + "https://docs.gradle.org/current/userguide/building_java_projects.html#sec:java_cross_compilation"); } // if -PspotlessModern=true, then use the modern stuff instead of the legacy stuff - Provider spotlessModernProperty = forUseAtConfigurationTime(project.getProviders().gradleProperty(SPOTLESS_MODERN)); - if (spotlessModernProperty.isPresent()) { + if (GradleCompat.isPropertyPresent(project, SPOTLESS_MODERN)) { project.getLogger().warn("'spotlessModern' has no effect as of Spotless 5.0, recommend removing it."); } // make sure there's a `clean` and a `check` @@ -70,30 +64,4 @@ public void apply(Project project) { static String capitalize(String input) { return Character.toUpperCase(input.charAt(0)) + input.substring(1); } - - static Provider forUseAtConfigurationTime(@Nonnull Provider provider) { - if (isGradle73OrNewer() && !isGradle74OrNewer()) { - try { - // Use reflection to access the forUseAtConfigurationTime method as it was removed in Gradle 9. - Method method = Provider.class.getMethod("forUseAtConfigurationTime"); - return (Provider) method.invoke(provider); - } catch (Exception e) { - throw new RuntimeException("Failed to invoke forUseAtConfigurationTime via reflection", e); - } - } else { - return provider; - } - } - - static boolean isGradle73OrNewer() { - return isGradleNewerThan("7.3"); - } - - static boolean isGradle74OrNewer() { - return isGradleNewerThan("7.4"); - } - - private static boolean isGradleNewerThan(String version) { - return GradleVersion.current().compareTo(GradleVersion.version(version)) >= 0; - } }