diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dba0ec1d89..b9bd8e1575 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,7 +5,7 @@ on: pull_request: push: - branches: [main] + branches: [main, release] workflow_dispatch: concurrency: diff --git a/CHANGES.md b/CHANGES.md index f4ac244ba4..c708257d88 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,6 +10,8 @@ This document is intended for Spotless developers. We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `1.27.0`). ## [Unreleased] +### Fixed +* `GitPrePushHookInstaller` didn't work on windows, now fixed. ([#2562](https://github.com/diffplug/spotless/pull/2562)) ## [3.3.0] - 2025-07-20 ### Added diff --git a/lib/src/main/java/com/diffplug/spotless/GitPrePushHookInstaller.java b/lib/src/main/java/com/diffplug/spotless/GitPrePushHookInstaller.java index 167e089b7d..7a627a070c 100644 --- a/lib/src/main/java/com/diffplug/spotless/GitPrePushHookInstaller.java +++ b/lib/src/main/java/com/diffplug/spotless/GitPrePushHookInstaller.java @@ -22,6 +22,7 @@ import java.io.FileWriter; import java.io.IOException; import java.nio.file.Files; +import java.util.Locale; /** * Abstract class responsible for installing a Git pre-push hook in a repository. @@ -192,12 +193,12 @@ private void uninstall(File gitHookFile) throws Exception { * @param commandApply The command to apply corrections. * @return A string template representing the Spotless Git pre-push hook content. */ - protected String preHookTemplate(String executor, String commandCheck, String commandApply) { + protected String preHookTemplate(Executor executor, String commandCheck, String commandApply) { var spotlessHook = ""; spotlessHook += "\n"; spotlessHook += "\n" + HOOK_HEADER; - spotlessHook += "\nSPOTLESS_EXECUTOR=" + executor; + spotlessHook += "\nSPOTLESS_EXECUTOR=" + executorPath(executor); spotlessHook += "\nif ! $SPOTLESS_EXECUTOR " + commandCheck + " ; then"; spotlessHook += "\n echo 1>&2 \"spotless found problems, running " + commandApply + "; commit the result and re-push\""; spotlessHook += "\n $SPOTLESS_EXECUTOR " + commandApply; @@ -209,6 +210,58 @@ protected String preHookTemplate(String executor, String commandCheck, String co return spotlessHook; } + /** + * Determines the path to the build tool executor (Maven or Gradle). + * This method first checks for the existence of a wrapper script in the project root. + * If the wrapper exists, returns a relative path to it, otherwise returns the global command. + * + * @param executor The build tool executor (GRADLE or MAVEN) + * @return The path to the executor - either the wrapper script path (e.g., "./gradlew") + * or the global command (e.g., "gradle") + */ + private String executorPath(Executor executor) { + final var wrapper = executorWrapperFile(executor); + if (wrapper.exists()) { + return "./" + wrapper.getName(); + } + + logger.info("Local %s wrapper (%s) not found, falling back to global command '%s'", + executor.name().toLowerCase(Locale.ROOT), executor.wrapper, executor.global); + + return executor.global; + } + + /** + * Resolves the wrapper script file for the specified build tool executor. + * On Windows systems, checks for both .bat and .cmd extensions. + * On non-Windows systems, uses the wrapper name without extension. + * + * @param executor The build tool executor (GRADLE or MAVEN) + * @return The File object representing the wrapper script + */ + private File executorWrapperFile(Executor executor) { + if (isWindows()) { + final var bat = root.toPath().resolve(executor.wrapper + ".bat").toFile(); + if (bat.exists()) { + return bat; + } + + return root.toPath().resolve(executor.wrapper + ".cmd").toFile(); + } + + return root.toPath().resolve(executor.wrapper).toFile(); + } + + /** + * Checks if the current operating system is Windows. + * This is determined by checking if the "os.name" system property contains "win". + * + * @return true if the current OS is Windows, false otherwise + */ + private boolean isWindows() { + return System.getProperty("os.name").toLowerCase(Locale.ROOT).startsWith("win"); + } + /** * Checks if Git is installed by validating the existence of `.git/config` in the repository root. * @@ -243,6 +296,18 @@ private void writeFile(File file, String content, boolean append) throws IOExcep } } + public enum Executor { + GRADLE("gradlew", "gradle"), MAVEN("mvnw", "mvn"),; + + public final String wrapper; + public final String global; + + Executor(String wrapper, String global) { + this.wrapper = wrapper; + this.global = global; + } + } + public interface GitPreHookLogger { void info(String format, Object... arguments); diff --git a/lib/src/main/java/com/diffplug/spotless/GitPrePushHookInstallerGradle.java b/lib/src/main/java/com/diffplug/spotless/GitPrePushHookInstallerGradle.java index 0b11db5cab..3cc61e566d 100644 --- a/lib/src/main/java/com/diffplug/spotless/GitPrePushHookInstallerGradle.java +++ b/lib/src/main/java/com/diffplug/spotless/GitPrePushHookInstallerGradle.java @@ -15,6 +15,8 @@ */ package com.diffplug.spotless; +import static com.diffplug.spotless.GitPrePushHookInstaller.Executor.GRADLE; + import java.io.File; /** @@ -23,14 +25,8 @@ */ public class GitPrePushHookInstallerGradle extends GitPrePushHookInstaller { - /** - * The Gradle wrapper file (`gradlew`) located in the root directory of the project. - */ - private final File gradlew; - public GitPrePushHookInstallerGradle(GitPreHookLogger logger, File root) { super(logger, root); - this.gradlew = root.toPath().resolve("gradlew").toFile(); } /** @@ -38,15 +34,6 @@ public GitPrePushHookInstallerGradle(GitPreHookLogger logger, File root) { */ @Override protected String preHookContent() { - return preHookTemplate(executorPath(), "spotlessCheck", "spotlessApply"); - } - - private String executorPath() { - if (gradlew.exists()) { - return gradlew.getAbsolutePath(); - } - - logger.info("Gradle wrapper is not installed, using global gradle"); - return "gradle"; + return preHookTemplate(GRADLE, "spotlessCheck", "spotlessApply"); } } diff --git a/lib/src/main/java/com/diffplug/spotless/GitPrePushHookInstallerMaven.java b/lib/src/main/java/com/diffplug/spotless/GitPrePushHookInstallerMaven.java index 138d27f9a3..f8b3943335 100644 --- a/lib/src/main/java/com/diffplug/spotless/GitPrePushHookInstallerMaven.java +++ b/lib/src/main/java/com/diffplug/spotless/GitPrePushHookInstallerMaven.java @@ -15,6 +15,8 @@ */ package com.diffplug.spotless; +import static com.diffplug.spotless.GitPrePushHookInstaller.Executor.MAVEN; + import java.io.File; /** @@ -23,11 +25,8 @@ */ public class GitPrePushHookInstallerMaven extends GitPrePushHookInstaller { - private final File mvnw; - public GitPrePushHookInstallerMaven(GitPreHookLogger logger, File root) { super(logger, root); - this.mvnw = root.toPath().resolve("mvnw").toFile(); } /** @@ -35,15 +34,6 @@ public GitPrePushHookInstallerMaven(GitPreHookLogger logger, File root) { */ @Override protected String preHookContent() { - return preHookTemplate(executorPath(), "spotless:check", "spotless:apply"); - } - - private String executorPath() { - if (mvnw.exists()) { - return mvnw.getAbsolutePath(); - } - - logger.info("Maven wrapper is not installed, using global maven"); - return "mvn"; + return preHookTemplate(MAVEN, "spotless:check", "spotless:apply"); } } diff --git a/plugin-gradle/CHANGES.md b/plugin-gradle/CHANGES.md index 0d89813e56..911c0f8717 100644 --- a/plugin-gradle/CHANGES.md +++ b/plugin-gradle/CHANGES.md @@ -3,6 +3,8 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `3.27.0`). ## [Unreleased] +### Fixed +* `spotlessInstallGitPrePushHook` didn't work on windows, now fixed. ([#2562](https://github.com/diffplug/spotless/pull/2562)) ## [7.2.0] - 2025-07-20 ### Added diff --git a/plugin-maven/CHANGES.md b/plugin-maven/CHANGES.md index b91be43d79..e1a8ac602b 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:install-git-pre-push-hook` didn't work on windows, now fixed. ([#2562](https://github.com/diffplug/spotless/pull/2562)) ## [2.46.0] - 2025-07-20 ### Added diff --git a/plugin-maven/src/test/java/com/diffplug/spotless/maven/MavenRunner.java b/plugin-maven/src/test/java/com/diffplug/spotless/maven/MavenRunner.java index 6e22fe5e31..11d52abc92 100644 --- a/plugin-maven/src/test/java/com/diffplug/spotless/maven/MavenRunner.java +++ b/plugin-maven/src/test/java/com/diffplug/spotless/maven/MavenRunner.java @@ -56,6 +56,11 @@ public MavenRunner withArguments(String... args) { return this; } + public MavenRunner withEnvironment(String key, String value) { + environment.put(key, value); + return this; + } + public MavenRunner withRunner(ProcessRunner runner) { this.runner = runner; return this; diff --git a/plugin-maven/src/test/java/com/diffplug/spotless/maven/SpotlessInstallPrePushHookMojoTest.java b/plugin-maven/src/test/java/com/diffplug/spotless/maven/SpotlessInstallPrePushHookMojoTest.java index ebfc13d982..dbbb4b1524 100644 --- a/plugin-maven/src/test/java/com/diffplug/spotless/maven/SpotlessInstallPrePushHookMojoTest.java +++ b/plugin-maven/src/test/java/com/diffplug/spotless/maven/SpotlessInstallPrePushHookMojoTest.java @@ -17,61 +17,80 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.io.File; import java.io.IOException; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; import org.junit.jupiter.api.Test; +import com.diffplug.spotless.ProcessRunner; + class SpotlessInstallPrePushHookMojoTest extends MavenIntegrationHarness { @Test public void should_create_pre_hook_file_when_hook_file_does_not_exists() throws Exception { // given setFile(".git/config").toContent(""); - setFile("license.txt").toResource("license/TestLicense"); + setFile("license.txt").toResource("git_pre_hook/TestLicense.txt"); writePomWithJavaLicenseHeaderStep(); // when - final var output = mavenRunner() - .withArguments("spotless:install-git-pre-push-hook") - .runNoError() - .stdOutUtf8(); + final var output = runMaven("spotless:install-git-pre-push-hook"); // then assertThat(output).contains("Installing git pre-push hook"); assertThat(output).contains("Git pre-push hook not found, creating it"); assertThat(output).contains("Git pre-push hook installed successfully to the file " + newFile(".git/hooks/pre-push")); - final var content = getTestResource("git_pre_hook/pre-push.created-tpl") - .replace("${executor}", newFile("mvnw").getAbsolutePath()) - .replace("${checkCommand}", "spotless:check") - .replace("${applyCommand}", "spotless:apply"); - assertFile(".git/hooks/pre-push").hasContent(content); + final var hookContent = getHookContent("git_pre_hook/pre-push.created-tpl"); + assertFile(".git/hooks/pre-push").hasContent(hookContent); } @Test public void should_append_to_existing_pre_hook_file_when_hook_file_exists() throws Exception { // given setFile(".git/config").toContent(""); - setFile("license.txt").toResource("license/TestLicense"); + setFile("license.txt").toResource("git_pre_hook/TestLicense.txt"); setFile(".git/hooks/pre-push").toResource("git_pre_hook/pre-push.existing"); writePomWithJavaLicenseHeaderStep(); // when - final var output = mavenRunner() - .withArguments("spotless:install-git-pre-push-hook") - .runNoError() - .stdOutUtf8(); + final var output = runMaven("spotless:install-git-pre-push-hook"); // then assertThat(output).contains("Installing git pre-push hook"); assertThat(output).contains("Git pre-push hook installed successfully to the file " + newFile(".git/hooks/pre-push")); - final var content = getTestResource("git_pre_hook/pre-push.existing-installed-end-tpl") - .replace("${executor}", newFile("mvnw").getAbsolutePath()) - .replace("${checkCommand}", "spotless:check") - .replace("${applyCommand}", "spotless:apply"); - assertFile(".git/hooks/pre-push").hasContent(content); + final var hookContent = getHookContent("git_pre_hook/pre-push.existing-installed-end-tpl"); + assertFile(".git/hooks/pre-push").hasContent(hookContent); + } + + @Test + public void should_execute_pre_push_script() throws Exception { + // given + setFile("license.txt").toResource("git_pre_hook/TestLicense.txt"); + setFile(".git/config").toContent(""); + setFile("src/main/java/com.github.youribonnaffe.gradle.format/Java8Test.java").toResource("git_pre_hook/MissingLicense.test"); + writePomWithJavaLicenseHeaderStep(); + + // when + // install pre-hook + final var output = runMaven("spotless:install-git-pre-push-hook"); + + final var result = executeHookScript(".git/hooks/pre-push"); + + // then + assertThat(output).contains("Git pre-push hook installed successfully to the file " + newFile(".git/hooks/pre-push")); + + assertThat(result.stdErrUtf8()).startsWith("spotless found problems, running spotless:apply; commit the result and re-push"); + assertThat(result.exitCode()).isEqualTo(1); + + final var fileContent = read("src/main/java/com.github.youribonnaffe.gradle.format/Java8Test.java"); + assertThat(fileContent).startsWith("this is a test license!\n"); } private void writePomWithJavaLicenseHeaderStep() throws IOException { @@ -80,4 +99,83 @@ private void writePomWithJavaLicenseHeaderStep() throws IOException { " ${basedir}/license.txt", ""); } + + private String getHookContent(String resourceFile) { + final var executorFile = executorWrapperFile(); + final var executorPath = executorFile.exists() ? executorFile.getName() : "mvn"; + return getTestResource(resourceFile) + .replace("${executor}", "./" + executorPath) + .replace("${checkCommand}", "spotless:check") + .replace("${applyCommand}", "spotless:apply"); + } + + private boolean isWindows() { + return System.getProperty("os.name").toLowerCase(Locale.ROOT).startsWith("win"); + } + + private File executorWrapperFile() { + if (isWindows()) { + final var bat = newFile("mvnw.bat"); + if (bat.exists()) { + return bat; + } + + return newFile("mvnw.cmd"); + } + + return newFile("mvnw"); + } + + private String runMaven(String command) throws Exception { + return mavenRunner() + .withArguments(command) + .withEnvironment("JAVA_HOME", System.getProperty("java.home")) + .runNoError() + .stdOutUtf8(); + } + + private ProcessRunner.Result executeHookScript(String hookFile) throws Exception { + try (final var runner = new ProcessRunner()) { + String executor = "sh"; + if (isWindows()) { + final var bashPath = findGitBashExecutable(); + if (bashPath.isEmpty()) { + throw new RuntimeException("Could not find git bash executable"); + } + + executor = bashPath.get(); + } + + return runner.exec(rootFolder(), Map.of("JAVA_HOME", System.getProperty("java.home")), null, List.of(executor, hookFile)); + } + } + + private Optional findGitBashExecutable() { + // 1. Check environment variable + final var envPath = System.getenv("GIT_BASH"); + if (envPath != null && new File(envPath).exists()) { + return Optional.of(envPath); + } + + // 2. Check common install paths + final var commonPaths = List.of( + "C:\\Program Files\\Git\\bin\\bash.exe", + "C:\\Program Files (x86)\\Git\\bin\\bash.exe", + "C:\\Program Files\\Git\\usr\\bin\\bash.exe"); + + for (var path : commonPaths) { + if (new File(path).exists()) { + return Optional.of(path); + } + } + + // 3. Try bash from PATH + try { + Process process = new ProcessBuilder("bash", "--version").start(); + process.waitFor(); + return Optional.of("bash"); // just use "bash" + } catch (Exception e) { + return Optional.empty(); + } + } } diff --git a/testlib/src/main/resources/git_pre_hook/MissingLicense.test b/testlib/src/main/resources/git_pre_hook/MissingLicense.test new file mode 100644 index 0000000000..c81a2412b7 --- /dev/null +++ b/testlib/src/main/resources/git_pre_hook/MissingLicense.test @@ -0,0 +1,28 @@ +package com.github.youribonnaffe.gradle.format; + +import java.util.function.Function; + + +public class Java8Test { + public void doStuff() throws Exception { + Function example = Integer::parseInt; + example.andThen(val -> { + return val + 2; + } ); + SimpleEnum val = SimpleEnum.A; + switch (val) { + case A: + break; + case B: + break; + case C: + break; + default: + throw new Exception(); + } + } + + public enum SimpleEnum { + A, B, C; + } +} \ No newline at end of file diff --git a/testlib/src/main/resources/git_pre_hook/TestLicense.txt b/testlib/src/main/resources/git_pre_hook/TestLicense.txt new file mode 100644 index 0000000000..2c4c9a3186 --- /dev/null +++ b/testlib/src/main/resources/git_pre_hook/TestLicense.txt @@ -0,0 +1 @@ +this is a test license! \ No newline at end of file diff --git a/testlib/src/test/java/com/diffplug/spotless/GitPrePushHookInstallerTest.java b/testlib/src/test/java/com/diffplug/spotless/GitPrePushHookInstallerTest.java index 6167e26b8c..0ac3fa2c1c 100644 --- a/testlib/src/test/java/com/diffplug/spotless/GitPrePushHookInstallerTest.java +++ b/testlib/src/test/java/com/diffplug/spotless/GitPrePushHookInstallerTest.java @@ -15,16 +15,22 @@ */ package com.diffplug.spotless; +import static com.diffplug.spotless.GitPrePushHookInstaller.Executor.GRADLE; +import static com.diffplug.spotless.GitPrePushHookInstaller.Executor.MAVEN; import static org.assertj.core.api.Assertions.assertThat; import java.util.ArrayList; import java.util.List; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import com.diffplug.spotless.GitPrePushHookInstaller.GitPreHookLogger; class GitPrePushHookInstallerTest extends ResourceHarness { + private final static String OS = System.getProperty("os.name"); + private final List logs = new ArrayList<>(); private final GitPreHookLogger logger = new GitPreHookLogger() { @Override @@ -43,6 +49,16 @@ public void error(String format, Object... arguments) { } }; + @BeforeEach + public void beforeEach() { + System.setProperty("os.name", "linux"); + } + + @AfterEach + public void afterEach() { + System.setProperty("os.name", OS); + } + @Test public void should_not_create_pre_hook_file_when_git_is_not_installed() throws Exception { // given @@ -71,7 +87,7 @@ public void should_use_global_gradle_when_gradlew_is_not_installed() throws Exce assertThat(logs).hasSize(4); assertThat(logs).element(0).isEqualTo("Installing git pre-push hook"); assertThat(logs).element(1).isEqualTo("Git pre-push hook not found, creating it"); - assertThat(logs).element(2).isEqualTo("Gradle wrapper is not installed, using global gradle"); + assertThat(logs).element(2).isEqualTo("Local gradle wrapper (gradlew) not found, falling back to global command 'gradle'"); assertThat(logs).element(3).isEqualTo("Git pre-push hook installed successfully to the file " + newFile(".git/hooks/pre-push").getAbsolutePath()); final var content = gradleHookContent("git_pre_hook/pre-push.created-tpl", ExecutorType.GLOBAL); @@ -217,23 +233,117 @@ public void should_use_global_maven_when_maven_wrapper_is_not_installed() throws assertThat(logs).hasSize(4); assertThat(logs).element(0).isEqualTo("Installing git pre-push hook"); assertThat(logs).element(1).isEqualTo("Git pre-push hook not found, creating it"); - assertThat(logs).element(2).isEqualTo("Maven wrapper is not installed, using global maven"); + assertThat(logs).element(2).isEqualTo("Local maven wrapper (mvnw) not found, falling back to global command 'mvn'"); assertThat(logs).element(3).isEqualTo("Git pre-push hook installed successfully to the file " + newFile(".git/hooks/pre-push").getAbsolutePath()); final var content = mavenHookContent("git_pre_hook/pre-push.created-tpl", ExecutorType.GLOBAL); assertFile(".git/hooks/pre-push").hasContent(content); } + @Test + public void should_use_maven_bat_wrapper_when_exists_for_windows() { + // given + System.setProperty("os.name", "Windows 10"); + setFile("mvnw.bat").toContent(""); + setFile("mvnw.cmd").toContent(""); + + final var gradle = new GitPrePushHookInstallerMaven(logger, rootFolder()); + + // when + final var hook = gradle.preHookTemplate(MAVEN, "spotless:check", "spotless:apply"); + + // then + assertThat(hook).contains("SPOTLESS_EXECUTOR=./mvnw.bat"); + } + + @Test + public void should_use_maven_cmd_wrapper_when_exists_for_windows() { + // given + System.setProperty("os.name", "Windows 10"); + setFile("mvnw.cmd").toContent(""); + + final var gradle = new GitPrePushHookInstallerMaven(logger, rootFolder()); + + // when + final var hook = gradle.preHookTemplate(MAVEN, "spotless:check", "spotless:apply"); + + // then + assertThat(hook).contains("SPOTLESS_EXECUTOR=./mvnw.cmd"); + } + + @Test + public void should_use_maven_global_when_bat_and_cmd_files_not_exists_for_windows() { + // given + System.setProperty("os.name", "Windows 10"); + setFile("mvnw").toContent(""); + + final var gradle = new GitPrePushHookInstallerMaven(logger, rootFolder()); + + // when + final var hook = gradle.preHookTemplate(MAVEN, "spotless:check", "spotless:apply"); + + // then + assertThat(hook).contains("SPOTLESS_EXECUTOR=mvn"); + } + + @Test + public void should_use_gradle_bat_wrapper_when_exists_for_windows() { + // given + System.setProperty("os.name", "Windows 10"); + setFile("gradlew.bat").toContent(""); + setFile("gradlew.cmd").toContent(""); + setFile("gradlew").toContent(""); + + final var gradle = new GitPrePushHookInstallerMaven(logger, rootFolder()); + + // when + final var hook = gradle.preHookTemplate(GRADLE, "spotlessCheck", "spotlessApply"); + + // then + assertThat(hook).contains("SPOTLESS_EXECUTOR=./gradlew.bat"); + } + + @Test + public void should_use_gradle_cmd_wrapper_when_exists_for_windows() { + // given + System.setProperty("os.name", "Windows 10"); + setFile("gradlew.cmd").toContent(""); + setFile("gradlew").toContent(""); + + final var gradle = new GitPrePushHookInstallerMaven(logger, rootFolder()); + + // when + final var hook = gradle.preHookTemplate(GRADLE, "spotlessCheck", "spotlessApply"); + + // then + assertThat(hook).contains("SPOTLESS_EXECUTOR=./gradlew.cmd"); + } + + @Test + public void should_use_gradle_global_when_bat_and_cmd_files_not_exists_for_windows() { + // given + System.setProperty("os.name", "Windows 10"); + setFile("gradlew").toContent(""); + + final var gradle = new GitPrePushHookInstallerMaven(logger, rootFolder()); + + // when + final var hook = gradle.preHookTemplate(GRADLE, "spotlessCheck", "spotlessApply"); + + // then + assertThat(hook).contains("SPOTLESS_EXECUTOR=gradle"); + } + private String gradleHookContent(String resourcePath, ExecutorType executorType) { return getTestResource(resourcePath) - .replace("${executor}", executorType == ExecutorType.WRAPPER ? newFile("gradlew").getAbsolutePath() : "gradle") + .replace("${executor}", executorType == ExecutorType.WRAPPER ? "./" + newFile("gradlew").getName() : "gradle") .replace("${checkCommand}", "spotlessCheck") .replace("${applyCommand}", "spotlessApply"); } private String mavenHookContent(String resourcePath, ExecutorType executorType) { return getTestResource(resourcePath) - .replace("${executor}", executorType == ExecutorType.WRAPPER ? newFile("mvnw").getAbsolutePath() : "mvn") + .replace("${executor}", executorType == ExecutorType.WRAPPER ? "./" + newFile("mvnw").getName() : "mvn") .replace("${checkCommand}", "spotless:check") .replace("${applyCommand}", "spotless:apply"); }