From 2e1f6cfa51a2994b7fbd0b20e3f9a06a4cd5bf0a Mon Sep 17 00:00:00 2001 From: Mirko Alicastro Date: Tue, 19 May 2026 22:12:25 +0200 Subject: [PATCH 1/2] Aggregate lint errors across all files in maven spotless:apply --- .../spotless/maven/SpotlessApplyMojo.java | 31 +++++++---- ...orbidWildcardImportsMultiFileStepTest.java | 55 +++++++++++++++++++ 2 files changed, 74 insertions(+), 12 deletions(-) create mode 100644 plugin-maven/src/test/java/com/diffplug/spotless/maven/java/ForbidWildcardImportsMultiFileStepTest.java diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/SpotlessApplyMojo.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/SpotlessApplyMojo.java index 42a9259591..1faf4b1c4e 100644 --- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/SpotlessApplyMojo.java +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/SpotlessApplyMojo.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. @@ -55,6 +55,8 @@ protected void process(String name, Iterable files, Formatter formatter, U } ImpactedFilesTracker counter = new ImpactedFilesTracker(); + int totalLintCount = 0; + StringBuilder lintMessage = new StringBuilder(); for (File file : files) { if (upToDateChecker.isUpToDate(file.toPath())) { @@ -79,25 +81,22 @@ protected void process(String name, Iterable files, Formatter formatter, U counter.checkedButAlreadyClean(); } - // In apply mode, any lints should fail the build (matching Gradle behavior) + // In apply mode, any lints should fail the build (matching Gradle behavior). + // Collect lints across all files and fail once at the end, so a single + // linting file doesn't prevent the remaining files from being formatted. if (hasUnsuppressedLints) { - int lintCount = lintState.getLintsByStep(formatter).values().stream() - .mapToInt(List::size) - .sum(); - StringBuilder message = new StringBuilder(); - message.append("There were ").append(lintCount).append(" lint error(s), they must be fixed or suppressed."); - // Build lint messages in Gradle format (using relative path, not just filename) for (Map.Entry> stepEntry : lintState.getLintsByStep(formatter).entrySet()) { String stepName = stepEntry.getKey(); for (Lint lint : stepEntry.getValue()) { String relativePath = LintSuppression.relativizeAsUnix(baseDir, file); - message.append("\n ").append(relativePath).append(":"); - lint.addWarningMessageTo(message, stepName, true); + lintMessage.append("\n ").append(relativePath).append(":"); + lint.addWarningMessageTo(lintMessage, stepName, true); + totalLintCount++; } } - message.append("\n Resolve these lints or suppress with ``"); - throw new MojoExecutionException(message.toString()); + // don't mark a linting file as up-to-date; it must be revisited next run + continue; } } catch (IOException | RuntimeException e) { throw new MojoExecutionException("Unable to format file " + file, e); @@ -106,6 +105,14 @@ protected void process(String name, Iterable files, Formatter formatter, U upToDateChecker.setUpToDate(file.toPath()); } + if (totalLintCount > 0) { + StringBuilder message = new StringBuilder(); + message.append("There were ").append(totalLintCount).append(" lint error(s), they must be fixed or suppressed."); + message.append(lintMessage); + message.append("\n Resolve these lints or suppress with ``"); + throw new MojoExecutionException(message.toString()); + } + // We print the number of considered files which is useful when ratchetFrom is setup if (counter.getTotal() > 0) { 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( diff --git a/plugin-maven/src/test/java/com/diffplug/spotless/maven/java/ForbidWildcardImportsMultiFileStepTest.java b/plugin-maven/src/test/java/com/diffplug/spotless/maven/java/ForbidWildcardImportsMultiFileStepTest.java new file mode 100644 index 0000000000..784a73db29 --- /dev/null +++ b/plugin-maven/src/test/java/com/diffplug/spotless/maven/java/ForbidWildcardImportsMultiFileStepTest.java @@ -0,0 +1,55 @@ +/* + * Copyright 2026 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.spotless.maven.java; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +import com.diffplug.spotless.ProcessRunner; +import com.diffplug.spotless.maven.MavenIntegrationHarness; + +class ForbidWildcardImportsMultiFileStepTest extends MavenIntegrationHarness { + + /** + * Regression test: in apply mode a single linting file must not abort processing of the + * remaining files. Lints across all files are collected and the build fails once at the end, + * so every file's lints are reported. + */ + @Test + void testApplyAggregatesLintsAcrossAllFiles() throws Exception { + writePomWithJavaSteps(""); + + String first = "src/main/java/test1.java"; + String second = "src/main/java/test2.java"; + setFile(first).toResource("java/forbidwildcardimports/JavaCodeWildcardsUnformatted.test"); + setFile(second).toResource("java/forbidwildcardimports/JavaCodeWildcardsUnformatted.test"); + + ProcessRunner.Result result = mavenRunner().withArguments("spotless:apply").runHasError(); + String output = result.stdOutUtf8(); + + // 5 wildcard imports per file across 2 files = 10, aggregated into a single failure + assertThat(output).contains("There were 10 lint error(s), they must be fixed or suppressed."); + // both files reported -> the loop did NOT abort on the first linting file + assertThat(output).contains(first + ":"); + assertThat(output).contains(second + ":"); + assertThat(output).contains("Resolve these lints or suppress with ``"); + + // forbidWildcardImports cannot auto-fix, so both files are left untouched + assertFile(first).sameAsResource("java/forbidwildcardimports/JavaCodeWildcardsUnformatted.test"); + assertFile(second).sameAsResource("java/forbidwildcardimports/JavaCodeWildcardsUnformatted.test"); + } +} From 9788e40441a10de3e7c495423bc7dfa0f458b833 Mon Sep 17 00:00:00 2001 From: Mirko Alicastro Date: Tue, 19 May 2026 22:25:16 +0200 Subject: [PATCH 2/2] Update changelog --- plugin-maven/CHANGES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugin-maven/CHANGES.md b/plugin-maven/CHANGES.md index d090aefe26..e8db904b02 100644 --- a/plugin-maven/CHANGES.md +++ b/plugin-maven/CHANGES.md @@ -3,6 +3,8 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `1.27.0`). ## [Unreleased] +### Fixed +- `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)) ### Changes - Improved formatting performance by eliminating redundant per-step line-ending normalization in the core formatter loop. ([#2934](https://github.com/diffplug/spotless/pull/2934))