Skip to content

Commit 474299b

Browse files
committed
Fix applying the Gradle plugin without Spotless
1 parent 4afb922 commit 474299b

3 files changed

Lines changed: 85 additions & 3 deletions

File tree

‎gradle-open-java-format/src/main/java/com/palantir/javaformat/gradle/PalantirJavaFormatSpotlessPlugin.java‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,13 @@ public void apply(Project project) {
3535
Supplier<FormatterService> memoizedService =
3636
rootProject.getExtensions().getByType(JavaFormatExtension.class)::serviceLoad;
3737

38-
SpotlessInterop spotlessInterop = rootProject.getObjects().newInstance(SpotlessInterop.class, memoizedService);
3938
project.getPluginManager().withPlugin("java", _javaPlugin -> {
4039
SPOTLESS_PLUGINS.forEach(spotlessPluginId -> project.getPluginManager()
4140
.withPlugin(spotlessPluginId, _spotlessPlugin -> {
41+
// Not before this point: Gradle's decoration of SpotlessInterop loads Spotless classes, which
42+
// are missing from a project that never applies Spotless.
43+
SpotlessInterop spotlessInterop =
44+
rootProject.getObjects().newInstance(SpotlessInterop.class, memoizedService);
4245
SpotlessExtension spotlessExtension =
4346
project.getExtensions().getByType(SpotlessExtension.class);
4447
spotlessExtension.java(spotlessInterop);
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* (c) Copyright 2026 Palantir Technologies Inc. All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.palantir.javaformat.gradle;
18+
19+
import static org.assertj.core.api.Assertions.assertThat;
20+
21+
import com.palantir.javaformat.gradle.testing.GradleTestProject;
22+
import java.nio.file.Path;
23+
import org.gradle.testkit.runner.BuildResult;
24+
import org.junit.jupiter.api.Test;
25+
import org.junit.jupiter.api.io.TempDir;
26+
27+
/**
28+
* Most projects that apply the formatter never apply Spotless, so there are no Spotless classes on their buildscript
29+
* classpath. The Spotless integration has to stay dormant there instead of failing the whole plugin.
30+
*/
31+
class AppliesWithoutSpotlessTest {
32+
33+
@TempDir
34+
private Path projectDir;
35+
36+
@Test
37+
void applies_without_spotless_on_the_classpath() {
38+
GradleTestProject project = new GradleTestProject(projectDir)
39+
.plugins("java", "dev.openjavaformat.java-format")
40+
.withoutSpotlessOnClasspath();
41+
42+
BuildResult result = project.succeeds("tasks", "--all");
43+
44+
assertThat(result.getOutput()).contains("formatDiff");
45+
}
46+
}

‎gradle-open-java-format/src/test/java/com/palantir/javaformat/gradle/testing/GradleTestProject.java‎

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616

1717
package com.palantir.javaformat.gradle.testing;
1818

19+
import java.io.File;
1920
import java.io.IOException;
21+
import java.io.InputStream;
2022
import java.io.UncheckedIOException;
2123
import java.nio.charset.StandardCharsets;
2224
import java.nio.file.Files;
@@ -25,6 +27,8 @@
2527
import java.util.Arrays;
2628
import java.util.LinkedHashSet;
2729
import java.util.List;
30+
import java.util.Objects;
31+
import java.util.Properties;
2832
import java.util.Set;
2933
import org.gradle.testkit.runner.BuildResult;
3034
import org.gradle.testkit.runner.GradleRunner;
@@ -46,11 +50,15 @@ public final class GradleTestProject {
4650
*/
4751
private static final String GRADLE_VERSION = "9.3.1";
4852

53+
/** Written by java-gradle-plugin: the classpath {@link GradleRunner#withPluginClasspath()} injects. */
54+
private static final String PLUGIN_METADATA = "plugin-under-test-metadata.properties";
55+
4956
private final Path projectDir;
5057
private final Set<String> plugins = new LinkedHashSet<>();
5158
private final StringBuilder buildGradle = new StringBuilder();
5259
private final StringBuilder gradleProperties = new StringBuilder();
5360
private boolean configurationCache;
61+
private boolean spotlessOnClasspath = true;
5462

5563
public GradleTestProject(Path projectDir) {
5664
this.projectDir = projectDir;
@@ -96,6 +104,16 @@ public GradleTestProject withConfigurationCache() {
96104
return this;
97105
}
98106

107+
/**
108+
* Leaves the Spotless jars out of the classpath injected next to the plugin under test. build.gradle puts them there
109+
* so that generated projects can apply Spotless, which gives every other test Spotless classes whether it applies
110+
* Spotless or not — unlike a real project that never does.
111+
*/
112+
public GradleTestProject withoutSpotlessOnClasspath() {
113+
this.spotlessOnClasspath = false;
114+
return this;
115+
}
116+
99117
public Path file(String relativePath) {
100118
return projectDir.resolve(relativePath);
101119
}
@@ -132,12 +150,27 @@ private GradleRunner runner(String... arguments) {
132150
List<String> allArguments = new ArrayList<>(Arrays.asList(arguments));
133151
allArguments.add("--stacktrace");
134152
allArguments.add(configurationCache ? "--configuration-cache" : "--no-configuration-cache");
135-
return GradleRunner.create()
153+
GradleRunner runner = GradleRunner.create()
136154
.withProjectDir(projectDir.toFile())
137-
.withPluginClasspath()
138155
.withGradleVersion(GRADLE_VERSION)
139156
.withArguments(allArguments)
140157
.forwardOutput();
158+
return spotlessOnClasspath
159+
? runner.withPluginClasspath()
160+
: runner.withPluginClasspath(pluginClasspathWithoutSpotless());
161+
}
162+
163+
private static List<File> pluginClasspathWithoutSpotless() {
164+
Properties metadata = new Properties();
165+
try (InputStream in = GradleTestProject.class.getClassLoader().getResourceAsStream(PLUGIN_METADATA)) {
166+
metadata.load(Objects.requireNonNull(in, PLUGIN_METADATA + " is not on the test classpath"));
167+
} catch (IOException e) {
168+
throw new UncheckedIOException("Could not read " + PLUGIN_METADATA, e);
169+
}
170+
return Arrays.stream(metadata.getProperty("implementation-classpath").split(File.pathSeparator))
171+
.map(File::new)
172+
.filter(file -> !file.getName().startsWith("spotless-"))
173+
.toList();
141174
}
142175

143176
private void write() {

0 commit comments

Comments
 (0)