From d516daf277ae7a4740bb7ac5717746944722402b Mon Sep 17 00:00:00 2001 From: Andy Damevin Date: Tue, 21 Mar 2023 16:25:50 +0100 Subject: [PATCH] Add rewrite support to update --- .../src/main/java/io/quarkus/cli/Update.java | 13 +- .../quarkus/cli/build/BuildSystemRunner.java | 5 +- .../io/quarkus/cli/build/GradleRunner.java | 4 +- .../io/quarkus/cli/build/JBangRunner.java | 4 +- .../io/quarkus/cli/build/MavenRunner.java | 27 ++- .../io/quarkus/cli/update/RewriteGroup.java | 23 ++ .../quarkus/gradle/tasks/QuarkusUpdate.java | 2 +- .../java/io/quarkus/maven/UpdateMojo.java | 52 ++++- .../devtools/commands/UpdateProject.java | 28 ++- .../handlers/UpdateProjectCommandHandler.java | 75 ++++--- .../project/update/QuarkusUpdateCommand.java | 207 ++++++++++++++++++ .../update/QuarkusUpdateException.java | 14 ++ .../project/update/QuarkusUpdates.java | 6 +- .../update/QuarkusUpdatesRepository.java | 11 +- 14 files changed, 410 insertions(+), 61 deletions(-) create mode 100644 devtools/cli/src/main/java/io/quarkus/cli/update/RewriteGroup.java create mode 100644 independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/QuarkusUpdateCommand.java create mode 100644 independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/QuarkusUpdateException.java diff --git a/devtools/cli/src/main/java/io/quarkus/cli/Update.java b/devtools/cli/src/main/java/io/quarkus/cli/Update.java index 617f0db87fa1e..c017b3ba91b26 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/Update.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/Update.java @@ -5,24 +5,29 @@ import io.quarkus.cli.build.BaseBuildCommand; import io.quarkus.cli.build.BuildSystemRunner; import io.quarkus.cli.common.TargetQuarkusVersionGroup; +import io.quarkus.cli.update.RewriteGroup; import picocli.CommandLine; -@CommandLine.Command(name = "update", sortOptions = false, showDefaultValues = true, mixinStandardHelpOptions = false, header = "Suggest recommended project updates with the possibility to apply them.", headerHeading = "%n", commandListHeading = "%nCommands:%n", synopsisHeading = "%nUsage: ", parameterListHeading = "%n", optionListHeading = "%nOptions:%n") +@CommandLine.Command(name = "update", aliases = { "up", + "upgrade" }, sortOptions = false, showDefaultValues = true, mixinStandardHelpOptions = false, header = "Suggest recommended project updates with the possibility to apply them.", headerHeading = "%n", commandListHeading = "%nCommands:%n", synopsisHeading = "%nUsage: ", parameterListHeading = "%n", optionListHeading = "%nOptions:%n") public class Update extends BaseBuildCommand implements Callable { @CommandLine.ArgGroup(order = 0, heading = "%nTarget Quarkus version:%n", multiplicity = "0..1") TargetQuarkusVersionGroup targetQuarkusVersion = new TargetQuarkusVersionGroup(); - @CommandLine.Option(order = 1, names = { "--per-module" }, description = "Display information per project module.") + @CommandLine.ArgGroup(order = 1, heading = "%nRewrite:%n") + RewriteGroup rewrite = new RewriteGroup(); + + @CommandLine.Option(order = 2, names = { "--per-module" }, description = "Display information per project module.") public boolean perModule = false; @Override public Integer call() throws Exception { try { final BuildSystemRunner runner = getRunner(); - return runner.updateProject(targetQuarkusVersion.platformVersion, targetQuarkusVersion.streamId, perModule); + return runner.updateProject(targetQuarkusVersion, rewrite, perModule); } catch (Exception e) { - return output.handleCommandException(e, "Unable to collect Quarkus project information: " + e.getMessage()); + return output.handleCommandException(e, "Unable to run Quarkus project update : " + e.getMessage()); } } } diff --git a/devtools/cli/src/main/java/io/quarkus/cli/build/BuildSystemRunner.java b/devtools/cli/src/main/java/io/quarkus/cli/build/BuildSystemRunner.java index 3744908121649..278698d6b7f6a 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/build/BuildSystemRunner.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/build/BuildSystemRunner.java @@ -19,7 +19,9 @@ import io.quarkus.cli.common.OutputOptionMixin; import io.quarkus.cli.common.PropertiesOptions; import io.quarkus.cli.common.RunModeOption; +import io.quarkus.cli.common.TargetQuarkusVersionGroup; import io.quarkus.cli.registry.RegistryClientMixin; +import io.quarkus.cli.update.RewriteGroup; import io.quarkus.devtools.project.BuildTool; import picocli.CommandLine; @@ -102,7 +104,8 @@ Integer listExtensions(RunModeOption runMode, ListFormatOptions format, boolean Integer projectInfo(boolean perModule) throws Exception; - Integer updateProject(String targetPlatformVersion, String targetPlatformStream, boolean perModule) throws Exception; + Integer updateProject(TargetQuarkusVersionGroup targetQuarkusVersion, RewriteGroup rewrite, boolean perModule) + throws Exception; BuildCommandArgs prepareAction(String action, BuildOptions buildOptions, RunModeOption runMode, List params); diff --git a/devtools/cli/src/main/java/io/quarkus/cli/build/GradleRunner.java b/devtools/cli/src/main/java/io/quarkus/cli/build/GradleRunner.java index e61a8593f4ab5..8afc522c1aa30 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/build/GradleRunner.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/build/GradleRunner.java @@ -19,7 +19,9 @@ import io.quarkus.cli.common.OutputOptionMixin; import io.quarkus.cli.common.PropertiesOptions; import io.quarkus.cli.common.RunModeOption; +import io.quarkus.cli.common.TargetQuarkusVersionGroup; import io.quarkus.cli.registry.RegistryClientMixin; +import io.quarkus.cli.update.RewriteGroup; import io.quarkus.devtools.messagewriter.MessageWriter; import io.quarkus.devtools.project.BuildTool; import io.quarkus.registry.config.RegistriesConfigLocator; @@ -136,7 +138,7 @@ public Integer projectInfo(boolean perModule) { } @Override - public Integer updateProject(String targetPlatformVersion, String targetPlatformStream, boolean perModule) + public Integer updateProject(TargetQuarkusVersionGroup targetQuarkusVersion, RewriteGroup rewrite, boolean perModule) throws Exception { MessageWriter.info().error( "quarkus update is not yet available for Gradle projects. See https://github.com/quarkusio/quarkus/issues/31658 for more information."); diff --git a/devtools/cli/src/main/java/io/quarkus/cli/build/JBangRunner.java b/devtools/cli/src/main/java/io/quarkus/cli/build/JBangRunner.java index 070cb7f5487be..704d49204812e 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/build/JBangRunner.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/build/JBangRunner.java @@ -15,7 +15,9 @@ import io.quarkus.cli.common.OutputOptionMixin; import io.quarkus.cli.common.PropertiesOptions; import io.quarkus.cli.common.RunModeOption; +import io.quarkus.cli.common.TargetQuarkusVersionGroup; import io.quarkus.cli.registry.RegistryClientMixin; +import io.quarkus.cli.update.RewriteGroup; import io.quarkus.devtools.project.BuildTool; public class JBangRunner implements BuildSystemRunner { @@ -75,7 +77,7 @@ public Integer projectInfo(boolean perModule) { } @Override - public Integer updateProject(String targetPlatformVersion, String targetPlatformStream, boolean perModule) + public Integer updateProject(TargetQuarkusVersionGroup targetQuarkusVersion, RewriteGroup rewrite, boolean perModule) throws Exception { throw new UnsupportedOperationException("Not there yet. ;)"); } diff --git a/devtools/cli/src/main/java/io/quarkus/cli/build/MavenRunner.java b/devtools/cli/src/main/java/io/quarkus/cli/build/MavenRunner.java index bd0c1efd5c318..a5370f51def63 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/build/MavenRunner.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/build/MavenRunner.java @@ -20,7 +20,9 @@ import io.quarkus.cli.common.OutputOptionMixin; import io.quarkus.cli.common.PropertiesOptions; import io.quarkus.cli.common.RunModeOption; +import io.quarkus.cli.common.TargetQuarkusVersionGroup; import io.quarkus.cli.registry.RegistryClientMixin; +import io.quarkus.cli.update.RewriteGroup; import io.quarkus.devtools.commands.AddExtensions; import io.quarkus.devtools.commands.ListCategories; import io.quarkus.devtools.commands.ListExtensions; @@ -147,7 +149,7 @@ public Integer projectInfo(boolean perModule) throws Exception { } @Override - public Integer updateProject(String targetPlatformVersion, String targetPlatformStream, boolean perModule) + public Integer updateProject(TargetQuarkusVersionGroup targetQuarkusVersion, RewriteGroup rewrite, boolean perModule) throws Exception { ArrayDeque args = new ArrayDeque<>(); setMavenProperties(args, true); @@ -157,14 +159,25 @@ public Integer updateProject(String targetPlatformVersion, String targetPlatform QuarkusProjectHelper.artifactResolver(), MessageWriter.info()); final Properties props = ToolsUtils.readQuarkusProperties(extensionCatalog); args.add(ToolsUtils.getPluginKey(props) + ":" + ToolsUtils.getMavenPluginVersion(props) + ":update"); - if (targetPlatformVersion != null) { - args.add("-DplatformVersion=" + targetPlatformVersion); + args.add("-e"); + args.add("-N"); + if (targetQuarkusVersion.platformVersion != null) { + args.add("-DplatformVersion=" + targetQuarkusVersion.platformVersion); } - if (targetPlatformStream != null) { - args.add("-Dstream=" + targetPlatformStream); + if (targetQuarkusVersion.streamId != null) { + args.add("-Dstream=" + targetQuarkusVersion.streamId); } - if (perModule) { - args.add("-DperModule"); + if (rewrite.noRewrite) { + args.add("-DnoRewrite"); + } + if (rewrite.pluginVersion != null) { + args.add("-DrewritePluginVersion=" + rewrite.pluginVersion); + } + if (rewrite.updateRecipesVersion != null) { + args.add("-DupdateRecipesVersion=" + rewrite.updateRecipesVersion); + } + if (rewrite.dryRun) { + args.add("-DrewriteDryRun"); } args.add("-ntp"); return run(prependExecutable(args)); diff --git a/devtools/cli/src/main/java/io/quarkus/cli/update/RewriteGroup.java b/devtools/cli/src/main/java/io/quarkus/cli/update/RewriteGroup.java new file mode 100644 index 0000000000000..48bd2a3a94c10 --- /dev/null +++ b/devtools/cli/src/main/java/io/quarkus/cli/update/RewriteGroup.java @@ -0,0 +1,23 @@ +package io.quarkus.cli.update; + +import picocli.CommandLine; + +public class RewriteGroup { + + @CommandLine.Option(order = 0, names = { + "--no-rewrite" }, description = "Disable the rewrite feature.", defaultValue = "false") + public boolean noRewrite = false; + + @CommandLine.Option(order = 1, names = { + "--dry-run" }, description = "Rewrite in dry-mode.", defaultValue = "false") + public boolean dryRun = false; + + @CommandLine.Option(order = 2, names = { + "--update-recipes-version" }, description = "Use a custom io.quarkus:quarkus-update-recipes version. This artifact contains the base recipes used by this tool to update a project.") + public String updateRecipesVersion; + + @CommandLine.Option(order = 3, names = { + "--rewrite-plugin-version" }, description = "Use a custom OpenRewrite plugin version.") + public String pluginVersion; + +} diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusUpdate.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusUpdate.java index 87555391c58a3..733ddda160afa 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusUpdate.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusUpdate.java @@ -89,7 +89,7 @@ public void logUpdates() { invoker.targetPlatformVersion(targetPlatformVersion); invoker.perModule(perModule); // This is currently not supported with gradle - invoker.generateRewriteConfig(false); + invoker.noRewrite(true); invoker.appModel(extension().getApplicationModel()); try { invoker.execute(); diff --git a/devtools/maven/src/main/java/io/quarkus/maven/UpdateMojo.java b/devtools/maven/src/main/java/io/quarkus/maven/UpdateMojo.java index d75b0d13884d5..7474e34d4376e 100644 --- a/devtools/maven/src/main/java/io/quarkus/maven/UpdateMojo.java +++ b/devtools/maven/src/main/java/io/quarkus/maven/UpdateMojo.java @@ -1,14 +1,21 @@ package io.quarkus.maven; +import static org.twdata.maven.mojoexecutor.MojoExecutor.*; + import java.util.List; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.plugin.BuildPluginManager; import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugins.annotations.Component; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import io.quarkus.devtools.commands.UpdateProject; import io.quarkus.devtools.commands.data.QuarkusCommandException; +import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; import io.quarkus.devtools.project.QuarkusProject; +import io.quarkus.devtools.project.update.QuarkusUpdateCommand; import io.quarkus.maven.dependency.ArtifactCoords; import io.quarkus.registry.RegistryResolutionException; import io.quarkus.registry.catalog.ExtensionCatalog; @@ -39,12 +46,43 @@ public class UpdateMojo extends QuarkusProjectStateMojoBase { @Parameter(property = "platformVersion", required = false) private String platformVersion; + /** + * The OpenRewrite plugin version + */ + @Parameter(property = "rewritePluginVersion", required = true, defaultValue = QuarkusUpdateCommand.DEFAULT_MAVEN_REWRITE_PLUGIN_VERSION) + private String rewritePluginVersion; + + /** + * Disable the rewrite feature. + */ + @Parameter(property = "noRewrite", required = false, defaultValue = "false") + private Boolean noRewrite; + + /** + * Rewrite in dry-mode. + */ + @Parameter(property = "rewriteDryRun", required = false, defaultValue = "false") + private Boolean rewriteDryRun; + + /** + * "The io.quarkus:quarkus-update-recipes version. This artifact contains the base recipes used by this tool to update a + * project. + */ + @Parameter(property = "updateRecipesVersion", required = false) + private String rewriteUpdateRecipesVersion; + /** * Target stream (e.g: 2.0) */ @Parameter(property = "stream", required = false) private String stream; + @Component + private MavenSession mavenSession; + + @Component + private BuildPluginManager pluginManager; + @Override protected void validateParameters() throws MojoExecutionException { getLog().warn("quarkus:update goal is experimental, its options and output might change in future versions"); @@ -78,9 +116,21 @@ protected void processProjectState(QuarkusProject quarkusProject) throws MojoExe invoker.targetPlatformVersion(platformVersion); invoker.perModule(perModule); invoker.appModel(resolveApplicationModel()); + if (rewritePluginVersion != null) { + invoker.rewritePluginVersion(rewritePluginVersion); + } + if (rewriteUpdateRecipesVersion != null) { + invoker.rewritePluginVersion(rewriteUpdateRecipesVersion); + } + invoker.rewriteDryRun(rewriteDryRun); + invoker.noRewrite(noRewrite); try { - invoker.execute(); + final QuarkusCommandOutcome result = invoker.execute(); + if (!result.isSuccess()) { + throw new MojoExecutionException( + "The command did not succeed."); + } } catch (QuarkusCommandException e) { throw new MojoExecutionException("Failed to resolve the available updates", e); } diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/UpdateProject.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/UpdateProject.java index 13ccd0cf77c1d..b11ad26840c02 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/UpdateProject.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/UpdateProject.java @@ -21,9 +21,13 @@ public class UpdateProject { public static final String APP_MODEL = "quarkus.update-project.app-model"; public static final String LATEST_CATALOG = "quarkus.update-project.latest-catalog"; public static final String PER_MODULE = "quarkus.update-project.per-module"; - - public static final String GENERATE_REWRITE_CONFIG = "quarkus.update-project.generate-update-config"; + public static final String NO_REWRITE = "quarkus.update-project.rewrite.disabled"; public static final String TARGET_PLATFORM_VERSION = "quarkus.update-project.target-platform-version"; + public static final String REWRITE_PLUGIN_VERSION = "quarkus.update-project.rewrite.plugin-version"; + public static final String REWRITE_UPDATE_RECIPES_VERSION = "quarkus.update-project.rewrite.update-recipes-version"; + public static final String REWRITE_DRY_RUN = "quarkus.update-project.rewrite.dry-run"; + + public static final String OUTCOME_REWRITE_UPDATE_SCRIPT = "quarkus.update-project.outcome.rewrite.update-script"; private final QuarkusCommandInvocation invocation; private final UpdateProjectCommandHandler handler = new UpdateProjectCommandHandler(); @@ -46,8 +50,24 @@ public UpdateProject appModel(ApplicationModel applicationModel) { return this; } - public UpdateProject generateRewriteConfig(boolean generateUpdateConfig) { - invocation.setValue(GENERATE_REWRITE_CONFIG, generateUpdateConfig); + public UpdateProject noRewrite(boolean noRewrite) { + invocation.setValue(NO_REWRITE, noRewrite); + return this; + } + + public UpdateProject rewritePluginVersion(String rewritePluginVersion) { + invocation.setValue(REWRITE_PLUGIN_VERSION, requireNonNull(rewritePluginVersion, "rewritePluginVersion is required")); + return this; + } + + public UpdateProject rewriteUpdateRecipesVersion(String rewriteUpdateRecipesVersion) { + invocation.setValue(REWRITE_UPDATE_RECIPES_VERSION, + requireNonNull(rewriteUpdateRecipesVersion, "rewriteUpdateRecipesVersion is required")); + return this; + } + + public UpdateProject rewriteDryRun(boolean rewriteDryRun) { + invocation.setValue(REWRITE_DRY_RUN, rewriteDryRun); return this; } diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/UpdateProjectCommandHandler.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/UpdateProjectCommandHandler.java index 86697c42f2007..e1a4afd78b8c9 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/UpdateProjectCommandHandler.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/UpdateProjectCommandHandler.java @@ -1,7 +1,5 @@ package io.quarkus.devtools.commands.handlers; -import static io.quarkus.devtools.project.update.QuarkusUpdateRecipe.RECIPE_IO_QUARKUS_OPENREWRITE_QUARKUS; - import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -13,7 +11,6 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Set; import java.util.stream.Collectors; import io.quarkus.bootstrap.model.ApplicationModel; @@ -23,11 +20,14 @@ import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; import io.quarkus.devtools.commands.handlers.ProjectInfoCommandHandler.PlatformInfo; import io.quarkus.devtools.messagewriter.MessageWriter; +import io.quarkus.devtools.project.QuarkusProject; import io.quarkus.devtools.project.QuarkusProjectHelper; import io.quarkus.devtools.project.state.ExtensionProvider; import io.quarkus.devtools.project.state.ModuleState; import io.quarkus.devtools.project.state.ProjectState; import io.quarkus.devtools.project.state.TopExtensionDependency; +import io.quarkus.devtools.project.update.QuarkusUpdateCommand; +import io.quarkus.devtools.project.update.QuarkusUpdateException; import io.quarkus.devtools.project.update.QuarkusUpdates; import io.quarkus.maven.dependency.ArtifactCoords; import io.quarkus.maven.dependency.ArtifactKey; @@ -45,6 +45,7 @@ public class UpdateProjectCommandHandler implements QuarkusCommandHandler { public static final String REMOVE = "Remove:"; public static final String UPDATE = "Update:"; public static final String ITEM_FORMAT = "%-7s %s"; + public static final String DEFAULT_UPDATE_RECIPES_VERSION = "LATEST"; @Override public QuarkusCommandOutcome execute(QuarkusCommandInvocation invocation) throws QuarkusCommandException { @@ -53,12 +54,10 @@ public QuarkusCommandOutcome execute(QuarkusCommandInvocation invocation) throws final String targetPlatformVersion = invocation.getValue(UpdateProject.TARGET_PLATFORM_VERSION); final boolean perModule = invocation.getValue(UpdateProject.PER_MODULE, false); - final boolean generateRewriteConfig = invocation.getValue(UpdateProject.GENERATE_REWRITE_CONFIG, true); - final ProjectState currentState = ProjectInfoCommandHandler.resolveProjectState(appModel, invocation.getQuarkusProject().getExtensionsCatalog()); final ArtifactCoords projectQuarkusPlatformBom = getProjectQuarkusPlatformBOM(currentState); - + final QuarkusCommandOutcome outcome = QuarkusCommandOutcome.success(); if (projectQuarkusPlatformBom == null) { invocation.log().error("The project does not import any Quarkus platform BOM"); return QuarkusCommandOutcome.failure(); @@ -68,42 +67,50 @@ public QuarkusCommandOutcome execute(QuarkusCommandInvocation invocation) throws } else { invocation.log().info("Instructions to update this project from '%s' to '%s':", projectQuarkusPlatformBom.getVersion(), targetPlatformVersion); - logUpdates(currentState, latestCatalog, false, perModule, invocation.getQuarkusProject().log()); - - if (generateRewriteConfig) { - Set additionalSourceFiles = Set.of("**/META-INF/services/**", - "**/*.txt", - "**/*.adoc", - "**/*.md", - "**/src/main/codestarts/**/*.java", - "**/src/test/resources/__snapshots__/**/*.java", - "**/*.kt"); + final QuarkusProject quarkusProject = invocation.getQuarkusProject(); + logUpdates(currentState, latestCatalog, false, perModule, quarkusProject.log()); + final boolean noRewrite = invocation.getValue(UpdateProject.NO_REWRITE, false); + if (!noRewrite) { QuarkusUpdates.ProjectUpdateRequest request = new QuarkusUpdates.ProjectUpdateRequest( projectQuarkusPlatformBom.getVersion(), targetPlatformVersion); + Path recipe = null; try { - final Path tempFile = Files.createTempFile("quarkus-project-recipe-", ".yaml"); - QuarkusUpdates.createRecipe(tempFile, - QuarkusProjectHelper.artifactResolver(), request); - invocation.log().info("Project update recipe has been created:\n%s", tempFile.toString()); - - invocation.log().info("The commands to update this project: \n" + - "./mvnw -e org.openrewrite.maven:rewrite-maven-plugin:4.39.0:run \\\n" + - " -D\"rewrite.configLocation=%s\" \\\n" + - " -D\"activeRecipes=%s\" \\\n" + - " -D\"plainTextMasks=%s\" \\\n" + - " -D\"rewrite.pomCacheEnabled=false\" \n" + - "./mvnw process-sources", - tempFile.toString(), RECIPE_IO_QUARKUS_OPENREWRITE_QUARKUS, - String.join(",", additionalSourceFiles)); - + recipe = Files.createTempFile("quarkus-project-recipe-", ".yaml"); + final String updateRecipesVersion = invocation.getValue(UpdateProject.REWRITE_UPDATE_RECIPES_VERSION, + DEFAULT_UPDATE_RECIPES_VERSION); + + QuarkusUpdates.createRecipe(recipe, + QuarkusProjectHelper.artifactResolver(), updateRecipesVersion, request); + + outcome.setValue(UpdateProject.OUTCOME_REWRITE_UPDATE_SCRIPT, recipe); + + final String rewritePluginVersion = invocation.getValue(UpdateProject.REWRITE_PLUGIN_VERSION); + final boolean rewriteDryRun = invocation.getValue(UpdateProject.REWRITE_DRY_RUN, false); + QuarkusUpdateCommand.handle( + invocation.log(), + quarkusProject.getExtensionManager().getBuildTool(), + quarkusProject.getProjectDirPath(), + rewritePluginVersion, + recipe, + rewriteDryRun); } catch (IOException e) { - throw new QuarkusCommandException("Failed to create project update recipe", e); + throw new QuarkusCommandException("Error while generating the project update script", e); + } catch (QuarkusUpdateException e) { + throw new QuarkusCommandException("Error while running the project update scrip", e); + } finally { + if (recipe != null) { + try { + Files.deleteIfExists(recipe); + } catch (IOException e) { + // ignore + } + } + } } } - - return QuarkusCommandOutcome.success(); + return outcome; } private static ArtifactCoords getProjectQuarkusPlatformBOM(ProjectState currentState) { diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/QuarkusUpdateCommand.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/QuarkusUpdateCommand.java new file mode 100644 index 0000000000000..8c5de244f32f5 --- /dev/null +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/QuarkusUpdateCommand.java @@ -0,0 +1,207 @@ +package io.quarkus.devtools.project.update; + +import static io.quarkus.devtools.project.update.QuarkusUpdateRecipe.RECIPE_IO_QUARKUS_OPENREWRITE_QUARKUS; + +import java.io.BufferedReader; +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Set; +import java.util.stream.Stream; + +import io.quarkus.devtools.messagewriter.MessageWriter; +import io.quarkus.devtools.project.BuildTool; + +public class QuarkusUpdateCommand { + + public static final String MAVEN_REWRITE_PLUGIN_GROUP = "org.openrewrite.maven"; + public static final String MAVEN_REWRITE_PLUGIN_ARTIFACT = "rewrite-maven-plugin"; + public static final String DEFAULT_MAVEN_REWRITE_PLUGIN_VERSION = "4.41.0"; + public static Set ADDITIONAL_SOURCE_FILES_SET = Set.of("**/META-INF/services/**", + "**/*.txt", + "**/*.adoc", + "**/*.md", + "**/src/main/codestarts/**/*.java", + "**/src/test/resources/__snapshots__/**/*.java", + "**/*.kt"); + + public static String ADDITIONAL_SOURCE_FILES = String.join(",", ADDITIONAL_SOURCE_FILES_SET); + + public static String goal(boolean dryRun) { + return dryRun ? "dryRun" : "run"; + } + + public static void handle(MessageWriter log, BuildTool buildTool, Path baseDir, + String rewritePluginVersion, Path recipe, boolean dryRun) throws QuarkusUpdateException { + switch (buildTool) { + case MAVEN: + runMavenUpdate(log, baseDir, rewritePluginVersion, recipe, dryRun); + break; + default: + throw new QuarkusUpdateException(buildTool.getKey() + " is not supported yet"); + } + } + + private static void printMavenUpdateCommand(MessageWriter log, Path baseDir, String rewritePluginVersion, Path recipe) + throws QuarkusUpdateException { + final String mvnBinary = findMvnBinary(baseDir); + final String[] mavenUpdateCommand = getMavenUpdateCommand(mvnBinary, rewritePluginVersion, recipe, false); + final String[] mavenProcessSourcesCommand = getMavenProcessSourcesCommand(mvnBinary); + + log.info(String.join(" ", mavenUpdateCommand).replace(" -D", "\\n-D")); + log.info(String.join(" ", mavenProcessSourcesCommand)); + } + + private static void runMavenUpdate(MessageWriter log, Path baseDir, String rewritePluginVersion, Path recipe, + boolean dryRun) + throws QuarkusUpdateException { + final String mvnBinary = findMvnBinary(baseDir); + executeCommand(getMavenUpdateCommand(mvnBinary, rewritePluginVersion, recipe, dryRun), log); + + // format the sources + executeCommand(getMavenProcessSourcesCommand(mvnBinary), log); + } + + private static String[] getMavenProcessSourcesCommand(String mvnBinary) { + return new String[] { mvnBinary, "process-sources" }; + } + + private static String[] getMavenUpdateCommand(String mvnBinary, String rewritePluginVersion, Path recipe, boolean dryRun) { + return new String[] { mvnBinary, + "-e", + String.format("%s:%s:%s:%s", MAVEN_REWRITE_PLUGIN_GROUP, MAVEN_REWRITE_PLUGIN_ARTIFACT, rewritePluginVersion, + dryRun ? "dryRun" : "run"), + String.format("-D\"plainTextMasks=%s\"", ADDITIONAL_SOURCE_FILES), + String.format("-D\"rewrite.configLocation=%s\"", recipe.toAbsolutePath()), + String.format("-D\"activeRecipes=%s\"", RECIPE_IO_QUARKUS_OPENREWRITE_QUARKUS), + "-D\"rewrite.pomCacheEnabled=false\"" }; + } + + private static void executeCommand(String[] command, MessageWriter log) throws QuarkusUpdateException { + ProcessBuilder processBuilder = new ProcessBuilder(); + log.info(""); + log.info(""); + log.info(""); + log.info(" ------------------------------------------------------------------------"); + log.info("Executing:\n" + String.join(" ", command)); + log.info(""); + processBuilder.command(command); + + try { + Process process = processBuilder.redirectOutput(ProcessBuilder.Redirect.INHERIT) + .redirectError(ProcessBuilder.Redirect.INHERIT).start(); + + BufferedReader reader = new BufferedReader(new java.io.InputStreamReader(process.getInputStream())); + + String line; + while ((line = reader.readLine()) != null) { + log.info(line); + } + + log.info(""); + log.info(""); + log.info(""); + + int exitCode = process.waitFor(); + if (exitCode != 0) { + throw new QuarkusUpdateException("The command to update the project exited with an error: " + exitCode); + } + } catch (Exception e) { + throw new QuarkusUpdateException("Error while executing command to udpate the project", e); + } + } + + static String findMvnBinary(Path baseDir) throws QuarkusUpdateException { + Path mavenCmd = findWrapperOrBinary(baseDir, "mvnw", "mvn"); + if (mavenCmd == null) { + throw new QuarkusUpdateException("Cannot locate mvnw or mvn" + + ". Make sure mvnw is in the project directory or mvn is in your PATH."); + } + return mavenCmd.toString(); + } + + static String findGradleBinary(Path baseDir) throws QuarkusUpdateException { + Path gradleCmd = findWrapperOrBinary(baseDir, "gradlew", "gradle"); + if (gradleCmd == null) { + throw new QuarkusUpdateException("Cannot gradlew mvnw or gradle" + + ". Make sure gradlew is in the current directory or gradle in your PATH."); + } + return gradleCmd.toString(); + } + + private static Path findWrapperOrBinary(Path baseDir, String wrapper, String cmd) { + Path found = searchPath(wrapper, baseDir.toString()); + if (found == null) { + found = searchPath(cmd); + } + return found; + } + + /** + * Searches the locations defined by PATH for the given executable + * + * @param cmd The name of the executable to look for + * @return A Path to the executable, if found, null otherwise + */ + public static Path searchPath(String cmd) { + String envPath = System.getenv("PATH"); + envPath = envPath != null ? envPath : ""; + return searchPath(cmd, envPath); + } + + /** + * Searches the locations defined by `paths` for the given executable + * + * @param cmd The name of the executable to look for + * @param paths A string containing the paths to search + * @return A Path to the executable, if found, null otherwise + */ + public static Path searchPath(String cmd, String paths) { + return Arrays.stream(paths.split(File.pathSeparator)) + .map(dir -> Paths.get(dir).resolve(cmd)) + .flatMap(QuarkusUpdateCommand::executables) + .filter(QuarkusUpdateCommand::isExecutable) + .findFirst() + .orElse(null); + } + + private static Stream executables(Path base) { + if (isWindows()) { + return Stream.of(Paths.get(base.toString() + ".exe"), + Paths.get(base.toString() + ".bat"), + Paths.get(base.toString() + ".cmd"), + Paths.get(base.toString() + ".ps1")); + } else { + return Stream.of(base); + } + } + + private static boolean isExecutable(Path file) { + if (Files.isRegularFile(file)) { + if (isWindows()) { + String nm = file.getFileName().toString().toLowerCase(); + return nm.endsWith(".exe") || nm.endsWith(".bat") || nm.endsWith(".cmd") || nm.endsWith(".ps1"); + } else { + return Files.isExecutable(file); + } + } + return false; + } + + private static String OS = System.getProperty("os.name").toLowerCase(); + + public static boolean isWindows() { + return OS.contains("win"); + } + + static boolean hasGradle(Path dir) { + return Files.exists(dir.resolve("build.gradle")); + } + + private static boolean hasMaven(Path dir) { + return Files.exists(dir.resolve("pom.xml")); + } + +} diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/QuarkusUpdateException.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/QuarkusUpdateException.java new file mode 100644 index 0000000000000..6bceefbf0b9cb --- /dev/null +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/QuarkusUpdateException.java @@ -0,0 +1,14 @@ +package io.quarkus.devtools.project.update; + +public class QuarkusUpdateException extends Exception { + + private static final long serialVersionUID = 1L; + + public QuarkusUpdateException(String message, Throwable cause) { + super(message, cause); + } + + public QuarkusUpdateException(String message) { + super(message); + } +} diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/QuarkusUpdates.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/QuarkusUpdates.java index 9a73585606c56..1df6db942ae4b 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/QuarkusUpdates.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/QuarkusUpdates.java @@ -13,9 +13,11 @@ public final class QuarkusUpdates { private QuarkusUpdates() { } - public static void createRecipe(Path target, MavenArtifactResolver artifactResolver, ProjectUpdateRequest request) + public static void createRecipe(Path target, MavenArtifactResolver artifactResolver, String updateRecipesVersion, + ProjectUpdateRequest request) throws IOException { - final List recipes = QuarkusUpdatesRepository.fetchLatestRecipes(artifactResolver, request.currentVersion, + final List recipes = QuarkusUpdatesRepository.fetchRecipes(artifactResolver, updateRecipesVersion, + request.currentVersion, request.targetVersion); QuarkusUpdateRecipe recipe = new QuarkusUpdateRecipe() .buildTool(request.buildTool) diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/QuarkusUpdatesRepository.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/QuarkusUpdatesRepository.java index 51aad48b948ff..9ecbb204a9a58 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/QuarkusUpdatesRepository.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/QuarkusUpdatesRepository.java @@ -20,13 +20,14 @@ public final class QuarkusUpdatesRepository { private QuarkusUpdatesRepository() { } - private static final String QUARKUS_RECIPE_GAV = "io.quarkus:quarkus-update-recipes:LATEST"; + private static final String QUARKUS_RECIPE_GA = "io.quarkus:quarkus-update-recipes"; - public static List fetchLatestRecipes(MavenArtifactResolver artifactResolver, String currentVersion, + public static List fetchRecipes(MavenArtifactResolver artifactResolver, String recipeVersion, String currentVersion, String targetVersion) { + final String gav = QUARKUS_RECIPE_GA + ":" + recipeVersion; try { final ResourceLoader resourceLoader = ResourceLoaders.resolveFileResourceLoader( - artifactResolver.resolve(DependencyUtils.toArtifact(QUARKUS_RECIPE_GAV)).getArtifact().getFile()); + artifactResolver.resolve(DependencyUtils.toArtifact(gav)).getArtifact().getFile()); return resourceLoader.loadResourceAsPath("quarkus-updates/core", path -> { @@ -44,9 +45,9 @@ public static List fetchLatestRecipes(MavenArtifactResolver artifactReso } }); } catch (BootstrapMavenException e) { - throw new RuntimeException("Failed to resolve artifact " + QUARKUS_RECIPE_GAV, e); + throw new RuntimeException("Failed to resolve artifact: " + gav, e); } catch (IOException e) { - throw new RuntimeException("Failed to load recipes in artifact " + QUARKUS_RECIPE_GAV, e); + throw new RuntimeException("Failed to load recipes in artifact: " + gav, e); } }