Add Rewrite Plugin Invoker for Gradle and Maven

This commit is contained in:
Fabian Krüger
2024-03-18 10:37:52 +01:00
committed by GitHub
parent a900ed4ab2
commit f07296d005
69 changed files with 3333 additions and 794 deletions

18
pom.xml
View File

@@ -17,7 +17,7 @@
<module>spring-rewrite-commons-examples</module>
<module>spring-rewrite-commons-starters</module>
<module>spring-rewrite-commons-gradle</module>
<module>spring-rewrite-commons-maven-invoker</module>
<module>spring-rewrite-commons-plugin-invoker</module>
</modules>
<organization>
@@ -37,13 +37,15 @@
<rewrite-polyglot.version>1.8.11</rewrite-polyglot.version>
<rewrite-maven-plugin.version>5.20.0</rewrite-maven-plugin.version>
<jaxb-api.version>2.3.1</jaxb-api.version>
<gradle-tooling-api.version>8.4</gradle-tooling-api.version>
<maven.version>3.9.1</maven.version>
<maven-invoker.version>3.2.0</maven-invoker.version>
<maven-shared-utils.version>3.4.2</maven-shared-utils.version>
<!-- testing dependencies -->
<maven.version>3.9.1</maven.version>
<maven-resolver.version>1.9.13</maven-resolver.version>
<maven-wagon-http.version>3.5.3</maven-wagon-http.version>
<plexus-cypher.version>1.8</plexus-cypher.version>
<maven-invoker.version>3.2.0</maven-invoker.version>
<junit-pioneer.version>2.1.0</junit-pioneer.version>
<!-- documentation dependencies -->
@@ -117,6 +119,16 @@
<version>${junit-pioneer.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.gradle</groupId>
<artifactId>gradle-tooling-api</artifactId>
<version>${gradle-tooling-api.version}</version>
</dependency>
<dependency>
<groupId>org.openrewrite</groupId>
<artifactId>rewrite-core</artifactId>
<version>${rewrite.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

View File

@@ -9,16 +9,15 @@ Fabian Krüger
== Overview
Spring Rewrite Commons provides abstractions and parsers for https://github.com/openrewrite/[OpenRewrite].
The project can parse Java projects using Maven and apply OpenRewrite recipes without the need of a build tool plugin.
It aims to become the foundation for Spring Boot applications using OpenRewrite.
https://github.com/openrewrite/[OpenRewrite] is a code refactoring, remediation, and modernization automation tool providing a rich https://docs.openrewrite.org/recipes[catalog of recipes] for tasks like automated code transformations.
https://github.com/openrewrite/[OpenRewrite] is a code refactoring, remediation, and modernization automation tool providing a rich https://docs.openrewrite.org/recipes[catalog of recipes] for task like automated code transformations.
These are the main components offered by Spring Rewrite Commons:
* `xref:concepts.adoc#_rewriteprojectparser[RewriteProjectParser]` - Parse a project to an OpenRewrite https://docs.openrewrite.org/concepts-explanations/lossless-semantic-trees[Lossless Semantic Tree] (LST).
* `xref:concepts.adoc#_recipediscovery[RewriteRecipeDiscovery]` - Discover recipes on the classpath.
* `xref:concepts.adoc#:_plugininvoker[RewritePlugin]` - Helper to execute OpenRewrite's Gradle or Maven plugin through Java
* `xref:concepts.adoc#_rewriteprojectparser[RewriteProjectParser]` - Parse a project to an OpenRewrite https://docs.openrewrite.org/concepts-explanations/lossless-semantic-trees[LST] outside a build tool plugin.
* `xref:concepts.adoc#_projectresourceset[ProjectResourceSet]` - Encapsulates the LST to run https://github.com/openrewrite/[OpenRewrite] recipes sequentially.
* `xref:concepts.adoc#_recipediscovery[RewriteRecipeDiscovery]` - Discover recipes on the classpath.
* `xref:concepts.adoc#_projectresourcesetserializer[ProjectResourceSetSerializer]` - Serialize the current state of the `ProjectResourceSet` to the filesystem.
* Some helpers for xref:testing.adoc[testíng] also exist

View File

@@ -149,6 +149,18 @@
<version>${maven-invoker.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.rewrite</groupId>
<artifactId>spring-rewrite-commons-plugin-invoker-maven</artifactId>
<version>0.1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.rewrite</groupId>
<artifactId>spring-rewrite-commons-plugin-invoker-gradle</artifactId>
<version>0.1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>

View File

@@ -1,379 +0,0 @@
/*
* Copyright 2021 - 2023 the original author or authors.
*
* 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
*
* https://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 org.springframework.rewrite.maven;
import org.apache.maven.shared.invoker.*;
import javax.swing.text.html.Option;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;
/**
* Executes OpenRewrite recipes by invoking local Maven installation. It requires
* MAVEN_HOME or M2_HOME to be set!
*
* @author Fabian Krüger
*/
public class MavenInvocationRewriteRecipeLauncher {
private MavenInvocationRequestFactory mavenInvocationRequestFactory = new MavenInvocationRequestFactory();
// private Invoker invoker = new DefaultInvoker();
public static Builders.OnDirBuilder applyRecipes(String... recipeNames) {
MavenInvokerRewriteRecipeLauncherBuilder builders = new MavenInvokerRewriteRecipeLauncherBuilder();
return builders.applyRecipes(recipeNames);
}
public static Builders.OnDirBuilder applyRecipes(List<String> recipeNames) {
MavenInvokerRewriteRecipeLauncherBuilder builders = new MavenInvokerRewriteRecipeLauncherBuilder();
return builders.applyRecipes(recipeNames);
}
private InvocationResult invokeRewritePlugin(Invoker invoker, Path baseDir, List<String> recipeNames,
List<String> dependencies, DebugConfig debugConfig, BuildConfig buildConfig, Consumer<String> lineConsumer,
RewritePluginGoals rewritePluginGoals) {
validateBaseDir(baseDir);
if (recipeNames.isEmpty()) {
throw new IllegalArgumentException("No recipe names provided.");
}
String openRewriteCommand = renderOpenRewriteCommand(recipeNames, dependencies, rewritePluginGoals);
return invokeMaven(invoker, baseDir, debugConfig, buildConfig, lineConsumer, openRewriteCommand);
}
public InvocationResult invokeRewritePlugin(Path baseDir, List<String> recipeNames, List<String> dependencies,
DebugConfig debugConfig, BuildConfig buildConfig, Consumer<String> lineConsumer,
RewritePluginGoals rewritePluginGoals) {
validateBaseDir(baseDir);
if (recipeNames.isEmpty()) {
throw new IllegalArgumentException("No recipe names provided.");
}
String openRewriteCommand = renderOpenRewriteCommand(recipeNames, dependencies, rewritePluginGoals);
return invokeMaven(new DefaultInvoker(), baseDir, debugConfig, buildConfig, lineConsumer, openRewriteCommand);
}
private void validateBaseDir(Path baseDir) {
if (baseDir == null) {
throw new IllegalArgumentException("Given baseDir was null.");
}
else if (!Files.exists(baseDir)) {
throw new IllegalArgumentException("Given baseDir '%s' does not exist.".formatted(baseDir));
}
}
private InvocationResult invokeMaven(Invoker invoker, Path baseDir, DebugConfig debugConfig,
BuildConfig buildConfig, Consumer<String> lineConsumer, String openRewriteCommand) {
List<String> goals = new ArrayList<>();
goals.add("clean");
goals.add("package");
goals.add("--fail-at-end");
goals.add(openRewriteCommand);
InvocationRequest request = mavenInvocationRequestFactory.createMavenInvocationRequest(baseDir, debugConfig,
buildConfig, goals, lineConsumer);
try {
return invoker.execute(request);
}
catch (MavenInvocationException e) {
throw new RuntimeException(e);
}
}
// public static InvocationResult invokeMaven(Path baseDir, BuildConfig buildConfig,
// DebugConfig debugConfig,
// Consumer<String> lineConsumer, List<String> providedGoals) {
// List<String> goals = new ArrayList<>(providedGoals);
//
// InvocationRequest mavenInvocationRequest = new
// MavenInvocationRequestFactory().createMavenInvocationRequest(baseDir, debugConfig,
// buildConfig, providedGoals, lineConsumer);
//
// String mavenHome = System.getenv("MAVEN_HOME");
// if (mavenHome == null) {
// mavenHome = System.getenv("M2_HOME");
// if (mavenHome == null) {
// throw new IllegalStateException(
// "MAVEN_HOME or M2_HOME must be set but System.getenv(\"MAVEN_HOME\") and
// System.getenv(\"M2_HOME\") returned null.");
// }
// }
//
// if (buildConfig.isSkipTests()) {
// goals.add("-DskipTests");
// }
//
// Invoker invoker = new DefaultInvoker();
// InvocationRequest request = new DefaultInvocationRequest();
// request.setGoals(goals);
// request.setBatchMode(true);
// request.setMavenHome(new File(mavenHome));
// request.setBaseDirectory(baseDir.toFile());
// request.setOutputHandler(s -> lineConsumer.accept(s));
//
// StringBuilder mavenOpts = new StringBuilder();
// mavenOpts.append("-Xms1G -Xmx6G ");
// if (debugConfig != null && debugConfig.isDebugEnabled()) {
// mavenOpts.append(" -Xdebug
// -Xrunjdwp:transport=dt_socket,server=y,suspend=%s,address=%s "
// .formatted(debugConfig.isSuspend(), debugConfig.getPort()));
// }
// request.setMavenOpts(mavenOpts.toString());
//
// try {
// InvocationResult result = invoker.execute(request);
// return result;
// }
// catch (MavenInvocationException e) {
// throw new RuntimeException(e);
// }
// }
private static String renderOpenRewriteCommand(List<String> recipeNames, List<String> dependencies,
RewritePluginGoals rewritePluginGoal) {
StringBuilder sb = new StringBuilder();
sb.append("org.openrewrite.maven:rewrite-maven-plugin:").append(rewritePluginGoal.getGoalName()).append(" ");
String recipesList = recipeNames.stream().collect(Collectors.joining(","));
sb.append("-Drewrite.activeRecipes=").append(recipesList);
if (!dependencies.isEmpty()) {
String dependenciesList = dependencies.stream().collect(Collectors.joining(","));
sb.append(" ").append("-Drewrite.recipeArtifactCoordinates=").append(dependenciesList);
}
return sb.toString();
}
public class Builders {
public interface StartBuilder {
OnDirBuilder applyRecipes(String... recipeNames);
OnDirBuilder applyRecipes(List<String> recipeNames);
}
public interface OnDirBuilder {
OptionalBuilder onDir(Path baseDir);
}
public interface OptionalBuilder {
OptionalBuilder withDebugConfig(DebugConfig debugConfig);
OptionalBuilder withDependencies(String... dependencyGavs);
OptionalBuilder withDependencies(List<String> dependencyGavs);
OptionalBuilder withBuildConfig(BuildConfig buildConfig);
OptionalBuilder withOutputListener(Consumer<String> outputListener);
OptionalBuilder withInvoker(Invoker invoker);
InvocationResult run();
InvocationResult runNoFork();
InvocationResult dryRun();
InvocationResult dryRunNoFork();
InvocationResult discover();
InvocationResult cyclonedx();
}
}
public static class MavenInvokerRewriteRecipeLauncherBuilder
implements Builders.StartBuilder, Builders.OnDirBuilder, Builders.OptionalBuilder {
private Path baseDir;
private List<String> recipes = new ArrayList<>();
private List<String> dependencies = new ArrayList<>();
private BuildConfig buildConfig = BuildConfig.defaultConfig();
private DebugConfig debugConfig = DebugConfig.disabled();
private Consumer<String> outputListener = line -> System.out.println(line);
private Invoker invoker = new DefaultInvoker();
public Builders.OptionalBuilder onDir(String baseDir) {
this.baseDir = Path.of(baseDir).toAbsolutePath().normalize();
return this;
}
@Override
public Builders.OnDirBuilder applyRecipes(String... recipeNames) {
validateRecipeNames(Arrays.asList(recipeNames));
return this.applyRecipes(Arrays.asList(recipeNames));
}
@Override
public Builders.OnDirBuilder applyRecipes(List<String> recipeNames) {
validateRecipeNames(recipeNames);
this.recipes = recipeNames;
return this;
}
@Override
public Builders.OptionalBuilder onDir(Path baseDir) {
this.baseDir = baseDir.toAbsolutePath().normalize();
return this;
}
@Override
public Builders.OptionalBuilder withOutputListener(Consumer<String> outputListener) {
this.outputListener = outputListener;
return this;
}
@Override
public Builders.OptionalBuilder withInvoker(Invoker invoker) {
this.invoker = invoker;
return this;
}
/**
* mvn rewrite:run - RunBuilder the configured recipes and apply the changes
* locally.
*/
@Override
public InvocationResult run() {
return executeRewriteGoal(invoker, baseDir, recipes, dependencies, debugConfig, buildConfig, outputListener,
RewritePluginGoals.RUN);
}
/**
* mvn rewrite:runNoFork - RunBuilder the configured recipes and apply the changes
* locally. This variant does not fork the Maven life cycle and can be a more
* efficient choice when using Rewrite within a CI workflow when combined with
* other Maven goals.
*/
@Override
public InvocationResult runNoFork() {
return executeRewriteGoal(invoker, baseDir, recipes, dependencies, debugConfig, buildConfig, outputListener,
RewritePluginGoals.RUN_NO_FORK);
}
/**
* mvn rewrite:dryRun - Generate warnings to the console for any recipe that would
* make changes and generates a diff file in each maven modules' target folder.
*/
@Override
public InvocationResult dryRun() {
return executeRewriteGoal(invoker, baseDir, recipes, dependencies, debugConfig, buildConfig, outputListener,
RewritePluginGoals.DRY_RUN);
}
/**
* mvn rewrite:dryRunNoFork - Generate warnings to the console for any recipe that
* would make changes and generates a diff file in each maven modules' target
* folder. This variant does not fork the Maven life cycle and can be a more
* efficient choice when using Rewrite within a CI workflow when combined with
* other Maven goals.
*/
@Override
public InvocationResult dryRunNoFork() {
return executeRewriteGoal(invoker, baseDir, recipes, dependencies, debugConfig, buildConfig, outputListener,
RewritePluginGoals.DRY_RUN_NO_FORK);
}
/**
* mvn rewrite:discover - Generate a report of available recipes found on the
* classpath.
*/
@Override
public InvocationResult discover() {
return executeRewriteGoal(invoker, baseDir, recipes, dependencies, debugConfig, buildConfig, outputListener,
RewritePluginGoals.DISCOVER);
}
/**
* mvn rewrite:cyclonedx - Generate a CycloneDx bill of materials outlining the
* project's dependencies, including transitive dependencies.
*
* @see <a href="https://cyclonedx.org/">CycloneDx</a>
*/
@Override
public InvocationResult cyclonedx() {
return executeRewriteGoal(invoker, baseDir, recipes, dependencies, debugConfig, buildConfig, outputListener,
RewritePluginGoals.CYCLONEDX);
}
private InvocationResult executeRewriteGoal(Invoker invoker, Path baseDir, List<String> recipeNames,
List<String> dependencies, DebugConfig debugConfig, BuildConfig buildConfig,
Consumer<String> lineConsumer, RewritePluginGoals rewritePluginGoals) {
MavenInvocationRewriteRecipeLauncher launcher = new MavenInvocationRewriteRecipeLauncher();
InvocationResult result = launcher.invokeRewritePlugin(invoker, baseDir, recipeNames, dependencies,
debugConfig, buildConfig, lineConsumer, rewritePluginGoals);
return result;
}
private void validateRecipeNames(List<String> recipeNames) {
if (recipeNames.isEmpty()) {
throw new IllegalArgumentException("At least one recipe name must be provided.");
}
if (recipeNames.stream().anyMatch(String::isBlank)) {
throw new IllegalArgumentException("At least one recipe name was blank.");
}
}
@Override
public Builders.OptionalBuilder withDependencies(String... dependencyGavs) {
return withDependencies(Arrays.asList(dependencyGavs));
}
@Override
public Builders.OptionalBuilder withDependencies(List<String> dependencyGavs) {
this.dependencies = dependencyGavs;
return this;
}
@Override
public Builders.OptionalBuilder withBuildConfig(BuildConfig buildConfig) {
this.buildConfig = buildConfig;
return this;
}
@Override
public Builders.OptionalBuilder withDebugConfig(DebugConfig debugConfig) {
this.debugConfig = debugConfig;
return this;
}
}
}

View File

@@ -1,156 +0,0 @@
package org.springframework.rewrite.maven;
/*
* Copyright 2021 - 2023 the original author or authors.
*
* 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
*
* https://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.
*/
import org.apache.maven.shared.invoker.*;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.*;
/**
* @author Fabian Krüger
*/
class MavenInvocationRewriteRecipeLauncherIntegrationTest {
@Mock
private Path baseDir = Path.of("./testcode/maven-projects/simple").toAbsolutePath().normalize();
@Test
@DisplayName("invokeRewritePlugin() method test")
void invokeRewritePluginMethodTest() {
List<String> capturedLines = new ArrayList<>();
MavenInvocationRewriteRecipeLauncher sut = new MavenInvocationRewriteRecipeLauncher();
sut.invokeRewritePlugin(Path.of("./testcode/maven-projects/simple"),
List.of("org.openrewrite.java.RemoveUnusedImports"), new ArrayList<>(), DebugConfig.from(9090, false),
BuildConfig.builder().skipTests(true).withMemory("64M", "256M").build(), capturedLines::add,
RewritePluginGoals.DRY_RUN);
assertThat(capturedLines).contains("Listening for transport dt_socket at address: 9090",
"[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ simple ---", "[INFO] Tests are skipped.",
"[INFO] Using active recipe(s) [org.openrewrite.java.RemoveUnusedImports]",
"[INFO] Using active styles(s) []", "[INFO] BUILD SUCCESS");
}
@Test
@DisplayName("mavenInvocationRequestBuilder success")
void mavenInvoker() {
List<String> capturedLines = new ArrayList<>();
Path baseDir = Path.of("./testcode/maven-projects/simple").toAbsolutePath().normalize();
InvocationResult result = MavenInvocationRewriteRecipeLauncher
.applyRecipes("org.openrewrite.java.RemoveUnusedImports", "org.openrewrite.java.format.AutoFormat")
.onDir(baseDir)
.withOutputListener(capturedLines::add)
.dryRun();
assertThat(result.getExitCode()).isEqualTo(0);
assertThat(capturedLines).contains("[INFO] Scanning for projects...",
"[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ simple ---",
"[INFO] Using active recipe(s) [org.openrewrite.java.RemoveUnusedImports, org.openrewrite.java.format.AutoFormat]",
"[INFO] BUILD SUCCESS");
}
@Test
@DisplayName("with debug enabled")
void withDebugEnabled() {
List<String> capturedLines = new ArrayList<>();
InvocationResult result = MavenInvocationRewriteRecipeLauncher
.applyRecipes("org.openrewrite.java.RemoveUnusedImports")
.onDir(Path.of("./testcode/maven-projects/simple").toAbsolutePath().normalize())
.withOutputListener(capturedLines::add)
.withDebugConfig(DebugConfig.fromDefault())
.withBuildConfig(BuildConfig.builder().withMemory("1G", "6G").build())
.run();
assertThat(capturedLines).contains("Listening for transport dt_socket at address: 5005");
assertThat(result.getExitCode()).isEqualTo(0);
}
@Test
@DisplayName("cyclonedx")
void cyclonedx() {
List<String> lines = new ArrayList<>();
MavenInvocationRewriteRecipeLauncher.Builders.OptionalBuilder builder = getBuilder(lines);
InvocationResult result = builder.cyclonedx();
assertThat(result.getExitCode()).isEqualTo(0);
assertThat(lines.stream().anyMatch(l -> l.endsWith(":cyclonedx (default-cli) @ simple ---"))).isTrue();
assertThat(baseDir.resolve("target/simple-0.1.0-SNAPSHOT-cyclonedx.xml")).exists();
}
@Test
@DisplayName("dryRun")
void dryRun() {
List<String> lines = new ArrayList<>();
MavenInvocationRewriteRecipeLauncher.Builders.OptionalBuilder builder = getBuilder(lines);
InvocationResult result = builder.dryRun();
assertThat(result.getExitCode()).isEqualTo(0);
assertThat(lines.stream().anyMatch(l -> l.endsWith(":dryRun (default-cli) @ simple ---"))).isTrue();
}
@Test
@DisplayName("dryRunNoFork")
void dryRunNoFork() {
List<String> lines = new ArrayList<>();
MavenInvocationRewriteRecipeLauncher.Builders.OptionalBuilder builder = getBuilder(lines);
InvocationResult result = builder.dryRunNoFork();
assertThat(result.getExitCode()).isEqualTo(0);
assertThat(lines.stream().anyMatch(l -> l.endsWith(":dryRunNoFork (default-cli) @ simple ---"))).isTrue();
}
@Test
@DisplayName("runNoFork")
void runNoFork() {
List<String> lines = new ArrayList<>();
MavenInvocationRewriteRecipeLauncher.Builders.OptionalBuilder builder = getBuilder(lines);
InvocationResult result = builder.dryRunNoFork();
assertThat(result.getExitCode()).isEqualTo(0);
assertThat(lines.stream().anyMatch(l -> l.endsWith(":dryRunNoFork (default-cli) @ simple ---"))).isTrue();
}
@Test
@DisplayName("discover")
void discover() {
List<String> lines = new ArrayList<>();
MavenInvocationRewriteRecipeLauncher.Builders.OptionalBuilder builder = getBuilder(lines);
InvocationResult result = builder.discover();
assertThat(result.getExitCode()).isEqualTo(0);
assertThat(lines).contains("[INFO] Available Recipes:", "[INFO] org.openrewrite.FindSourceFiles");
assertThat(lines.stream().anyMatch(l -> l.endsWith(":discover (default-cli) @ simple ---"))).isTrue();
}
private static MavenInvocationRewriteRecipeLauncher.Builders.OptionalBuilder getBuilder(List<String> lines) {
Path baseDir = Path.of("./testcode/maven-projects/simple").toAbsolutePath().normalize();
MavenInvocationRewriteRecipeLauncher.Builders.OptionalBuilder builder = MavenInvocationRewriteRecipeLauncher
.applyRecipes("org.openrewrite.java.RemoveUnusedImports")
.onDir(baseDir)
.withOutputListener(lines::add);
return builder;
}
}

View File

@@ -1,192 +0,0 @@
package org.springframework.rewrite.maven;
/*
* Copyright 2021 - 2023 the original author or authors.
*
* 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
*
* https://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.
*/
import org.apache.maven.shared.invoker.*;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.springframework.test.util.ReflectionTestUtils;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.*;
/**
* @author Fabian Krüger
*/
class MavenInvocationRewriteRecipeLauncherTest {
private static final int SUCCESS = 0;
private MavenInvocationRewriteRecipeLauncher.Builders.OptionalBuilder invokerBuilder;
@Mock
Invoker mavenInvoker = mock(Invoker.class);
private Path baseDir = Path.of("./testcode/maven-projects/simple").toAbsolutePath().normalize();
private List<String> capturedLines = new ArrayList<>();
private Consumer<String> outputListener = capturedLines::add;
@BeforeEach
void beforeEach() throws MavenInvocationException {
outputListener = capturedLines::add;
invokerBuilder = MavenInvocationRewriteRecipeLauncher
.applyRecipes("org.openrewrite.java.RemoveUnusedImports", "org.openrewrite.java.format.AutoFormat")
.onDir(baseDir)
.withInvoker(mavenInvoker)
.withOutputListener(outputListener);
InvocationResult result = new DummyInvocationResult();
when(mavenInvoker.execute(any(InvocationRequest.class))).thenReturn(result);
}
@Test
@DisplayName("minimal")
void minimal() throws MavenInvocationException {
InvocationResult actualResult = invokerBuilder.run();
assertThat(actualResult.getExitCode()).isEqualTo(0);
String[] expectedGoals = { "clean", "package", "--fail-at-end",
"org.openrewrite.maven:rewrite-maven-plugin:run -Drewrite.activeRecipes=org.openrewrite.java.RemoveUnusedImports,org.openrewrite.java.format.AutoFormat" };
verifyCallToMaven(expectedGoals);
}
@Test
@DisplayName("with dependencies")
void withDependencies() throws MavenInvocationException {
InvocationResult actualResult = invokerBuilder
.withDependencies("com.example:some-dep:1.0.0", "com.example:another-dep:1.0.0")
.run();
assertThat(actualResult.getExitCode()).isEqualTo(0);
String[] expectedGoals = { "clean", "package", "--fail-at-end",
"org.openrewrite.maven:rewrite-maven-plugin:run -Drewrite.activeRecipes=org.openrewrite.java.RemoveUnusedImports,org.openrewrite.java.format.AutoFormat -Drewrite.recipeArtifactCoordinates=com.example:some-dep:1.0.0,com.example:another-dep:1.0.0" };
verifyCallToMaven(expectedGoals);
}
@Test
@DisplayName("with build tools config")
void withBuildToolConfig() throws MavenInvocationException {
InvocationResult actualResult = invokerBuilder
.withBuildConfig(BuildConfig.builder().withMemory("64M", "256M").skipTests(true).build())
.run();
assertThat(actualResult.getExitCode()).isEqualTo(0);
String[] expectedGoals = { "clean", "package", "--fail-at-end",
"org.openrewrite.maven:rewrite-maven-plugin:run -Drewrite.activeRecipes=org.openrewrite.java.RemoveUnusedImports,org.openrewrite.java.format.AutoFormat" };
verifyCallToMaven(expectedGoals, BuildConfig.builder().withMemory("64M", "256M").skipTests(true).build());
}
@Test
@DisplayName("builder")
void builder(@TempDir Path tempDir) {
InvocationResult result = MavenInvocationRewriteRecipeLauncher.applyRecipes("recipe1").onDir(tempDir).run();
MavenInvocationRewriteRecipeLauncher.applyRecipes("recipe1").onDir(tempDir).withDependencies("", "").run();
MavenInvocationRewriteRecipeLauncher.applyRecipes(List.of("NoName")).onDir(tempDir).withDependencies("").run();
// build tool config only
MavenInvocationRewriteRecipeLauncher.applyRecipes(List.of("NoName"))
.onDir(tempDir)
.withDependencies("")
.withBuildConfig(BuildConfig.defaultConfig())
.run();
// debug config only
MavenInvocationRewriteRecipeLauncher.applyRecipes("NoName")
.onDir(tempDir)
.withDependencies("")
.withDebugConfig(DebugConfig.disabled())
.run();
MavenInvocationRewriteRecipeLauncher.applyRecipes(List.of("NoName"))
.onDir(tempDir)
.withDependencies("")
.withDebugConfig(DebugConfig.disabled())
.dryRun();
// build tool and debug config
MavenInvocationRewriteRecipeLauncher.applyRecipes(List.of("NoName"))
.onDir(tempDir)
.withDependencies("")
.withBuildConfig(BuildConfig.defaultConfig())
.withDebugConfig(DebugConfig.disabled())
.run();
MavenInvocationRewriteRecipeLauncher.applyRecipes("NoName").onDir(tempDir).run();
}
@Test
@DisplayName("missing recipeNames should throw exception")
void missingRecipeNamesShouldThrowException() {
assertThrows(IllegalArgumentException.class, () -> {
MavenInvocationRewriteRecipeLauncher.applyRecipes("").onDir(Path.of("")).run();
});
}
@Test
@DisplayName("mavenInvocationRequestBuilder extra memory")
void mavenInvokerExtraMemory() {
List<String> lines = new ArrayList<>();
Path baseDir = Path.of("./testcode/maven-projects/simple").toAbsolutePath().normalize();
InvocationResult result = MavenInvocationRewriteRecipeLauncher
.applyRecipes("org.openrewrite.java.RemoveUnusedImports")
.onDir(baseDir)
.withBuildConfig(BuildConfig.builder().skipTests(false).withMemory("32M", "1G").build())
.withOutputListener(lines::add)
.dryRun();
assertThat(result.getExitCode()).isEqualTo(SUCCESS);
assertThat(lines).contains("[INFO] Scanning for projects...",
"[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ simple ---", "[INFO] BUILD SUCCESS");
}
private void verifyCallToMaven(String[] expectedGoals) throws MavenInvocationException {
BuildConfig buildConfig = BuildConfig.defaultConfig();
verifyCallToMaven(expectedGoals, buildConfig, DebugConfig.disabled(), baseDir, outputListener,
Arrays.asList(expectedGoals));
}
private void verifyCallToMaven(String[] expectedGoals, BuildConfig buildConfig) throws MavenInvocationException {
verifyCallToMaven(expectedGoals, buildConfig, DebugConfig.disabled(), baseDir, outputListener,
Arrays.asList(expectedGoals));
}
private void verifyCallToMaven(String[] expectedGoals, BuildConfig buildConfig, DebugConfig debugConfig,
Path baseDir, Consumer<String> lineConsumer, List<String> givenGoals) throws MavenInvocationException {
InvocationRequest mavenInvocationRequest = new MavenInvocationRequestFactory()
.createMavenInvocationRequest(baseDir, debugConfig, buildConfig, givenGoals, lineConsumer);
ArgumentCaptor<InvocationRequest> captor = ArgumentCaptor.forClass(InvocationRequest.class);
verify(mavenInvoker).execute(captor.capture());
InvocationRequest request = captor.getValue();
assertThat(request).usingRecursiveComparison().isEqualTo(mavenInvocationRequest);
}
}

View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.rewrite</groupId>
<artifactId>spring-rewrite-commons</artifactId>
<version>0.1.0-SNAPSHOT</version>
</parent>
<artifactId>spring-rewrite-commons-plugin-invoker</artifactId>
<packaging>pom</packaging>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<modules>
<module>spring-rewrite-commons-plugin-invoker-shared</module>
<module>spring-rewrite-commons-plugin-invoker-maven</module>
<module>spring-rewrite-commons-plugin-invoker-gradle</module>
<module>spring-rewrite-commons-plugin-invoker-polyglot</module>
</modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.rewrite</groupId>
<artifactId>spring-rewrite-commons-plugin-invoker-maven</artifactId>
<version>0.1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.rewrite</groupId>
<artifactId>spring-rewrite-commons-plugin-invoker-gradle</artifactId>
<version>0.1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>

View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.rewrite</groupId>
<artifactId>spring-rewrite-commons-plugin-invoker</artifactId>
<version>0.1.0-SNAPSHOT</version>
</parent>
<artifactId>spring-rewrite-commons-plugin-invoker-gradle</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.gradle</groupId>
<artifactId>gradle-tooling-api</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.rewrite</groupId>
<artifactId>spring-rewrite-commons-plugin-invoker-shared</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openrewrite</groupId>
<artifactId>rewrite-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<repositories>
<repository>
<id>gradle</id>
<url>https://repo.gradle.org/gradle/libs-releases</url>
</repository>
</repositories>
</project>

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2021 - 2023 the original author or authors.
*
* 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
*
* https://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 org.springframework.rewrite.plugin.gradle;
import org.openrewrite.marker.BuildTool;
/**
* @author Fabian Krüger
*/
public class GradleInvocationResult {
private final String output;
private final String error;
public GradleInvocationResult(String output, String error) {
this.output = output;
this.error = error;
}
public String getOutput() {
return output;
}
public String getError() {
return error;
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2021 - 2023 the original author or authors.
*
* 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
*
* https://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 org.springframework.rewrite.plugin.gradle;
import org.gradle.tooling.BuildLauncher;
import org.gradle.tooling.GradleConnector;
import org.gradle.tooling.ProjectConnection;
import org.gradle.tooling.internal.consumer.DefaultGradleConnector;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
/**
* @author Fabian Krüger
*/
public class GradleInvoker {
private static BuildLauncher getBuildLauncher(Path baseDir, OutputStream os, String[] tasks) {
ProjectConnection connection = getProjectConnection(baseDir);
BuildLauncher buildLauncher = connection.newBuild().setStandardOutput(os).forTasks(tasks);
return buildLauncher;
}
public static GradleInvocationResult runTasks(Path baseDir, String[] args, String... tasks) {
ByteArrayOutputStream os = new ByteArrayOutputStream();
ByteArrayOutputStream es = new ByteArrayOutputStream();
BuildLauncher buildLauncher = getBuildLauncher(baseDir, os, tasks);
buildLauncher.addArguments(args);
buildLauncher.setStandardError(es);
buildLauncher.run();
return new GradleInvocationResult(new String(os.toByteArray()), new String(es.toByteArray()));
}
protected static ProjectConnection getProjectConnection(Path projectDir) {
ProjectConnection connection;
var connector = (DefaultGradleConnector) GradleConnector.newConnector();
if (Files.exists(projectDir.resolve("gradle/wrapper/gradle-wrapper.properties"))) {
connector.useBuildDistribution();
}
else {
connector.useGradleVersion("8.4");
}
connection = connector.forProjectDirectory(projectDir.toFile()).connect();
return connection;
}
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright 2021 - 2023 the original author or authors.
*
* 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
*
* https://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 org.springframework.rewrite.plugin.gradle;
import org.springframework.rewrite.plugin.shared.DebugConfig;
import org.springframework.rewrite.plugin.shared.PluginInvocationResult;
import java.nio.file.Path;
/**
* @author Fabian Krüger
*/
public interface OpenRewriteGradlePluginBuilder {
public interface Recipes {
PluginVersion recipes(String... recipeNames);
}
public interface FinalizingBuilder {
FinalizingBuilder withDebugger(int port, boolean suspend);
FinalizingBuilder withDependencies(String... dependencies);
FinalizingBuilder withDebug();
FinalizingBuilder withMemory(String minMemory, String maxMemory);
FinalizingBuilder withDebugConfig(DebugConfig debugConfig);
PluginInvocationResult onDir(Path baseDir);
}
public interface PluginVersion {
FinalizingBuilder usingPluginVersion(String version);
}
}

View File

@@ -0,0 +1,199 @@
/*
* Copyright 2021 - 2023 the original author or authors.
*
* 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
*
* https://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 org.springframework.rewrite.plugin.gradle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.rewrite.plugin.shared.BuildConfig;
import org.springframework.rewrite.plugin.shared.DebugConfig;
import org.springframework.rewrite.plugin.shared.PluginInvocationResult;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.springframework.rewrite.plugin.gradle.RewriteGradlePlugin.Task.*;
/**
* @author Fabian Krüger
*/
public class RewriteGradlePlugin implements OpenRewriteGradlePluginBuilder.Recipes,
OpenRewriteGradlePluginBuilder.FinalizingBuilder, OpenRewriteGradlePluginBuilder.PluginVersion {
private static final Logger log = LoggerFactory.getLogger(RewriteGradlePlugin.class);
private Task task;
private List<String> dependencies = new ArrayList<>();
private String pluginVersion;
private List<String> recipes = new ArrayList<>();
private DebugConfig debugConfig = DebugConfig.disabled();
private String minMemory;
private String maxMemory;
private boolean debug = false;
public static OpenRewriteGradlePluginBuilder.Recipes run() {
return executeGradleRewritePlugin(RUN);
}
public static OpenRewriteGradlePluginBuilder.Recipes dryRun() {
return executeGradleRewritePlugin(DRY_RUN);
}
public static OpenRewriteGradlePluginBuilder.Recipes discover() {
return executeGradleRewritePlugin(DISCOVER);
}
private static OpenRewriteGradlePluginBuilder.Recipes executeGradleRewritePlugin(Task task) {
RewriteGradlePlugin plugin = new RewriteGradlePlugin();
plugin.task = task;
return plugin;
}
@Override
public OpenRewriteGradlePluginBuilder.PluginVersion recipes(String... recipeNames) {
this.recipes = Arrays.asList(recipeNames);
return this;
}
@Override
public OpenRewriteGradlePluginBuilder.FinalizingBuilder withDebugger(int port, boolean suspend) {
this.debugConfig = DebugConfig.from(port, suspend);
return this;
}
@Override
public OpenRewriteGradlePluginBuilder.FinalizingBuilder withDependencies(String... dependencies) {
this.dependencies = Arrays.asList(dependencies);
return this;
}
@Override
public OpenRewriteGradlePluginBuilder.FinalizingBuilder withDebug() {
this.debug = true;
return this;
}
@Override
public OpenRewriteGradlePluginBuilder.FinalizingBuilder withMemory(String minMemory, String maxMemory) {
this.minMemory = minMemory;
this.maxMemory = maxMemory;
return this;
}
@Override
public PluginInvocationResult onDir(Path baseDir) {
baseDir = baseDir.toAbsolutePath().normalize();
BuildConfig buildConfig = BuildConfig.defaultConfig();
if (minMemory != null && !minMemory.isBlank()) {
buildConfig = BuildConfig.builder().withMemory(minMemory, maxMemory).build();
}
GradleInvocationResult result = execute(baseDir, debug, debugConfig, buildConfig, task, dependencies,
pluginVersion, recipes.toArray(String[]::new));
boolean success = result.getError() == null || result.getError().isEmpty();
return new PluginInvocationResult(success, result.getOutput());
}
@Override
public OpenRewriteGradlePluginBuilder.FinalizingBuilder withDebugConfig(DebugConfig debugConfig) {
this.debugConfig = debugConfig;
return this;
}
/**
* Executes given OpenRewrite recipes using Gradle Tooling API.
* @param baseDir the dir getting parsed
* @param debug print debug output
* @param debugConfig set remote debug config to debug the recipe execution
*/
static GradleInvocationResult execute(Path baseDir, boolean debug, DebugConfig debugConfig, BuildConfig buildConfig,
Task goal, List<String> dependencies, String pluginVersion, String... recipes) {
try (TempGradleInitFile initFile = new TempGradleInitFile(baseDir, dependencies, pluginVersion)) {
String initFileLocation = initFile.getPath().toString();
List<String> args = new ArrayList<>();
// provide init file location
args.add("-I%s".formatted(initFileLocation));
// the recipes
args.add("-Drewrite.activeRecipe=%s".formatted(Stream.of(recipes).collect(Collectors.joining(","))));
// debug outout
if (debug) {
args.add("-d");
}
// Remote debugging
if (debugConfig.isDebugEnabled()) {
args.add("-Dorg.gradle.logging.stacktrace=full");
args.add("-Dorg.gradle.debug=true");
args.add("-Dorg.gradle.debug.port=" + debugConfig.getPort());
args.add("-Dorg.gradle.debug.suspend=" + debugConfig.isSuspend());
}
// JVM Memory
List<String> jvmArgs = new ArrayList<>();
if (buildConfig.hasMemorySettings()) {
String min = buildConfig.getMemorySettings().getMin();
String max = buildConfig.getMemorySettings().getMax();
jvmArgs.add("-Xms%s -Xmx%s".formatted(min, max));
String jvmArgsStr = jvmArgs.stream().collect(Collectors.joining(" "));
args.add("-Dorg.gradle.jvmargs=%s".formatted(jvmArgsStr));
}
if (log.isDebugEnabled()) {
log.debug("Command: ./gradlew %s %s".formatted(goal.getTask(),
args.stream().collect(Collectors.joining(" "))));
log.debug(initFileLocation + " \n" + initFile.getContent());
}
return GradleInvoker.runTasks(baseDir, args.toArray(String[]::new), goal.getTask());
}
finally {
TempGradleInitFile.clear(baseDir);
}
}
@Override
public OpenRewriteGradlePluginBuilder.FinalizingBuilder usingPluginVersion(String version) {
this.pluginVersion = version;
return this;
}
enum Task {
RUN("rewriteRun"), DRY_RUN("rewriteDryRun"), DISCOVER("rewriteDiscover");
private final String task;
Task(String task) {
this.task = task;
}
public String getTask() {
return task;
}
}
}

View File

@@ -0,0 +1,107 @@
/*
* Copyright 2021 - 2023 the original author or authors.
*
* 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
*
* https://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 org.springframework.rewrite.plugin.gradle;
import java.io.Closeable;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author Fabian Krüger
*/
public class TempGradleInitFile implements Closeable {
private static final String template = """
initscript {
repositories {
maven { url "https://plugins.gradle.org/m2" }
}
dependencies {
classpath("org.openrewrite:plugin:%s")
}
}
rootProject {
plugins.apply(org.openrewrite.gradle.RewritePlugin)
dependencies {
%s
}
afterEvaluate {
if (repositories.isEmpty()) {
repositories {
mavenCentral()
}
}
}
}
""";
private final Path gradleFile;
private final String initFileContent;
public TempGradleInitFile(Path baseDir, List<String> dependencies, String pluginVersion) {
String dependenciesStr = dependencies.stream()
.map(d -> " rewrite(\"" + d + "\")")
.collect(Collectors.joining("\n"));
initFileContent = template.formatted(pluginVersion, dependenciesStr);
try {
gradleFile = baseDir.resolve("recipes.init.gradle").toAbsolutePath().normalize();
gradleFile.toFile().getParentFile().mkdirs();
Files.writeString(gradleFile, initFileContent);
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
public static void clear(Path baseDir) {
Path gradleFile = baseDir.resolve("recipes.init.gradle");
if (Files.exists(gradleFile)) {
try {
Files.delete(gradleFile);
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
}
public String getContent() {
return initFileContent;
}
public Path getPath() {
return gradleFile;
}
@Override
public void close() {
if (Files.exists(gradleFile)) {
try {
Files.delete(gradleFile);
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}

View File

@@ -0,0 +1,145 @@
/*
* Copyright 2021 - 2023 the original author or authors.
*
* 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
*
* https://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 org.springframework.rewrite.plugin.gradle;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.rewrite.plugin.shared.PluginInvocationResult;
import org.springframework.test.util.TestSocketUtils;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Fabian Krüger
*/
public class RewriteGradlePluginTest {
public static final String[] RECIPES = { "org.openrewrite.java.RemoveUnusedImports",
"org.openrewrite.java.format.NormalizeLineBreaks" };
public static final String[] EXTENDED_RECIPES = { "org.openrewrite.java.RemoveUnusedImports",
"org.openrewrite.java.format.NormalizeLineBreaks",
"org.openrewrite.java.migrate.jakarta.MaybeAddJakartaServletApi" };
public static final String[] DEFAULT_TASKS = new String[] { "compileJava", "processResources", "classes",
"compileTestJava", "rewriteResolveDependencies" };
public static final String EXTENDED_RECIPES_DEPS = "org.openrewrite.recipe:rewrite-migrate-java:2.9.0";
@Test
@DisplayName("simple project minimal setup")
void simpleProjectMinimalSetup() {
Path baseDir = Path.of("./testcode/gradle-projects/simple");
PluginInvocationResult result = RewriteGradlePlugin.dryRun()
.recipes(RECIPES)
.usingPluginVersion("6.10.0")
.onDir(baseDir);
String out = result.capturedOutput();
assertThat(result.success()).isTrue();
assertSuccessfulBuild(out);
assertTasksExecuted(out, DEFAULT_TASKS, "rewriteDryRun");
assertRecipesExecuted(out, RECIPES);
}
@Test
@DisplayName("simple project skip tests")
void simpleProjectSkipTests() {
Path baseDir = Path.of("./testcode/gradle-projects/simple");
PluginInvocationResult result = RewriteGradlePlugin.run()
.recipes(RECIPES)
.usingPluginVersion("6.10.0")
.withMemory("256M", "1G")
.onDir(baseDir);
String out = result.capturedOutput();
assertThat(result.success()).isTrue();
assertSuccessfulBuild(out);
assertTasksExecuted(out, DEFAULT_TASKS, "rewriteRun");
assertRecipesExecuted(out, RECIPES);
}
@Test
@DisplayName("simple project full config")
void simpleProjectFullConfig() {
Path baseDir = Path.of("./testcode/gradle-projects/simple");
int port = TestSocketUtils.findAvailableTcpPort();
PluginInvocationResult result = RewriteGradlePlugin.run()
.recipes(EXTENDED_RECIPES)
.usingPluginVersion("6.10.0")
.withDebugger(port, false)
.withDependencies(EXTENDED_RECIPES_DEPS)
.withDebug()
.withMemory("64M", "484M")
.onDir(baseDir);
assertThat(result.success()).isTrue();
String out = result.capturedOutput();
assertMemory(out, "64M", "484M");
assertDebugConfig(out, port, false);
assertRecipesExecuted(out, EXTENDED_RECIPES);
assertTasksExecuted(out, DEFAULT_TASKS, "rewriteRun");
}
private void assertDebugConfig(String out, int port, boolean suspend) {
assertThat(out).contains("-agentlib:jdwp=transport=dt_socket,server=y,suspend=%s,address=%s"
.formatted(suspend ? "y" : "n", port));
}
private void assertMemory(String out, String min, String max) {
assertThat(out).contains("-Xms%s".formatted(min));
assertThat(out).contains("-Xmx%s".formatted(max));
}
private void assertSuccessfulBuild(String out) {
assertThat(out).contains("BUILD SUCCESSFUL in");
}
private void assertTasksExecuted(String out, String[] defaultTasks, String rewriteTask) {
String[] tasks = tasksOf(defaultTasks, rewriteTask);
List<String> tasksOutput = Stream.of(tasks).map(t -> "Task :" + t).toList();
assertThat(out).contains(tasksOutput);
}
private void assertRecipesExecuted(String out, String[] recipes) {
assertThat(out).containsIgnoringWhitespaces(
"All sources parsed, running active recipes: " + Stream.of(recipes).collect(Collectors.joining(",")));
}
private String[] tasksOf(String[] defaultTasks, String[] testTasks, String rewriteTask) {
List<String> tasksList = new ArrayList<>();
tasksList.addAll(Arrays.asList(defaultTasks));
if (testTasks != null) {
tasksList.addAll(Arrays.asList(testTasks));
}
tasksList.add(rewriteTask);
return tasksList.toArray(String[]::new);
}
private String[] tasksOf(String[] defaultTasks, String rewriteTask) {
return tasksOf(defaultTasks, null, rewriteTask);
}
}

View File

@@ -0,0 +1,122 @@
/*
* Copyright 2021 - 2023 the original author or authors.
*
* 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
*
* https://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 org.springframework.rewrite.plugin.gradle;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import java.nio.file.Path;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Fabian Krüger
*/
class TempGradleInitFileTest {
@Test
@DisplayName("should create and delete gradle init file")
void shouldCreateAndDeleteGradleInitFile(@TempDir Path temoDir) {
List<String> depemdemcies = List.of("dep1", "dep2");
String pluginVersion = "x.y.z";
assertThat(temoDir.resolve("recipes.init.gradle")).doesNotExist();
try (TempGradleInitFile initFile = new TempGradleInitFile(temoDir, depemdemcies, pluginVersion)) {
assertThat(temoDir.resolve("recipes.init.gradle")).exists();
assertThat(initFile.getContent()).isEqualTo("""
initscript {
repositories {
maven { url "https://plugins.gradle.org/m2" }
}
dependencies {
classpath("org.openrewrite:plugin:x.y.z")
}
}
rootProject {
plugins.apply(org.openrewrite.gradle.RewritePlugin)
dependencies {
rewrite("dep1")
rewrite("dep2")
}
afterEvaluate {
if (repositories.isEmpty()) {
repositories {
mavenCentral()
}
}
}
}
""");
}
assertThat(temoDir.resolve("recipes.init.gradle")).doesNotExist();
}
@Test
@DisplayName("should create and delete gradle init file on exception")
void shouldCreateAndDeleteGradleInitFileOnException(@TempDir Path temoDir) {
List<String> depemdemcies = List.of("dep1", "dep2");
String pluginVersion = "x.y.z";
assertThat(temoDir.resolve("recipes.init.gradle")).doesNotExist();
try {
try (TempGradleInitFile initFile = new TempGradleInitFile(temoDir, depemdemcies, pluginVersion)) {
assertThat(temoDir.resolve("recipes.init.gradle")).exists();
assertThat(initFile.getContent()).isEqualTo("""
initscript {
repositories {
maven { url "https://plugins.gradle.org/m2" }
}
dependencies {
classpath("org.openrewrite:plugin:x.y.z")
}
}
rootProject {
plugins.apply(org.openrewrite.gradle.RewritePlugin)
dependencies {
rewrite("dep1")
rewrite("dep2")
}
afterEvaluate {
if (repositories.isEmpty()) {
repositories {
mavenCentral()
}
}
}
}
""");
throw new RuntimeException();
}
}
catch (Exception e) {
assertThat(temoDir.resolve("recipes.init.gradle")).doesNotExist();
}
}
}

View File

@@ -0,0 +1,42 @@
.gradle
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
out/
!**/src/main/**/out/
!**/src/test/**/out/
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store

View File

@@ -0,0 +1,19 @@
plugins {
id("java")
}
group = "org.example"
version = "1.0-SNAPSHOT"
repositories {
mavenCentral()
}
dependencies {
testImplementation(platform("org.junit:junit-bom:5.9.1"))
testImplementation("org.junit.jupiter:junit-jupiter")
}
tasks.test {
useJUnitPlatform()
}

View File

@@ -0,0 +1,6 @@
#Wed Mar 13 15:37:19 CET 2024
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@@ -0,0 +1,234 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# 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
#
# https://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.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

View File

@@ -0,0 +1,89 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@@ -0,0 +1,10 @@
package org.example;
/**
* @author Fabian Krüger
*/
public class Main {
public static void main(String[] args) {
System.out.println("Hello world!");
}
}

View File

@@ -5,11 +5,11 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.rewrite</groupId>
<artifactId>spring-rewrite-commons</artifactId>
<artifactId>spring-rewrite-commons-plugin-invoker</artifactId>
<version>0.1.0-SNAPSHOT</version>
</parent>
<artifactId>spring-rewrite-commons-maven-invoker</artifactId>
<artifactId>spring-rewrite-commons-plugin-invoker-maven</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
@@ -25,9 +25,8 @@
<dependency>
<groupId>org.apache.maven.shared</groupId>
<artifactId>maven-shared-utils</artifactId>
<version>3.4.2</version>
<version>${maven-shared-utils.version}</version>
</dependency>
<dependency>
<groupId>org.apache.maven.shared</groupId>
<artifactId>maven-invoker</artifactId>
@@ -85,6 +84,12 @@
<version>${maven.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.rewrite</groupId>
<artifactId>spring-rewrite-commons-plugin-invoker-shared</artifactId>
<version>0.1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>

View File

@@ -13,10 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.rewrite.maven;
package org.springframework.rewrite.plugin.maven;
import org.apache.maven.shared.invoker.*;
import org.jetbrains.annotations.NotNull;
import org.springframework.rewrite.plugin.shared.BuildConfig;
import org.springframework.rewrite.plugin.shared.DebugConfig;
import java.io.File;
import java.nio.file.Path;
@@ -30,7 +32,7 @@ import java.util.function.Consumer;
public class MavenInvocationRequestFactory {
@NotNull
public InvocationRequest createMavenInvocationRequest(Path baseDir, DebugConfig debugConfig,
public InvocationRequest createMavenInvocationRequest(Path baseDir, DebugConfig debugConfig, boolean debug,
BuildConfig buildConfig, List<String> givenGoals, Consumer<String> lineConsumer) {
List<String> goals = new ArrayList<>(givenGoals);
String mavenHome = System.getenv("MAVEN_HOME");
@@ -52,15 +54,18 @@ public class MavenInvocationRequestFactory {
request.setMavenHome(new File(mavenHome));
request.setBaseDirectory(baseDir.toFile());
request.setOutputHandler(s -> lineConsumer.accept(s));
if (debug) {
request.setDebug(true);
}
StringBuilder mavenOpts = new StringBuilder();
if (buildConfig.getMemorySettings() != null) {
if (buildConfig.hasMemorySettings()) {
mavenOpts.append("-Xms%s -Xmx%s ".formatted(buildConfig.getMemorySettings().getMin(),
buildConfig.getMemorySettings().getMax()));
}
if (debugConfig != null && debugConfig.isDebugEnabled()) {
mavenOpts.append(" -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=%s,address=%s "
.formatted(debugConfig.isSuspend(), debugConfig.getPort()));
.formatted(debugConfig.isSuspendEnabled(), debugConfig.getPort()));
}
request.setMavenOpts(mavenOpts.toString());
return request;

View File

@@ -13,21 +13,28 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.rewrite.maven;
package org.springframework.rewrite.plugin.maven;
import org.apache.maven.shared.invoker.InvocationResult;
import org.apache.maven.shared.utils.cli.CommandLineException;
/**
* @author Fabian Krüger
*/
public class MavenInvocationResult {
public class DummyInvocationResult implements InvocationResult {
private final int exitCode;
@Override
public CommandLineException getExecutionException() {
return null;
private final String capturedLines;
public MavenInvocationResult(int exitCode, String capturedLines) {
this.exitCode = exitCode;
this.capturedLines = capturedLines;
}
@Override
public int getExitCode() {
return 0;
return exitCode;
}
}
public String getCapturedLines() {
return capturedLines;
}
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright 2021 - 2023 the original author or authors.
*
* 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
*
* https://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 org.springframework.rewrite.plugin.maven;
import org.apache.maven.shared.invoker.*;
import org.jetbrains.annotations.NotNull;
import org.springframework.rewrite.plugin.shared.BuildConfig;
import org.springframework.rewrite.plugin.shared.DebugConfig;
import org.springframework.rewrite.plugin.shared.MemorySettings;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
/**
* @author Fabian Krüger
*/
public class MavenInvoker {
private static final Invoker invoker = new DefaultInvoker();
public static MavenInvocationResult runGoals(Path baseDir, DebugConfig debugConfig, boolean debug,
BuildConfig buildConfig, List<String> goals) {
try {
StringBuilder sb = new StringBuilder();
InvocationRequest request = createInvocationRequest(baseDir, debugConfig, debug, buildConfig, goals, sb);
InvocationResult result = invoker.execute(request);
return new MavenInvocationResult(result.getExitCode(), sb.toString());
}
catch (MavenInvocationException e) {
throw new RuntimeException(e);
}
}
@NotNull
private static InvocationRequest createInvocationRequest(Path baseDir, DebugConfig debugConfig, boolean debug,
BuildConfig buildConfig, List<String> goals, StringBuilder sb) {
return new MavenInvocationRequestFactory().createMavenInvocationRequest(baseDir, debugConfig, debug,
buildConfig, goals, line -> {
sb.append(line).append("\n");
});
}
}

View File

@@ -0,0 +1,165 @@
/*
* Copyright 2021 - 2023 the original author or authors.
*
* 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
*
* https://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 org.springframework.rewrite.plugin.maven;
import org.springframework.rewrite.plugin.shared.BuildConfig;
import org.springframework.rewrite.plugin.shared.DebugConfig;
import org.springframework.rewrite.plugin.shared.PluginInvocationResult;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import static org.springframework.rewrite.plugin.maven.RewriteMavenPlugin.Goal.*;
public class RewriteMavenPlugin
implements RewriteMavenPluginBuilder.FinalizingBuilder, RewriteMavenPluginBuilder.Recipes {
private Goal goal;
private DebugConfig debugConfig;
private List<String> recipes = new ArrayList<>();
private String minMemory;
private String maxMemory;
private MavenInvocationRequestFactory factory = new MavenInvocationRequestFactory();
private List<String> dependencies = new ArrayList<>();
private boolean debug;
@Override
public RewriteMavenPluginBuilder.FinalizingBuilder withDependencies(String... dependencies) {
this.dependencies = Arrays.asList(dependencies);
return this;
}
public static RewriteMavenPluginBuilder.Recipes run() {
Goal run = RUN;
return executeMavenRewritePlugin(run);
}
public static RewriteMavenPluginBuilder.Recipes runNoFork() {
Goal run = RUN_NO_FORK;
return executeMavenRewritePlugin(run);
}
public static RewriteMavenPluginBuilder.Recipes dryRun() {
Goal run = DRY_RUN;
return executeMavenRewritePlugin(run);
}
public static RewriteMavenPluginBuilder.Recipes dryRunNoFork() {
Goal run = DRY_RUN_NO_FORK;
return executeMavenRewritePlugin(run);
}
public static RewriteMavenPluginBuilder.Recipes discover() {
Goal run = DISCOVER;
return executeMavenRewritePlugin(run);
}
public static RewriteMavenPluginBuilder.Recipes cyclonedx() {
Goal run = CYCLONEDX;
return executeMavenRewritePlugin(run);
}
private static RewriteMavenPlugin executeMavenRewritePlugin(Goal run) {
RewriteMavenPlugin rewriteMavenPlugin = new RewriteMavenPlugin();
rewriteMavenPlugin.goal = run;
return rewriteMavenPlugin;
}
@Override
public RewriteMavenPluginBuilder.FinalizingBuilder recipes(String... recipeNames) {
this.recipes = Arrays.asList(recipeNames);
return this;
}
@Override
public RewriteMavenPluginBuilder.FinalizingBuilder withDebugger(int port, boolean suspend) {
debugConfig = DebugConfig.from(port, suspend);
return this;
}
@Override
public RewriteMavenPluginBuilder.FinalizingBuilder withDebug() {
this.debug = true;
return this;
}
@Override
public PluginInvocationResult onDir(Path baseDir) {
BuildConfig.Builder builder = BuildConfig.builder();
if (minMemory != null) {
builder.withMemory(minMemory, maxMemory);
}
MavenInvocationResult result = execute(baseDir, debug, debugConfig, builder.build(), goal, recipes,
dependencies);
return new PluginInvocationResult(result.getExitCode() != 0 ? false : true, result.getCapturedLines());
}
static MavenInvocationResult execute(Path baseDir, boolean debug, DebugConfig debugConfig, BuildConfig buildConfig,
Goal openRewriteGoal, List<String> recipes1, List<String> dependencies1) {
String openRewriteCommand = renderOpenRewriteCommand(recipes1, dependencies1, openRewriteGoal);
List<String> goals = List.of("--fail-at-end", openRewriteCommand);
return MavenInvoker.runGoals(baseDir, debugConfig, debug, buildConfig, goals);
}
private static String renderOpenRewriteCommand(List<String> recipeNames, List<String> dependencies,
Goal rewritePluginGoal) {
StringBuilder sb = new StringBuilder();
sb.append("org.openrewrite.maven:rewrite-maven-plugin:").append(rewritePluginGoal.getMaven()).append(" ");
String recipesList = recipeNames.stream().collect(Collectors.joining(","));
sb.append("-Drewrite.activeRecipes=").append(recipesList);
if (!dependencies.isEmpty()) {
String dependenciesList = dependencies.stream().collect(Collectors.joining(","));
sb.append(" ").append("-Drewrite.recipeArtifactCoordinates=").append(dependenciesList);
}
return sb.toString();
}
@Override
public RewriteMavenPluginBuilder.FinalizingBuilder withMemory(String minMemory, String maxMemory) {
this.minMemory = minMemory;
this.maxMemory = maxMemory;
return this;
}
public enum Goal {
RUN("run"), RUN_NO_FORK("runNoFork"), DRY_RUN("dryRun"), DRY_RUN_NO_FORK("dryRunNoFork"), DISCOVER("discover"),
CYCLONEDX("cyclonedx");
private final String maven;
Goal(String maven) {
this.maven = maven;
}
public String getMaven() {
return maven;
}
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2021 - 2023 the original author or authors.
*
* 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
*
* https://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 org.springframework.rewrite.plugin.maven;
import org.springframework.rewrite.plugin.shared.PluginInvocationResult;
import java.nio.file.Path;
/**
* @author Fabian Krüger
*/
public interface RewriteMavenPluginBuilder {
public interface Recipes {
FinalizingBuilder recipes(String... recipeNames);
}
interface FinalizingBuilder {
FinalizingBuilder withDependencies(String... dependencies);
FinalizingBuilder withDebugger(int port, boolean suspend);
FinalizingBuilder withDebug();
FinalizingBuilder withMemory(String s, String s1);
PluginInvocationResult onDir(Path baseDir);
}
}

View File

@@ -13,12 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.rewrite.maven;
package org.springframework.rewrite.plugin.maven;
import org.apache.maven.shared.invoker.InvocationRequest;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.rewrite.plugin.shared.BuildConfig;
import org.springframework.rewrite.plugin.shared.DebugConfig;
import java.nio.file.Path;
import java.util.ArrayList;
@@ -41,7 +43,7 @@ public class MavenInvocationRequestFactoryTest {
};
Path baseDir = Path.of("./testcode/does-not-exist");
InvocationRequest request = sut.createMavenInvocationRequest(baseDir, DebugConfig.disabled(),
InvocationRequest request = sut.createMavenInvocationRequest(baseDir, DebugConfig.disabled(), false,
BuildConfig.defaultConfig(), goals, lineConsumer);
assertThat(request.getGoals()).isEqualTo(goals);
@@ -59,7 +61,7 @@ public class MavenInvocationRequestFactoryTest {
};
Path baseDir = Path.of("./testcode/does-not-exist");
InvocationRequest request = sut.createMavenInvocationRequest(baseDir, DebugConfig.from(1234, true),
InvocationRequest request = sut.createMavenInvocationRequest(baseDir, DebugConfig.from(1234, true), true,
BuildConfig.defaultConfig(), goals, lineConsumer);
assertThat(request.getMavenOpts())
@@ -73,7 +75,7 @@ public class MavenInvocationRequestFactoryTest {
List<String> lines = new ArrayList<>();
Path baseDir = Path.of("./testcode/does-not-exist").toAbsolutePath().normalize();
@NotNull
InvocationRequest result = sut.createMavenInvocationRequest(baseDir, DebugConfig.disabled(),
InvocationRequest result = sut.createMavenInvocationRequest(baseDir, DebugConfig.disabled(), false,
BuildConfig.defaultConfig(), List.of("clean"), lines::add);
}
@@ -87,7 +89,7 @@ public class MavenInvocationRequestFactoryTest {
};
Path baseDir = Path.of("./testcode/does-not-exist");
InvocationRequest request = sut.createMavenInvocationRequest(baseDir, DebugConfig.disabled(),
InvocationRequest request = sut.createMavenInvocationRequest(baseDir, DebugConfig.disabled(), false,
BuildConfig.builder().withMemory("12M", "2G").skipTests(true).build(), goals, lineConsumer);
assertThat(request.getMavenOpts()).isEqualTo("-Xms12M -Xmx2G ");

View File

@@ -0,0 +1,243 @@
/*
* Copyright 2021 - 2023 the original author or authors.
*
* 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
*
* https://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 org.springframework.rewrite.plugin.maven;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.rewrite.plugin.shared.BuildConfig;
import org.springframework.rewrite.plugin.shared.DebugConfig;
import org.springframework.rewrite.plugin.shared.PluginInvocationResult;
import org.springframework.test.util.TestSocketUtils;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
/**
* @author Fabian Krüger
*/
class RewriteMavenPluginTest {
public static final String[] RECIPES = { "org.openrewrite.java.RemoveUnusedImports",
"org.openrewrite.java.format.NormalizeLineBreaks" };
public static final String[] EXTENDED_RECIPES = { "org.openrewrite.java.RemoveUnusedImports",
"org.openrewrite.java.format.NormalizeLineBreaks",
"org.openrewrite.java.migrate.jakarta.MaybeAddJakartaServletApi" };
public static final String EXTENDED_REFCIPES_DEPS = "org.openrewrite.recipe:rewrite-migrate-java:2.9.0";
public static final String[] DEFAULT_GOALS = new String[] { "resources", "compile", "testResources",
"testCompile" };
@Test
@DisplayName("simple project simple config")
void simpleProjectSimpleConfig() {
Path baseDir = Path.of("./testcode/maven-projects/simple").toAbsolutePath().normalize();
PluginInvocationResult result = RewriteMavenPlugin.run().recipes(RECIPES).onDir(baseDir);
String out = result.capturedOutput();
assertThat(result.success()).isTrue();
assertTasksExecuted(out, tasksOf(DEFAULT_GOALS, "run"));
assertRecipesExecuted(out, RECIPES);
}
@Test
@DisplayName("builder runNoFork")
void simpleProjectSimpleConfigRunNoFork() {
Path baseDir = Path.of("./testcode/maven-projects/simple").toAbsolutePath().normalize();
PluginInvocationResult result = RewriteMavenPlugin.runNoFork().recipes(RECIPES).onDir(baseDir);
String out = result.capturedOutput();
assertThat(result.success()).isTrue();
assertTasksExecuted(out, "runNoFork");
assertRecipesExecuted(out, RECIPES);
}
@Test
@DisplayName("builder dryRun")
void simpleProjectSimpleConfigDryRun() {
Path baseDir = Path.of("./testcode/maven-projects/simple").toAbsolutePath().normalize();
PluginInvocationResult result = RewriteMavenPlugin.dryRun().recipes(RECIPES).onDir(baseDir);
String out = result.capturedOutput();
assertThat(result.success()).isTrue();
assertTasksExecuted(out, tasksOf(DEFAULT_GOALS, "dryRun"));
assertRecipesExecuted(out, RECIPES);
}
@Test
@DisplayName("builder dryRunNoFork")
void simpleProjectSimpleConfigDryRunNoFork() {
Path baseDir = Path.of("./testcode/maven-projects/simple").toAbsolutePath().normalize();
PluginInvocationResult result = RewriteMavenPlugin.dryRunNoFork().recipes(RECIPES).onDir(baseDir);
String out = result.capturedOutput();
assertThat(result.success()).isTrue();
assertTasksExecuted(out, "dryRunNoFork");
assertRecipesExecuted(out, RECIPES);
}
@Test
@DisplayName("builder discover")
void simpleProjectSimpleConfigDiscover() {
Path baseDir = Path.of("./testcode/maven-projects/simple").toAbsolutePath().normalize();
PluginInvocationResult result = RewriteMavenPlugin.discover().recipes(RECIPES).onDir(baseDir);
String out = result.capturedOutput();
assertThat(result.success()).isTrue();
assertTasksExecuted(out, "discover");
assertThat(out).contains("[INFO] Available Recipes:");
}
@Test
@DisplayName("builder cyclonedx")
void simpleProjectSimpleConfigCyclonedx() {
Path baseDir = Path.of("./testcode/maven-projects/simple").toAbsolutePath().normalize();
PluginInvocationResult result = RewriteMavenPlugin.cyclonedx().recipes(RECIPES).onDir(baseDir);
String out = result.capturedOutput();
System.out.println(out);
assertThat(result.success()).isTrue();
assertTasksExecuted(out, "cyclonedx");
}
@Test
@DisplayName("simple project full config")
void simpleProjectFullConfig() {
int port = TestSocketUtils.findAvailableTcpPort();
Path baseDir = Path.of("./testcode/maven-projects/simple").toAbsolutePath().normalize();
PluginInvocationResult result = RewriteMavenPlugin.run()
.recipes(EXTENDED_RECIPES)
.withDependencies(EXTENDED_REFCIPES_DEPS)
.withDebugger(port, false)
.withDebug()
.withMemory("1G", "4G")
.onDir(baseDir);
String out = result.capturedOutput();
assertThat(result.success()).isTrue();
assertMemory(out, "1G", "4G");
assertDebugConfig(out, port, false);
assertTasksExecuted(out, tasksOf(DEFAULT_GOALS, "run"));
assertRecipesExecuted(out, EXTENDED_RECIPES);
}
@Test
@DisplayName("execute dryRun")
void executeDryRun() {
int port = TestSocketUtils.findAvailableTcpPort();
Path baseDir = Path.of("./testcode/maven-projects/simple").toAbsolutePath().normalize();
MavenInvocationResult result = RewriteMavenPlugin.execute(baseDir, true, DebugConfig.from(port, false),
BuildConfig.builder().withMemory("1G", "4G").build(), RewriteMavenPlugin.Goal.DRY_RUN,
Arrays.asList(RECIPES), List.of());
String out = result.getCapturedLines();
assertDebugConfig(out, port, false);
assertMemory(out, "1G", "4G");
assertTasksExecuted(out, "dryRun");
}
@Test
@DisplayName("execute dryRunNoFork")
void executeDryRunNoFork() {
Path baseDir = Path.of("./testcode/maven-projects/simple").toAbsolutePath().normalize();
verify(baseDir, RewriteMavenPlugin.Goal.DRY_RUN_NO_FORK, "dryRunNoFork");
}
@Test
@DisplayName("execute run")
void executeRun() {
Path baseDir = Path.of("./testcode/maven-projects/simple").toAbsolutePath().normalize();
verify(baseDir, RewriteMavenPlugin.Goal.RUN, "run");
}
@Test
@DisplayName("execute runNoFork")
void executeRunNoFork() {
Path baseDir = Path.of("./testcode/maven-projects/simple").toAbsolutePath().normalize();
verify(baseDir, RewriteMavenPlugin.Goal.RUN_NO_FORK, "runNoFork");
}
@Test
@DisplayName("execute discover")
void executeDiscover() {
Path baseDir = Path.of("./testcode/maven-projects/simple").toAbsolutePath().normalize();
MavenInvocationResult result = RewriteMavenPlugin.execute(baseDir, false, DebugConfig.disabled(),
BuildConfig.fromDefault(), RewriteMavenPlugin.Goal.DISCOVER, Arrays.asList(EXTENDED_RECIPES),
List.of(EXTENDED_REFCIPES_DEPS));
String out = result.getCapturedLines();
assertThat(result.getExitCode()).isEqualTo(0);
assertTasksExecuted(out, "discover");
assertThat(out).contains("[INFO] Available Recipes:");
}
@Test
@DisplayName("execute cylonedx")
void executeCyclonedx() {
Path baseDir = Path.of("./testcode/maven-projects/simple").toAbsolutePath().normalize();
MavenInvocationResult result = RewriteMavenPlugin.execute(baseDir, false, DebugConfig.disabled(),
BuildConfig.fromDefault(), RewriteMavenPlugin.Goal.CYCLONEDX, Arrays.asList(EXTENDED_RECIPES),
List.of(EXTENDED_REFCIPES_DEPS));
String out = result.getCapturedLines();
assertThat(result.getExitCode()).isEqualTo(0);
assertTasksExecuted(out, "cyclonedx");
}
private void verify(Path baseDir, RewriteMavenPlugin.Goal runNoFork, String expectedGoal) {
MavenInvocationResult result = RewriteMavenPlugin.execute(baseDir, false, DebugConfig.disabled(),
BuildConfig.fromDefault(), runNoFork, Arrays.asList(EXTENDED_RECIPES), List.of(EXTENDED_REFCIPES_DEPS));
String out = result.getCapturedLines();
assertThat(result.getExitCode()).isEqualTo(0);
assertRecipesExecuted(out, EXTENDED_RECIPES);
assertTasksExecuted(out, expectedGoal);
}
private void assertTasksExecuted(String out, String... goals) {
if (goals.length == 0) {
fail("No goals provided");
}
Stream.of(goals).forEach(goal -> assertThat(out).contains(":" + goal + " "));
}
private String[] tasksOf(String[] defaultTasks, String rewriteTask) {
List<String> tasksList = new ArrayList<>();
tasksList.addAll(Arrays.asList(defaultTasks));
tasksList.add(rewriteTask);
return tasksList.toArray(String[]::new);
}
private void assertRecipesExecuted(String out, String... recipes) {
assertThat(out)
.contains("active recipe(s) [%s]".formatted(Stream.of(recipes).collect(Collectors.joining(", "))));
}
private void assertMemory(String out, String min, String max) {
// Setting memory fails in GH Actions under Windows
if (!System.getenv().containsKey("GITHUB_ACTIONS") && !System.getProperty("os.name").contains("Windows")) {
assertThat(out).contains("env.MAVEN_OPTS= -Xms%s -Xmx%s".formatted(min, max));
}
}
private void assertDebugConfig(String out, int port, boolean suspend) {
assertThat(out).contains("Listening for transport dt_socket at address: %s".formatted(port),
"-Xrunjdwp:transport=dt_socket,server=y,suspend=%s,address=%s".formatted(suspend ? "y" : "n", port));
}
}

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.rewrite</groupId>
<artifactId>spring-rewrite-commons-plugin-invoker</artifactId>
<version>0.1.0-SNAPSHOT</version>
</parent>
<artifactId>spring-rewrite-commons-plugin-invoker-polyglot</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.rewrite</groupId>
<artifactId>spring-rewrite-commons-plugin-invoker-maven</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.rewrite</groupId>
<artifactId>spring-rewrite-commons-plugin-invoker-gradle</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,252 @@
/*
* Copyright 2021 - 2023 the original author or authors.
*
* 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
*
* https://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 org.springframework.rewrite.plugin.polyglot;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.rewrite.plugin.gradle.RewriteGradlePlugin;
import org.springframework.rewrite.plugin.gradle.OpenRewriteGradlePluginBuilder;
import org.springframework.rewrite.plugin.maven.RewriteMavenPlugin;
import org.springframework.rewrite.plugin.maven.RewriteMavenPluginBuilder;
import org.springframework.rewrite.plugin.shared.DebugConfig;
import org.springframework.rewrite.plugin.shared.PluginInvocationResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author Fabian Krüger
*/
public class RewritePlugin implements OpenRewritePluginBuilder.GradlePluginVersion, OpenRewritePluginBuilder.Recipes,
OpenRewritePluginBuilder.FinalizingBuilder {
private static final Logger log = LoggerFactory.getLogger(RewriteGradlePlugin.class);
private Action action;
private String gradlePluginVersion;
private List<String> recipes = new ArrayList<>();
private boolean debug;
private DebugConfig debugConfig = DebugConfig.disabled();
private List<String> dependencies = new ArrayList<>();
private boolean preferGradle = false;
private String minMemory;
private String maxMemory;
public static OpenRewritePluginBuilder.GradlePluginVersion dryRun() {
RewritePlugin plugin = new RewritePlugin();
plugin.action = Action.DRY_RUN;
return plugin;
}
public static OpenRewritePluginBuilder.GradlePluginVersion run() {
RewritePlugin plugin = new RewritePlugin();
plugin.action = Action.RUN;
return plugin;
}
public static OpenRewritePluginBuilder.GradlePluginVersion discover() {
RewritePlugin plugin = new RewritePlugin();
plugin.action = Action.DISCOVER;
return plugin;
}
@Override
public OpenRewritePluginBuilder.Recipes gradlePluginVersion(String pluginVersion) {
this.gradlePluginVersion = pluginVersion;
return this;
}
@Override
public OpenRewritePluginBuilder.FinalizingBuilder recipes(String... recipes) {
this.recipes = Arrays.asList(recipes);
return this;
}
@Override
public PluginInvocationResult onDir(Path baseDir) {
PluginInvocationResult result;
boolean mavenProject = isMavenProject(baseDir);
boolean gradleProject = isGradleProject(baseDir);
if (gradleProject && mavenProject && preferGradle) {
result = executeGradle(baseDir);
}
else if (mavenProject) {
result = executeMaven(baseDir);
}
else if (gradleProject) {
result = executeGradle(baseDir);
}
else {
throw new IllegalArgumentException(
"Neither pom.xml, build.gradle, nor build.gradle.kts was found in '%s'".formatted(baseDir));
}
return new PluginInvocationResult(true, result.capturedOutput());
}
private boolean isPolyglotProject(Path baseDir) {
return isMavenProject(baseDir) && isGradleProject(baseDir);
}
private PluginInvocationResult executeGradle(Path baseDir) {
PluginInvocationResult result;
OpenRewriteGradlePluginBuilder.Recipes builder;
switch (action) {
case DRY_RUN -> builder = RewriteGradlePlugin.dryRun();
case DISCOVER -> builder = RewriteGradlePlugin.discover();
default -> builder = RewriteGradlePlugin.run();
}
OpenRewriteGradlePluginBuilder.FinalizingBuilder finalizingBuilder = builder
.recipes(recipes.toArray(String[]::new))
.usingPluginVersion(gradlePluginVersion);
if (minMemory != null) {
finalizingBuilder = finalizingBuilder.withMemory(minMemory, maxMemory);
}
if (debug) {
finalizingBuilder = finalizingBuilder.withDebug();
}
if (!dependencies.isEmpty()) {
finalizingBuilder.withDependencies(this.dependencies.toArray(String[]::new));
}
finalizingBuilder = finalizingBuilder.withDebugConfig(debugConfig);
result = finalizingBuilder.onDir(baseDir);
return result;
}
private PluginInvocationResult executeMaven(Path baseDir) {
PluginInvocationResult result;
RewriteMavenPluginBuilder.Recipes builder;
switch (action) {
case DRY_RUN -> builder = RewriteMavenPlugin.dryRun();
case DISCOVER -> builder = RewriteMavenPlugin.discover();
default -> builder = RewriteMavenPlugin.run();
}
RewriteMavenPluginBuilder.FinalizingBuilder finalizingBuilder = builder
.recipes(this.recipes.toArray(String[]::new));
if (minMemory != null) {
finalizingBuilder = finalizingBuilder.withMemory(minMemory, maxMemory);
}
if (debug) {
finalizingBuilder = finalizingBuilder.withDebug();
}
if (debugConfig != null) {
finalizingBuilder = finalizingBuilder.withDebugger(debugConfig.getPort(), debugConfig.isSuspend());
}
result = finalizingBuilder.onDir(baseDir);
return result;
}
private boolean isGradleProject(Path baseDir) {
return Files.exists(baseDir.resolve("build.gradle")) || Files.exists(baseDir.resolve("build.gradle.kts"));
}
private boolean isMavenProject(Path baseDir) {
return Files.exists(baseDir.resolve("pom.xml"));
}
@Override
public OpenRewritePluginBuilder.FinalizingBuilder dependencies(String... dependencies) {
this.dependencies = Arrays.asList(dependencies);
return this;
}
@Override
public OpenRewritePluginBuilder.FinalizingBuilder withMemory(String minMemory, String maxMemory) {
this.minMemory = minMemory;
this.maxMemory = maxMemory;
return this;
}
@Override
public OpenRewritePluginBuilder.FinalizingBuilder withDebugging() {
return this;
}
@Override
public OpenRewritePluginBuilder.FinalizingBuilder withDebugging(int port, boolean suspend) {
this.debugConfig = DebugConfig.from(port, suspend);
return this;
}
@Override
public OpenRewritePluginBuilder.FinalizingBuilder withDebug() {
this.debug = true;
return this;
}
@Override
public OpenRewritePluginBuilder.FinalizingBuilder preferGradle() {
this.preferGradle = true;
return this;
}
public enum Action {
RUN, DRY_RUN, DISCOVER
}
}
class OpenRewritePluginBuilder {
public interface GradlePluginVersion {
Recipes gradlePluginVersion(String pluginVersion);
}
public interface Recipes {
OpenRewritePluginBuilder.FinalizingBuilder recipes(String... recipes);
}
public interface FinalizingBuilder {
PluginInvocationResult onDir(Path baseDir);
FinalizingBuilder dependencies(String... dependencies);
FinalizingBuilder withMemory(String min, String max);
FinalizingBuilder withDebugging();
FinalizingBuilder withDebugging(int port, boolean suspend);
FinalizingBuilder withDebug();
FinalizingBuilder preferGradle();
}
}

View File

@@ -0,0 +1,288 @@
/*
* Copyright 2021 - 2023 the original author or authors.
*
* 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
*
* https://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 org.springframework.rewrite.plugin.polyglot;
import org.apache.commons.lang3.stream.Streams;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.rewrite.plugin.shared.PluginInvocationResult;
import org.springframework.test.util.TestSocketUtils;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
/**
* @author Fabian Krüger
*/
public class RewritePluginTest {
public static final String[] RECIPES = { "org.openrewrite.java.RemoveUnusedImports",
"org.openrewrite.java.format.NormalizeLineBreaks" };
public static final String[] EXTENDED_RECIPES = Stream
.concat(Stream.of(RECIPES), Stream.of("org.openrewrite.java.migrate.jakarta.MaybeAddJakartaServletApi"))
.toList()
.toArray(String[]::new);
public static final String EXTENDED_RECIPES_DEPS = "org.openrewrite.recipe:rewrite-migrate-java:2.9.0";
@Nested
class PolyglotTests {
@Test
@DisplayName("should prefer Maven over Gradle by default")
void shouldPreferMavenOverGradle() {
Path baseDir = Path.of("./testcode/polyglot-projects/simple-polyglot").toAbsolutePath().normalize();
PluginInvocationResult result = RewritePlugin.run()
.gradlePluginVersion("6.10.0")
.recipes(RECIPES)
.onDir(baseDir);
assertThat(result.capturedOutput()).contains("[INFO] >>> rewrite-maven-plugin:");
}
@Test
@DisplayName("should prefer Gradle over Maven with flag")
void shouldPreferGradleOverMavenWithFlag() {
Path baseDir = Path.of("./testcode/polyglot-projects/simple-polyglot").toAbsolutePath().normalize();
PluginInvocationResult result = RewritePlugin.run()
.gradlePluginVersion("6.10.0")
.recipes(RECIPES)
.preferGradle()
.onDir(baseDir);
assertThat(result.capturedOutput()).contains("> Task :rewriteRun");
}
}
@Nested
class MavenTests {
public static final String[] DEFAULT_GOALS = new String[] { "resources", "compile", "testResources",
"testCompile" };
@Test
@DisplayName("simple Maven run")
void simpleMavenRun() {
Path baseDir = Path.of("./testcode/maven-projects/simple").toAbsolutePath().normalize();
verify(RewritePlugin.run(), baseDir, "run");
}
@Test
@DisplayName("simple Maven dryRun")
void simpleMavenDryRun() {
Path baseDir = Path.of("./testcode/maven-projects/simple").toAbsolutePath().normalize();
verify(RewritePlugin.dryRun(), baseDir, "dryRun");
}
@Test
@DisplayName("simple Maven discover")
void simpleMavenDiscover() {
Path baseDir = Path.of("./testcode/maven-projects/simple").toAbsolutePath().normalize();
PluginInvocationResult result = RewritePlugin.discover()
.gradlePluginVersion("1.2.3")
.recipes(RECIPES)
.onDir(baseDir);
assertBuildSuccessful(result);
String out = result.capturedOutput();
assertGoalsExecuted(out, "discover");
assertThat(out).contains("[INFO] Available Recipes:");
}
@Test
@DisplayName("simple Maven full api")
void simpleMavenFullApi() {
int port = TestSocketUtils.findAvailableTcpPort();
Path baseDir = Path.of("./testcode/maven-projects/simple").toAbsolutePath().normalize();
PluginInvocationResult result = RewritePlugin.run()
.gradlePluginVersion("6.10.0")
.recipes(EXTENDED_RECIPES)
.dependencies(EXTENDED_RECIPES_DEPS)
.withMemory("18M", "280M")
.withDebug()
.withDebugging(port, false)
.onDir(baseDir);
assertBuildSuccessful(result);
String out = result.capturedOutput();
assertGoalsExecuted(out, DEFAULT_GOALS, "run");
assertMemory(out, "18M", "280M");
assertDebugConfig(out, port, false);
assertRecipesExecuted(out, EXTENDED_RECIPES);
}
private void verify(OpenRewritePluginBuilder.GradlePluginVersion rewriteGoalExecution, Path baseDir,
String rewriteGoal) {
PluginInvocationResult result = rewriteGoalExecution.gradlePluginVersion("1.2.3")
.recipes(RECIPES)
.onDir(baseDir);
assertBuildSuccessful(result);
String out = result.capturedOutput();
assertGoalsExecuted(out, DEFAULT_GOALS, rewriteGoal);
assertRecipesExecuted(out, RECIPES);
}
private void assertMemory(String out, String min, String max) {
// Setting memory fails in GH Actions under Windows
if (!System.getenv().containsKey("GITHUB_ACTIONS") && !System.getProperty("os.name").contains("Windows")) {
assertThat(out).contains("env.MAVEN_OPTS= -Xms%s -Xmx%s".formatted(min, max));
}
}
private void assertDebugConfig(String out, int port, boolean suspend) {
assertThat(out).contains("Listening for transport dt_socket at address: %s".formatted(port),
"-Xrunjdwp:transport=dt_socket,server=y,suspend=%s,address=%s".formatted(suspend ? "y" : "n",
port));
}
private void assertBuildSuccessful(PluginInvocationResult result) {
assertThat(result.success()).isTrue();
}
private void assertGoalsExecuted(String out, String... goals) {
if (goals.length == 0) {
fail("No goals provided");
}
Stream.of(goals).forEach(goal -> assertThat(out).contains(":" + goal + " "));
}
private void assertGoalsExecuted(String out, String[] defaultTasks, String rewriteTask) {
String[] tasks = tasksOf(defaultTasks, rewriteTask);
Streams.of(tasks).forEach(t -> assertThat(out).contains(t));
}
private void assertRecipesExecuted(String out, String... recipes) {
assertThat(out)
.contains("active recipe(s) [%s]".formatted(Stream.of(recipes).collect(Collectors.joining(", "))));
}
}
@Nested
class GradleTests {
public static final Path BASE_DIR = Path.of("./testcode/gradle-projects/simple").toAbsolutePath().normalize();
public static final String[] DEFAULT_TASKS = new String[] { "compileJava", "processResources", "classes",
"compileTestJava", "rewriteResolveDependencies" };
@Test
@DisplayName("simple Gradle run")
void simpleGradleRun() {
verify(RewritePlugin.run(), "rewriteRun");
}
@Test
@DisplayName("simple Gradle dryRun")
void simpleGradleDryRun() {
verify(RewritePlugin.dryRun(), "rewriteDryRun");
}
@Test
@DisplayName("simple Gradle discover")
void simpleGradleDiscover() {
PluginInvocationResult result = RewritePlugin.discover()
.gradlePluginVersion("6.10.0")
.recipes(RECIPES)
.onDir(BASE_DIR);
String out = result.capturedOutput();
assertSuccessfulBuild(out);
assertTasksExecuted(out, "rewriteDiscover");
assertThat(out).contains("Available Recipes:");
}
@Test
@DisplayName("simple Gradle full API")
void simpleGradleFullApi() {
int port = TestSocketUtils.findAvailableTcpPort();
Path baseDir = Path.of("./testcode/gradle-projects/simple").toAbsolutePath().normalize();
PluginInvocationResult result = RewritePlugin.run()
.gradlePluginVersion("6.10.0")
.recipes(EXTENDED_RECIPES)
.dependencies(EXTENDED_RECIPES_DEPS)
.withDebug()
.withMemory("64M", "484M")
.withDebugging(port, false)
.onDir(baseDir);
assertThat(result.success()).isTrue();
String out = result.capturedOutput();
assertSuccessfulBuild(out);
assertMemory(out, "64M", "484M");
assertDebugConfig(out, port, false);
assertTasksExecuted(out, DEFAULT_TASKS, "rewriteRun");
assertRecipesExecuted(out, EXTENDED_RECIPES);
}
private void verify(OpenRewritePluginBuilder.GradlePluginVersion run, String run1) {
PluginInvocationResult result = run.gradlePluginVersion("6.10.0").recipes(RECIPES).onDir(BASE_DIR);
String out = result.capturedOutput();
assertSuccessfulBuild(out);
assertTasksExecuted(out, DEFAULT_TASKS, run1);
assertRecipesExecuted(out, RECIPES);
}
private void assertMemory(String out, String min, String max) {
assertThat(out).contains("-Xms%s".formatted(min));
assertThat(out).contains("-Xmx%s".formatted(max));
}
private void assertDebugConfig(String out, int port, boolean suspend) {
assertThat(out).contains("-agentlib:jdwp=transport=dt_socket,server=y,suspend=%s,address=%s"
.formatted(suspend ? "y" : "n", port));
}
private void assertSuccessfulBuild(String out) {
assertThat(out).contains("BUILD SUCCESSFUL in");
}
private void assertTasksExecuted(String out, String[] defaultTasks, String rewriteTask) {
String[] tasks = tasksOf(defaultTasks, rewriteTask);
assertTasksExecuted(out, tasks);
}
private static void assertTasksExecuted(String out, String... tasks) {
List<String> tasksOutput = Stream.of(tasks).map(t -> "Task :" + t).toList();
assertThat(out).contains(tasksOutput);
}
private void assertRecipesExecuted(String out, String[] recipes) {
assertThat(out).containsIgnoringWhitespaces("All sources parsed, running active recipes: "
+ Stream.of(recipes).collect(Collectors.joining(",")));
}
}
private String[] tasksOf(String[] defaultTasks, String rewriteTask) {
List<String> tasksList = new ArrayList<>();
tasksList.addAll(Arrays.asList(defaultTasks));
tasksList.add(rewriteTask);
return tasksList.toArray(String[]::new);
}
}

View File

@@ -0,0 +1,42 @@
.gradle
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
out/
!**/src/main/**/out/
!**/src/test/**/out/
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store

View File

@@ -0,0 +1,19 @@
plugins {
id("java")
}
group = "org.example"
version = "1.0-SNAPSHOT"
repositories {
mavenCentral()
}
dependencies {
testImplementation(platform("org.junit:junit-bom:5.9.1"))
testImplementation("org.junit.jupiter:junit-jupiter")
}
tasks.test {
useJUnitPlatform()
}

View File

@@ -0,0 +1,6 @@
#Wed Mar 13 15:37:19 CET 2024
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@@ -0,0 +1,234 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# 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
#
# https://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.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

View File

@@ -0,0 +1,89 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@@ -0,0 +1,10 @@
package org.example;
/**
* @author Fabian Krüger
*/
public class Main {
public static void main(String[] args) {
System.out.println("Hello world!");
}
}

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example.maven</groupId>
<artifactId>simple</artifactId>
<version>0.1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>

View File

@@ -0,0 +1,43 @@
.gradle
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
out/
!**/src/main/**/out/
!**/src/test/**/out/
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store
/target/

View File

@@ -0,0 +1,19 @@
plugins {
id("java")
}
group = "org.example"
version = "1.0.0-SNAPSHOT"
repositories {
mavenCentral()
}
dependencies {
testImplementation(platform("org.junit:junit-bom:5.9.1"))
testImplementation("org.junit.jupiter:junit-jupiter")
}
tasks.test {
useJUnitPlatform()
}

View File

@@ -0,0 +1,6 @@
#Fri Mar 15 13:11:41 CET 2024
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@@ -0,0 +1,234 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# 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
#
# https://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.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

View File

@@ -0,0 +1,89 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>simple-ployglot</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>

View File

@@ -0,0 +1,10 @@
package org.example;
/**
* @author Fabian Krüger
*/
public class Main {
public static void main(String[] args) {
System.out.println("Hello world!");
}
}

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.rewrite</groupId>
<artifactId>spring-rewrite-commons-plugin-invoker</artifactId>
<version>0.1.0-SNAPSHOT</version>
</parent>
<artifactId>spring-rewrite-commons-plugin-invoker-shared</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.rewrite.maven;
package org.springframework.rewrite.plugin.shared;
/**
* @author Fabian Krüger
@@ -46,7 +46,11 @@ public class BuildConfig {
return new Builder();
}
boolean isSkipTests() {
public static BuildConfig fromDefault() {
return new BuildConfig(true, MemorySettings.noop());
}
public boolean isSkipTests() {
return skipTests;
}
@@ -54,11 +58,15 @@ public class BuildConfig {
return memorySettings;
}
public boolean hasMemorySettings() {
return memorySettings != null && memorySettings.getMin() != null;
}
public static class Builder {
private boolean skipTests;
private MemorySettings memorySettings = MemorySettings.of("31M", "256M");
private MemorySettings memorySettings = MemorySettings.of("256M", "1024M");
public Builder skipTests(boolean b) {
this.skipTests = b;

View File

@@ -13,24 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.rewrite.maven;
import org.apache.maven.shared.invoker.*;
package org.springframework.rewrite.plugin.shared;
/**
* @author Fabian Krüger
*/
public class MavenInvoker {
public enum BuildTool {
private Invoker invoker = new DefaultInvoker();
public InvocationResult invoke(InvocationRequest request) {
try {
return invoker.execute(request);
}
catch (MavenInvocationException e) {
throw new RuntimeException(e);
}
}
GRADLE, MAVEN
}

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.rewrite.maven;
package org.springframework.rewrite.plugin.shared;
/**
* @author Fabian Krüger
@@ -48,7 +48,11 @@ public class DebugConfig {
return port;
}
public char isSuspend() {
public boolean isSuspend() {
return suspend;
}
public char isSuspendEnabled() {
return suspend ? 'y' : 'n';
}

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.rewrite.maven;
package org.springframework.rewrite.plugin.shared;
/**
* @author Fabian Krüger
@@ -33,6 +33,10 @@ public class MemorySettings {
return new MemorySettings(min, max);
}
public static MemorySettings noop() {
return new MemorySettings(null, null);
}
public String getMin() {
return min;
}

View File

@@ -13,24 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.rewrite.maven;
package org.springframework.rewrite.plugin.shared;
/**
* @author Fabian Krüger
*/
public enum RewritePluginGoals {
RUN("run"), RUN_NO_FORK("runNoFork"), DRY_RUN("dryRun"), DRY_RUN_NO_FORK("dryRunNoFork"), DISCOVER("discover"),
CYCLONEDX("cyclonedx");
private final String goalName;
RewritePluginGoals(String goalName) {
this.goalName = goalName;
}
public String getGoalName() {
return goalName;
}
public record PluginInvocationResult(boolean success, String capturedOutput) {
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2021 - 2023 the original author or authors.
*
* 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
*
* https://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 org.springframework.rewrite.plugin.shared;
/**
* @author Fabian Krüger
*/
public enum RewritePluginGoals {
RUN("run", "rewriteRun"), RUN_NO_FORK("runNoFork", "rewriteRun"), DRY_RUN("dryRun", "rewriteDryRun"),
DRY_RUN_NO_FORK("dryRunNoFork", "rewriteDryRun"), DISCOVER("discover", "rewriteDiscover"),
CYCLONEDX("cyclonedx", null);
private final String maven;
private final String gradle;
RewritePluginGoals(String maven, String gradle) {
this.maven = maven;
this.gradle = gradle;
}
public String getMaven() {
return maven;
}
public String getGradle() {
return gradle;
}
}