From ed8119a3ee2c38200140c774bb70d3f1fd7e005c Mon Sep 17 00:00:00 2001 From: Brice Dutheil Date: Tue, 12 May 2026 14:36:04 +0200 Subject: [PATCH 1/3] feat: Makes spotlessPredeclare available to Kotlin DSL users Fixes #2580 # Conflicts: # plugin-gradle/CHANGES.md --- plugin-gradle/CHANGES.md | 1 + plugin-gradle/README.md | 1 + .../gradle/spotless/SpotlessExtension.java | 2 +- .../spotless/SpotlessExtensionPredeclare.java | 56 ++++++++++++++----- .../gradle/spotless/SpotlessPlugin.java | 3 + .../gradle/spotless/MultiProjectTest.java | 9 ++- .../SpotlessPredeclareIntegrationTest.java | 28 ++++++++-- 7 files changed, 74 insertions(+), 26 deletions(-) diff --git a/plugin-gradle/CHANGES.md b/plugin-gradle/CHANGES.md index 7598404841..d8cf4ae220 100644 --- a/plugin-gradle/CHANGES.md +++ b/plugin-gradle/CHANGES.md @@ -12,6 +12,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ### Changes - Bump default `cleanthat` version `2.24` -> `2.25`. ([#2903](https://github.com/diffplug/spotless/pull/2903)) - Bump default `eclipse-jdt` version from `4.35` to `4.39`. ([#2912](https://github.com/diffplug/spotless/pull/2912)) +- Make `spotlessPredeclare` visible to Gradle Kotlin DSL type-safe accessors. ## [8.4.0] - 2026-03-18 ### Added diff --git a/plugin-gradle/README.md b/plugin-gradle/README.md index 3efa2f2438..f8c6e8341e 100644 --- a/plugin-gradle/README.md +++ b/plugin-gradle/README.md @@ -1972,6 +1972,7 @@ spotlessPredeclare { Alternatively, you can also use `predeclareDepsFromBuildscript()` to resolve the dependencies from the buildscript repositories rather than the project repositories. If you use this feature, you will get an error if you use a formatter in a subproject which is not declared in the `spotlessPredeclare` block. +The `spotlessPredeclare` block can appear before or after the `spotless` block, but `predeclareDeps()` or `predeclareDepsFromBuildscript()` must be called to enable it. Note that this feature is also incompatible with Isolated projects, because every project must reference the root project. diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessExtension.java index 26e91eba8c..6b34ecdcf3 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessExtension.java @@ -323,6 +323,6 @@ public void predeclareDeps() { } protected void predeclare(GradleProvisioner.Policy policy) { - project.getExtensions().create(SpotlessExtensionPredeclare.class, EXTENSION_PREDECLARE, SpotlessExtensionPredeclare.class, project, policy); + project.getExtensions().getByType(SpotlessExtensionPredeclare.class).enablePredeclare(policy); } } diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessExtensionPredeclare.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessExtensionPredeclare.java index ef8589523f..416a5943e6 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessExtensionPredeclare.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessExtensionPredeclare.java @@ -18,7 +18,10 @@ import java.util.SortedMap; import java.util.TreeMap; +import javax.annotation.Nullable; + import org.gradle.api.Action; +import org.gradle.api.GradleException; import org.gradle.api.Project; import org.gradle.api.tasks.TaskProvider; @@ -26,23 +29,36 @@ public class SpotlessExtensionPredeclare extends SpotlessExtension { private final SortedMap toSetup = new TreeMap<>(); - private final RegisterDependenciesTask registerDependenciesTask; + private @Nullable GradleProvisioner.Policy policy; + private @Nullable RegisterDependenciesTask registerDependenciesTask; - public SpotlessExtensionPredeclare(Project project, GradleProvisioner.Policy policy) { + public SpotlessExtensionPredeclare(Project project) { super(project); - this.registerDependenciesTask = findRegisterDepsTask().get(); - SpotlessTaskService taskService = getSpotlessTaskService().get(); - taskService.registerDependenciesTask = registerDependenciesTask; - taskService.predeclaredProvisioner = policy.dedupingProvisioner(project); - taskService.predeclaredP2Provisioner = policy.dedupingP2Provisioner(project); - project.afterEvaluate(unused -> toSetup.forEach((name, formatExtension) -> { - for (Action lazyAction : formatExtension.lazyActions) { - lazyAction.execute(formatExtension); + project.afterEvaluate(unused -> { + if (policy == null) { + if (!toSetup.isEmpty()) { + throw new GradleException("spotlessPredeclare requires `spotless { predeclareDeps() }` or `spotless { predeclareDepsFromBuildscript() }` in the root project."); + } + return; + } + RegisterDependenciesTask task = registerDependenciesTask; + if (task == null) { + throw new IllegalStateException("spotlessPredeclare was enabled without a register dependencies task."); } - registerDependenciesTask.steps.addAll(formatExtension.steps); - // needed to fix Deemon memory leaks (#1194), but this line came from https://github.com/diffplug/spotless/pull/1206 - LazyForwardingEquality.unlazy(registerDependenciesTask.steps); - })); + toSetup.forEach((name, formatExtension) -> { + for (Action lazyAction : formatExtension.lazyActions) { + lazyAction.execute(formatExtension); + } + task.steps.addAll(formatExtension.steps); + // needed to fix Deemon memory leaks (#1194), but this line came from https://github.com/diffplug/spotless/pull/1206 + LazyForwardingEquality.unlazy(task.steps); + }); + }); + } + + public SpotlessExtensionPredeclare(Project project, GradleProvisioner.Policy policy) { + this(project); + enablePredeclare(policy); } @Override @@ -55,6 +71,18 @@ protected void predeclare(GradleProvisioner.Policy policy) { throw new UnsupportedOperationException("predeclare can't be called from within `" + EXTENSION_PREDECLARE + "`"); } + void enablePredeclare(GradleProvisioner.Policy policy) { + if (this.policy != null) { + throw new GradleException("predeclareDeps can only be called once."); + } + this.policy = policy; + this.registerDependenciesTask = findRegisterDepsTask().get(); + SpotlessTaskService taskService = getSpotlessTaskService().get(); + taskService.registerDependenciesTask = registerDependenciesTask; + taskService.predeclaredProvisioner = policy.dedupingProvisioner(project); + taskService.predeclaredP2Provisioner = policy.dedupingP2Provisioner(project); + } + private TaskProvider findRegisterDepsTask() { try { return findRegisterDepsTask(RegisterDependenciesTask.TASK_NAME); 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 2bd773f2d3..2598c6aa7d 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 @@ -50,6 +50,9 @@ public void apply(Project project) { // setup the extension project.getExtensions().create(SpotlessExtension.class, SpotlessExtension.EXTENSION, SpotlessExtensionImpl.class, project); + if (project.getRootProject() == project) { + project.getExtensions().create(SpotlessExtensionPredeclare.class, SpotlessExtension.EXTENSION_PREDECLARE, SpotlessExtensionPredeclare.class, project); + } // clear spotless' cache when the user does a clean // resolution for: https://github.com/diffplug/spotless/issues/243#issuecomment-564323856 diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/MultiProjectTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/MultiProjectTest.java index 35bf2708b4..41a17d425f 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/MultiProjectTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/MultiProjectTest.java @@ -122,7 +122,7 @@ public void predeclaredFromBuildscriptSucceeds() throws IOException { } @Test - public void predeclaredOrdering() throws IOException { + public void predeclaredOrderingIsFlexible() throws IOException { setFile("build.gradle").toLines( "plugins {", " id 'com.diffplug.spotless'", @@ -131,10 +131,9 @@ public void predeclaredOrdering() throws IOException { "spotlessPredeclare {", " java { googleJavaFormat('1.17.0') }", "}", - "spotless { predeclareDepsFromBuildscript() }"); + "spotless { predeclareDeps() }"); createNSubprojects(); - Assertions.assertThat(gradleRunner().withArguments("spotlessApply").buildAndFail().getOutput()) - .contains("Could not find method spotlessPredeclare() for arguments"); + gradleRunner().withArguments("spotlessApply").build(); } @Test @@ -179,6 +178,6 @@ public void predeclaredUndeclared() throws IOException { "}"); createNSubprojects(); Assertions.assertThat(gradleRunner().withArguments("spotlessApply").buildAndFail().getOutput()) - .contains("Could not find method spotlessPredeclare() for arguments"); + .contains("spotlessPredeclare requires `spotless { predeclareDeps() }` or `spotless { predeclareDepsFromBuildscript() }` in the root project."); } } diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/SpotlessPredeclareIntegrationTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/SpotlessPredeclareIntegrationTest.java index c1bfcf906e..80de812e30 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/SpotlessPredeclareIntegrationTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/SpotlessPredeclareIntegrationTest.java @@ -524,25 +524,41 @@ void predeclareRequiresPredeclareDepsCall() throws IOException { BuildResult result = gradleRunner().withArguments("spotlessApply").buildAndFail(); assertThat(result.getOutput()) - .contains("Could not find method spotlessPredeclare() for arguments"); + .contains("spotlessPredeclare requires `spotless { predeclareDeps() }` or `spotless { predeclareDepsFromBuildscript() }` in the root project."); } @Test - void predeclareBlockMustComeAfterPredeclareDeps() throws IOException { + void predeclareBlockCanComeBeforePredeclareDeps() throws IOException { setFile("build.gradle").toContent(""" plugins { id 'com.diffplug.spotless' } repositories { mavenCentral() } spotlessPredeclare { - java { googleJavaFormat('1.17.0') } + format('misc') { trimTrailingWhitespace() } } spotless { predeclareDeps() } """); - BuildResult result = gradleRunner().withArguments("spotlessApply").buildAndFail(); - assertThat(result.getOutput()) - .contains("Could not find method spotlessPredeclare() for arguments"); + BuildResult result = gradleRunner().withArguments("help").build(); + assertThat(result.getOutput()).contains("BUILD SUCCESSFUL"); + } + + @Test + void predeclareBlockHasKotlinDslAccessor() throws IOException { + setFile("build.gradle.kts").toContent(""" + plugins { + id("com.diffplug.spotless") + } + repositories { mavenCentral() } + spotlessPredeclare { + format("misc") { trimTrailingWhitespace() } + } + spotless { predeclareDeps() } + """); + + BuildResult result = gradleRunner().withArguments("help").build(); + assertThat(result.getOutput()).contains("BUILD SUCCESSFUL"); } @Test From 050a93beee4bfc379b94aac89e5c235eb3a9cc94 Mon Sep 17 00:00:00 2001 From: Brice Dutheil Date: Tue, 12 May 2026 15:57:37 +0200 Subject: [PATCH 2/3] feat: Makes spotlessPredeclare immediately usable This avoids the necessity to declare it in the spotless extension. The old style API are kept for backward compat. --- plugin-gradle/CHANGES.md | 1 + plugin-gradle/README.md | 18 ++++-- .../gradle/spotless/SpotlessExtension.java | 27 ++++++++- .../spotless/SpotlessExtensionPredeclare.java | 60 +++++++++++++++---- .../gradle/spotless/SpotlessPlugin.java | 2 +- .../gradle/spotless/MultiProjectTest.java | 36 ++++++++--- .../SpotlessPredeclareIntegrationTest.java | 26 ++++++-- 7 files changed, 139 insertions(+), 31 deletions(-) diff --git a/plugin-gradle/CHANGES.md b/plugin-gradle/CHANGES.md index d8cf4ae220..32eeaccd8e 100644 --- a/plugin-gradle/CHANGES.md +++ b/plugin-gradle/CHANGES.md @@ -13,6 +13,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( - Bump default `cleanthat` version `2.24` -> `2.25`. ([#2903](https://github.com/diffplug/spotless/pull/2903)) - Bump default `eclipse-jdt` version from `4.35` to `4.39`. ([#2912](https://github.com/diffplug/spotless/pull/2912)) - Make `spotlessPredeclare` visible to Gradle Kotlin DSL type-safe accessors. +- Allow `spotlessPredeclare` to be used directly without enabling it first in spotless extension. ## [8.4.0] - 2026-03-18 ### Added diff --git a/plugin-gradle/README.md b/plugin-gradle/README.md index f8c6e8341e..9f4e4d04d2 100644 --- a/plugin-gradle/README.md +++ b/plugin-gradle/README.md @@ -1959,20 +1959,28 @@ spotless { By default, Spotless resolves dependencies on a per-project basis. For very large parallel builds, this can sometimes cause problems. As an alternative, Spotless can be configured to resolve all dependencies in the root project like so: ```gradle -spotless { - ... - predeclareDeps() +spotlessPredeclare { + java { eclipse() } + kotlin { ktfmt('0.28') } +} +``` + +By default, `spotlessPredeclare` resolves dependencies from the root project's repositories. Alternatively, you can resolve dependencies from the buildscript repositories rather than the project repositories: + +```gradle +buildscript { + repositories { mavenCentral() } } spotlessPredeclare { + fromBuildscriptRepositories() java { eclipse() } kotlin { ktfmt('0.28') } } ``` -Alternatively, you can also use `predeclareDepsFromBuildscript()` to resolve the dependencies from the buildscript repositories rather than the project repositories. +The older `spotless { predeclareDeps() }` and `spotless { predeclareDepsFromBuildscript() }` APIs are still supported. If you use this feature, you will get an error if you use a formatter in a subproject which is not declared in the `spotlessPredeclare` block. -The `spotlessPredeclare` block can appear before or after the `spotless` block, but `predeclareDeps()` or `predeclareDepsFromBuildscript()` must be called to enable it. Note that this feature is also incompatible with Isolated projects, because every project must reference the root project. diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessExtension.java index 6b34ecdcf3..e9acc00c24 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessExtension.java @@ -308,21 +308,46 @@ T instantiateFormatExtension(Class clazz) { protected abstract void createFormatTasks(String name, FormatExtension formatExtension); + /** + * Enables predeclared dependency resolution using the root project's {@code buildscript} repositories. + * + * @deprecated Configure the repository policy directly in {@code spotlessPredeclare} instead: + *
{@code
+	 *             spotlessPredeclare {
+	 *                 fromBuildscriptRepositories()
+	 *                 java { googleJavaFormat("1.17.0") }
+	 *             }
+	 *             }
+ */ + @Deprecated public void predeclareDepsFromBuildscript() { if (project.getRootProject() != project) { throw new GradleException("predeclareDepsFromBuildscript can only be called from the root project"); } + project.getLogger().info("predeclareDepsFromBuildscript() is deprecated, use 'spotlessPredeclare { fromBuildscriptRepositories() }' directly instead."); predeclare(GradleProvisioner.Policy.ROOT_BUILDSCRIPT); } + /** + * Enables predeclared dependency resolution using the root project's repositories. + * + * @deprecated Declare formats directly in {@code spotlessPredeclare} instead: + *
{@code
+	 *             spotlessPredeclare {
+	 *                 java { googleJavaFormat("1.17.0") }
+	 *             }
+	 *             }
+ */ + @Deprecated public void predeclareDeps() { if (project.getRootProject() != project) { throw new GradleException("predeclareDeps can only be called from the root project"); } + project.getLogger().info("predeclareDeps() is deprecated, use 'spotlessPredeclare { ... }' directly instead."); predeclare(GradleProvisioner.Policy.ROOT_PROJECT); } - protected void predeclare(GradleProvisioner.Policy policy) { + void predeclare(GradleProvisioner.Policy policy) { project.getExtensions().getByType(SpotlessExtensionPredeclare.class).enablePredeclare(policy); } } diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessExtensionPredeclare.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessExtensionPredeclare.java index 416a5943e6..ad3f69e848 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessExtensionPredeclare.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessExtensionPredeclare.java @@ -30,15 +30,13 @@ public class SpotlessExtensionPredeclare extends SpotlessExtension { private final SortedMap toSetup = new TreeMap<>(); private @Nullable GradleProvisioner.Policy policy; + private boolean policyExplicit; private @Nullable RegisterDependenciesTask registerDependenciesTask; public SpotlessExtensionPredeclare(Project project) { super(project); project.afterEvaluate(unused -> { if (policy == null) { - if (!toSetup.isEmpty()) { - throw new GradleException("spotlessPredeclare requires `spotless { predeclareDeps() }` or `spotless { predeclareDepsFromBuildscript() }` in the root project."); - } return; } RegisterDependenciesTask task = registerDependenciesTask; @@ -56,27 +54,67 @@ public SpotlessExtensionPredeclare(Project project) { }); } - public SpotlessExtensionPredeclare(Project project, GradleProvisioner.Policy policy) { - this(project); - enablePredeclare(policy); - } - @Override protected void createFormatTasks(String name, FormatExtension formatExtension) { + enablePredeclareIfAbsent(GradleProvisioner.Policy.ROOT_PROJECT); toSetup.put(name, formatExtension); } @Override - protected void predeclare(GradleProvisioner.Policy policy) { + void predeclare(GradleProvisioner.Policy policy) { throw new UnsupportedOperationException("predeclare can't be called from within `" + EXTENSION_PREDECLARE + "`"); } + /** + * Resolves predeclared dependencies from the root project's repositories. + *

+ * This is the default behavior when any format is declared in {@code spotlessPredeclare}. + * + * @see SpotlessExtension#predeclareDeps() + */ + public void fromProjectRepositories() { + enablePredeclare(GradleProvisioner.Policy.ROOT_PROJECT); + } + + /** + * Resolves predeclared dependencies from the root project's {@code buildscript} repositories. + *

+ * Use this when formatter dependencies should be resolved from {@code buildscript { repositories { ... } }} + * instead of the root project's normal repositories. + * + * @see SpotlessExtension#predeclareDepsFromBuildscript() + */ + public void fromBuildscriptRepositories() { + enablePredeclare(GradleProvisioner.Policy.ROOT_BUILDSCRIPT); + } + void enablePredeclare(GradleProvisioner.Policy policy) { + enablePredeclare(policy, true); + } + + private void enablePredeclareIfAbsent(GradleProvisioner.Policy policy) { + enablePredeclare(policy, false); + } + + private void enablePredeclare(GradleProvisioner.Policy policy, boolean explicit) { if (this.policy != null) { - throw new GradleException("predeclareDeps can only be called once."); + if (!explicit || !policyExplicit) { + if (explicit) { + this.policy = policy; + this.policyExplicit = true; + configureProvisioners(policy); + } + return; + } + throw new GradleException("predeclared dependency resolution can only be configured once."); } this.policy = policy; - this.registerDependenciesTask = findRegisterDepsTask().get(); + this.policyExplicit = explicit; + registerDependenciesTask = findRegisterDepsTask().get(); + configureProvisioners(policy); + } + + private void configureProvisioners(GradleProvisioner.Policy policy) { SpotlessTaskService taskService = getSpotlessTaskService().get(); taskService.registerDependenciesTask = registerDependenciesTask; taskService.predeclaredProvisioner = policy.dedupingProvisioner(project); 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 2598c6aa7d..a5d0e19e7a 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 @@ -1,5 +1,5 @@ /* - * Copyright 2016-2025 DiffPlug + * Copyright 2016-2026 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/MultiProjectTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/MultiProjectTest.java index 41a17d425f..3403bbfaad 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/MultiProjectTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/MultiProjectTest.java @@ -92,30 +92,53 @@ public void predeclaredFails() throws IOException { } @Test - public void predeclaredSucceeds() throws IOException { + public void predeclaredSucceeds_deprecatedAPI() throws IOException { setFile("build.gradle").toLines( "plugins {", " id 'com.diffplug.spotless'", "}", "repositories { mavenCentral() }", "spotless { predeclareDeps() }", + "", "spotlessPredeclare {", - " java { googleJavaFormat('1.17.0') }", + " java { googleJavaFormat('1.17.0') }", "}"); createNSubprojects(); gradleRunner().withArguments("spotlessApply").build(); } @Test - public void predeclaredFromBuildscriptSucceeds() throws IOException { + public void predeclaredFromBuildscriptSucceeds_deprecatedAPI() throws IOException { setFile("build.gradle").toLines( + "buildscript {", + " repositories { mavenCentral() }", + "}", "plugins {", " id 'com.diffplug.spotless'", "}", "repositories { mavenCentral() }", "spotless { predeclareDepsFromBuildscript() }", "spotlessPredeclare {", - " java { googleJavaFormat('1.17.0') }", + " java { googleJavaFormat('1.17.0') }", + "}"); + createNSubprojects(); + gradleRunner().withArguments("spotlessApply").build(); + } + + @Test + public void predeclaredFromBuildscriptInPredeclareBlockSucceeds() throws IOException { + setFile("build.gradle").toLines( + "buildscript {", + " repositories { mavenCentral() }", + "}", + "plugins {", + " id 'com.diffplug.spotless'", + "}", + "repositories { mavenCentral() }", + "", + "spotlessPredeclare {", + " fromBuildscriptRepositories()", + " java { googleJavaFormat('1.17.0') }", "}"); createNSubprojects(); gradleRunner().withArguments("spotlessApply").build(); @@ -167,7 +190,7 @@ public void predeclaredDepsRegression() throws IOException { } @Test - public void predeclaredUndeclared() throws IOException { + public void predeclaredWithoutSpotlessBlockSucceeds() throws IOException { setFile("build.gradle").toLines( "plugins {", " id 'com.diffplug.spotless'", @@ -177,7 +200,6 @@ public void predeclaredUndeclared() throws IOException { " java { googleJavaFormat('1.17.0') }", "}"); createNSubprojects(); - Assertions.assertThat(gradleRunner().withArguments("spotlessApply").buildAndFail().getOutput()) - .contains("spotlessPredeclare requires `spotless { predeclareDeps() }` or `spotless { predeclareDepsFromBuildscript() }` in the root project."); + gradleRunner().withArguments("spotlessApply").build(); } } diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/SpotlessPredeclareIntegrationTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/SpotlessPredeclareIntegrationTest.java index 80de812e30..4970b6bb5b 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/SpotlessPredeclareIntegrationTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/SpotlessPredeclareIntegrationTest.java @@ -511,20 +511,19 @@ target file('test.java') @Nested class EdgeCases { @Test - void predeclareRequiresPredeclareDepsCall() throws IOException { + void predeclareBlockEnablesPredeclareDeps() throws IOException { setFile("build.gradle").toContent(""" plugins { id 'com.diffplug.spotless' } repositories { mavenCentral() } spotlessPredeclare { - java { googleJavaFormat('1.17.0') } + format('misc') { trimTrailingWhitespace() } } """); - BuildResult result = gradleRunner().withArguments("spotlessApply").buildAndFail(); - assertThat(result.getOutput()) - .contains("spotlessPredeclare requires `spotless { predeclareDeps() }` or `spotless { predeclareDepsFromBuildscript() }` in the root project."); + BuildResult result = gradleRunner().withArguments("help").build(); + assertThat(result.getOutput()).contains("BUILD SUCCESSFUL"); } @Test @@ -554,7 +553,22 @@ void predeclareBlockHasKotlinDslAccessor() throws IOException { spotlessPredeclare { format("misc") { trimTrailingWhitespace() } } - spotless { predeclareDeps() } + """); + + BuildResult result = gradleRunner().withArguments("help").build(); + assertThat(result.getOutput()).contains("BUILD SUCCESSFUL"); + } + + @Test + void predeclareBlockCanSelectBuildscriptRepositories() throws IOException { + setFile("build.gradle.kts").toContent(""" + plugins { + id("com.diffplug.spotless") + } + spotlessPredeclare { + format("misc") { trimTrailingWhitespace() } + fromBuildscriptRepositories() + } """); BuildResult result = gradleRunner().withArguments("help").build(); From cd5be1d1241bb557e2d37b24ecf6ce3a8ebbdaca Mon Sep 17 00:00:00 2001 From: Brice Dutheil Date: Tue, 12 May 2026 16:20:59 +0200 Subject: [PATCH 3/3] chore: Adds PR links to Changes --- plugin-gradle/CHANGES.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin-gradle/CHANGES.md b/plugin-gradle/CHANGES.md index 32eeaccd8e..b07bfb59db 100644 --- a/plugin-gradle/CHANGES.md +++ b/plugin-gradle/CHANGES.md @@ -12,8 +12,8 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ### Changes - Bump default `cleanthat` version `2.24` -> `2.25`. ([#2903](https://github.com/diffplug/spotless/pull/2903)) - Bump default `eclipse-jdt` version from `4.35` to `4.39`. ([#2912](https://github.com/diffplug/spotless/pull/2912)) -- Make `spotlessPredeclare` visible to Gradle Kotlin DSL type-safe accessors. -- Allow `spotlessPredeclare` to be used directly without enabling it first in spotless extension. +- Make `spotlessPredeclare` visible to Gradle Kotlin DSL type-safe accessors. ([#2925](https://github.com/diffplug/spotless/pull/2925)) +- Allow `spotlessPredeclare` to be used directly without enabling it first in spotless extension. ([#2925](https://github.com/diffplug/spotless/pull/2925)) ## [8.4.0] - 2026-03-18 ### Added