Skip to content

Commit c1683f2

Browse files
authored
Fix Maven spotlessApply aborting on first file with lints (#2937)
2 parents e5152fa + 9788e40 commit c1683f2

3 files changed

Lines changed: 76 additions & 12 deletions

File tree

plugin-maven/CHANGES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `1.27.0`).
44

55
## [Unreleased]
6+
### Fixed
7+
- `spotless:apply` no longer aborts on the first file with lints; it now formats all files and reports a single aggregated lint failure across every file, matching the Gradle plugin's behavior. ([#2937](https://github.com/diffplug/spotless/pull/2937))
68
### Changes
79
- Improved formatting performance by eliminating redundant per-step line-ending normalization in the core formatter loop. ([#2934](https://github.com/diffplug/spotless/pull/2934))
810

plugin-maven/src/main/java/com/diffplug/spotless/maven/SpotlessApplyMojo.java

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2025 DiffPlug
2+
* Copyright 2016-2026 DiffPlug
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -55,6 +55,8 @@ protected void process(String name, Iterable<File> files, Formatter formatter, U
5555
}
5656

5757
ImpactedFilesTracker counter = new ImpactedFilesTracker();
58+
int totalLintCount = 0;
59+
StringBuilder lintMessage = new StringBuilder();
5860

5961
for (File file : files) {
6062
if (upToDateChecker.isUpToDate(file.toPath())) {
@@ -79,25 +81,22 @@ protected void process(String name, Iterable<File> files, Formatter formatter, U
7981
counter.checkedButAlreadyClean();
8082
}
8183

82-
// In apply mode, any lints should fail the build (matching Gradle behavior)
84+
// In apply mode, any lints should fail the build (matching Gradle behavior).
85+
// Collect lints across all files and fail once at the end, so a single
86+
// linting file doesn't prevent the remaining files from being formatted.
8387
if (hasUnsuppressedLints) {
84-
int lintCount = lintState.getLintsByStep(formatter).values().stream()
85-
.mapToInt(List::size)
86-
.sum();
87-
StringBuilder message = new StringBuilder();
88-
message.append("There were ").append(lintCount).append(" lint error(s), they must be fixed or suppressed.");
89-
9088
// Build lint messages in Gradle format (using relative path, not just filename)
9189
for (Map.Entry<String, List<Lint>> stepEntry : lintState.getLintsByStep(formatter).entrySet()) {
9290
String stepName = stepEntry.getKey();
9391
for (Lint lint : stepEntry.getValue()) {
9492
String relativePath = LintSuppression.relativizeAsUnix(baseDir, file);
95-
message.append("\n ").append(relativePath).append(":");
96-
lint.addWarningMessageTo(message, stepName, true);
93+
lintMessage.append("\n ").append(relativePath).append(":");
94+
lint.addWarningMessageTo(lintMessage, stepName, true);
95+
totalLintCount++;
9796
}
9897
}
99-
message.append("\n Resolve these lints or suppress with `<lintSuppressions>`");
100-
throw new MojoExecutionException(message.toString());
98+
// don't mark a linting file as up-to-date; it must be revisited next run
99+
continue;
101100
}
102101
} catch (IOException | RuntimeException e) {
103102
throw new MojoExecutionException("Unable to format file " + file, e);
@@ -106,6 +105,14 @@ protected void process(String name, Iterable<File> files, Formatter formatter, U
106105
upToDateChecker.setUpToDate(file.toPath());
107106
}
108107

108+
if (totalLintCount > 0) {
109+
StringBuilder message = new StringBuilder();
110+
message.append("There were ").append(totalLintCount).append(" lint error(s), they must be fixed or suppressed.");
111+
message.append(lintMessage);
112+
message.append("\n Resolve these lints or suppress with `<lintSuppressions>`");
113+
throw new MojoExecutionException(message.toString());
114+
}
115+
109116
// We print the number of considered files which is useful when ratchetFrom is setup
110117
if (counter.getTotal() > 0) {
111118
getLog().info("Spotless.%s is keeping %s files clean - %s were changed to be clean, %s were already clean, %s were skipped because caching determined they were already clean".formatted(
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright 2026 DiffPlug
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+
package com.diffplug.spotless.maven.java;
17+
18+
import static org.assertj.core.api.Assertions.assertThat;
19+
20+
import org.junit.jupiter.api.Test;
21+
22+
import com.diffplug.spotless.ProcessRunner;
23+
import com.diffplug.spotless.maven.MavenIntegrationHarness;
24+
25+
class ForbidWildcardImportsMultiFileStepTest extends MavenIntegrationHarness {
26+
27+
/**
28+
* Regression test: in apply mode a single linting file must not abort processing of the
29+
* remaining files. Lints across all files are collected and the build fails once at the end,
30+
* so every file's lints are reported.
31+
*/
32+
@Test
33+
void testApplyAggregatesLintsAcrossAllFiles() throws Exception {
34+
writePomWithJavaSteps("<forbidWildcardImports/>");
35+
36+
String first = "src/main/java/test1.java";
37+
String second = "src/main/java/test2.java";
38+
setFile(first).toResource("java/forbidwildcardimports/JavaCodeWildcardsUnformatted.test");
39+
setFile(second).toResource("java/forbidwildcardimports/JavaCodeWildcardsUnformatted.test");
40+
41+
ProcessRunner.Result result = mavenRunner().withArguments("spotless:apply").runHasError();
42+
String output = result.stdOutUtf8();
43+
44+
// 5 wildcard imports per file across 2 files = 10, aggregated into a single failure
45+
assertThat(output).contains("There were 10 lint error(s), they must be fixed or suppressed.");
46+
// both files reported -> the loop did NOT abort on the first linting file
47+
assertThat(output).contains(first + ":");
48+
assertThat(output).contains(second + ":");
49+
assertThat(output).contains("Resolve these lints or suppress with `<lintSuppressions>`");
50+
51+
// forbidWildcardImports cannot auto-fix, so both files are left untouched
52+
assertFile(first).sameAsResource("java/forbidwildcardimports/JavaCodeWildcardsUnformatted.test");
53+
assertFile(second).sameAsResource("java/forbidwildcardimports/JavaCodeWildcardsUnformatted.test");
54+
}
55+
}

0 commit comments

Comments
 (0)