You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Correction: this issue originally claimed Autostyle was unmaintained since 2021. That is wrong — 4.0 was released in January 2024 and 4.0.1 in July 2025, and @vlsi has said he plans to move the Java part to OpenRewrite. So "the formatter is abandoned" is not a reason to migrate. The rest of the issue is kept for the measurements, which are still useful to whoever revisits this.
The build formats Java sources with Autostyle 4.0 (build-logic/code-quality/src/main/kotlin/testng.style.gradle.kts). Swapping it for Spotless was attempted as part of #3291 and backed out, because Spotless is flaky in this build.
With the "unmaintained" premise gone, the remaining options are: stay on Autostyle, follow @vlsi to OpenRewrite, or revisit Spotless once the flakiness below has a fix.
Why the first attempt was reverted
With org.gradle.parallel=true (set in gradle.properties), spotlessJavaCheck fails intermittently — roughly 1 run in 6 of ./gradlew clean build:
Execution failed for task ':testng:spotlessJavaCheck' (registered by plugin 'testng.style').
> There were 1 lint error(s), they must be fixed or suppressed.
src/test/java/org/testng/test/VerifyShadedJarWorksTest.java:LINE_UNDEFINED
removeUnusedImports(java.lang.NoClassDefFoundError)
com/google/googlejavaformat/java/RemoveUnusedImports (...)
The class reported missing differs on every occurrence — com/google/common/collect/Iterators$MergingIterator, ImmutableList$SubList, ImmutableEnumMap, FluentIterable$3, com/google/common/base/Predicate, and google-java-format's own RemoveUnusedImports. That points at a formatter classloader being closed while still in use, rather than at any single misbehaving step.
What was measured
12 runs of ./gradlew clean build -x test per configuration, on Gradle 9.6.1 / JDK 25 / Spotless 8.8.0:
Spotless, without the root-project apply false declaration
12 / 12
Spotless, tasks serialized through a maxParallelUsages = 1 build service
6 / 12
Autostyle (current state)
0 / 10
Two results are worth keeping in mind:
Removing the root id("com.diffplug.spotless") apply false makes it fail every time. The plugin also reaches the build via api("com.diffplug.spotless:spotless-plugin-gradle") in build-logic/code-quality, so the two entry points appear to matter. Switching that api to compileOnly does not work — the precompiled script plugin then cannot resolve com.diffplug.spotless at apply time.
Serializing the tasks with a shared build service made things worse, not better, which argues against a simple task-level race.
Spotless 8.8.0 was the latest release at the time, so there was no upstream fix to pick up.
Possible directions
Apply Spotless from the root project only, targeting **/src/**/*.java, so there is a single set of formatter tasks and no cross-project concurrency. Untested. Costs per-project incrementality and changes how check is wired.
Report upstream to diffplug/spotless with the reproducer above — a classloader closed mid-use across parallel projects looks like a genuine bug.
Wait for a newer Spotless and re-measure with the table above.
Stay on Autostyle. It works, and the usual argument against it does not currently bite: Gradle 9 keeps the configuration cache off by default, and Spotless' removeUnusedImports is not configuration-cache compatible either, so the swap would not unlock it.
Separate finding: Autostyle 4.0.1 does not run on JDK 25
The build is pinned to Autostyle 4.0 on purpose. 4.0.1 fails every module on JDK 25:
Execution failed for task ':testng-reflection-utils:autostyleJavaProcess'
> Error while formatting .../ReflectionHelper.java: class
com.google.googlejavaformat.java.RemoveUnusedImports (in unnamed module @0x2f5f0157)
cannot access class com.sun.tools.javac.file.JavacFileManager (in module jdk.compiler)
because module jdk.compiler does not export com.sun.tools.javac.file to unnamed module
google-java-format needs --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED on JDK 16+, and 4.0.1 does not appear to pass it. 4.0 is unaffected. Worth reporting upstream before anyone bumps the version.
Notes for whoever picks this up
The migration itself is small: swap the plugin id and dependency, rename the skipAutostyle build parameter to skipSpotless, and update .github/CONTRIBUTING.md, .github/PULL_REQUEST_TEMPLATE.md, docs/BUILD_SYSTEM.md and docs/README.md from autostyleApply/autostyleCheck to spotlessApply/spotlessCheck.
Expect a second, separate commit of pure reformatting: the google-java-format version bundled with Spotless reflows javadoc and method chains differently from the one Autostyle used. Last time that was 86 files, +829/-281, with no semantic change. Keeping it apart from the configuration change makes review possible.
Whatever the fix, please validate it by running ./gradlew clean build -x test at least a dozen times in a row. A single green run proves nothing here.
Context
The build formats Java sources with Autostyle 4.0 (
build-logic/code-quality/src/main/kotlin/testng.style.gradle.kts). Swapping it for Spotless was attempted as part of #3291 and backed out, because Spotless is flaky in this build.With the "unmaintained" premise gone, the remaining options are: stay on Autostyle, follow @vlsi to OpenRewrite, or revisit Spotless once the flakiness below has a fix.
Why the first attempt was reverted
With
org.gradle.parallel=true(set ingradle.properties),spotlessJavaCheckfails intermittently — roughly 1 run in 6 of./gradlew clean build:The class reported missing differs on every occurrence —
com/google/common/collect/Iterators$MergingIterator,ImmutableList$SubList,ImmutableEnumMap,FluentIterable$3,com/google/common/base/Predicate, and google-java-format's ownRemoveUnusedImports. That points at a formatter classloader being closed while still in use, rather than at any single misbehaving step.What was measured
12 runs of
./gradlew clean build -x testper configuration, on Gradle 9.6.1 / JDK 25 / Spotless 8.8.0:--no-parallelremoveUnusedImports("cleanthat-javaparser-unnecessaryimport")removeUnusedImports()apply falsedeclarationmaxParallelUsages = 1build serviceTwo results are worth keeping in mind:
id("com.diffplug.spotless") apply falsemakes it fail every time. The plugin also reaches the build viaapi("com.diffplug.spotless:spotless-plugin-gradle")inbuild-logic/code-quality, so the two entry points appear to matter. Switching thatapitocompileOnlydoes not work — the precompiled script plugin then cannot resolvecom.diffplug.spotlessat apply time.Spotless 8.8.0 was the latest release at the time, so there was no upstream fix to pick up.
Possible directions
**/src/**/*.java, so there is a single set of formatter tasks and no cross-project concurrency. Untested. Costs per-project incrementality and changes howcheckis wired.removeUnusedImportsis not configuration-cache compatible either, so the swap would not unlock it.Separate finding: Autostyle 4.0.1 does not run on JDK 25
The build is pinned to Autostyle 4.0 on purpose. 4.0.1 fails every module on JDK 25:
google-java-format needs
--add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMEDon JDK 16+, and 4.0.1 does not appear to pass it. 4.0 is unaffected. Worth reporting upstream before anyone bumps the version.Notes for whoever picks this up
skipAutostylebuild parameter toskipSpotless, and update.github/CONTRIBUTING.md,.github/PULL_REQUEST_TEMPLATE.md,docs/BUILD_SYSTEM.mdanddocs/README.mdfromautostyleApply/autostyleChecktospotlessApply/spotlessCheck../gradlew clean build -x testat least a dozen times in a row. A single green run proves nothing here.