diff --git a/devtools/cli/src/main/java/io/quarkus/cli/CreateApp.java b/devtools/cli/src/main/java/io/quarkus/cli/CreateApp.java index 9cbaaf94e0e408..6752ac38c47863 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/CreateApp.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/CreateApp.java @@ -5,7 +5,7 @@ import io.quarkus.cli.common.DataOptions; import io.quarkus.cli.common.PropertiesOptions; -import io.quarkus.cli.common.TargetQuarkusVersionGroup; +import io.quarkus.cli.common.TargetQuarkusPlatformGroup; import io.quarkus.cli.create.BaseCreateCommand; import io.quarkus.cli.create.CodeGenerationGroup; import io.quarkus.cli.create.TargetBuildToolGroup; @@ -40,7 +40,7 @@ public class CreateApp extends BaseCreateCommand { String description; @CommandLine.ArgGroup(order = 4, heading = "%nQuarkus version:%n") - TargetQuarkusVersionGroup targetQuarkusVersion = new TargetQuarkusVersionGroup(); + TargetQuarkusPlatformGroup targetQuarkusVersion = new TargetQuarkusPlatformGroup(); @CommandLine.ArgGroup(order = 5, heading = "%nBuild tool (Maven):%n") TargetBuildToolGroup targetBuildTool = new TargetBuildToolGroup(); diff --git a/devtools/cli/src/main/java/io/quarkus/cli/CreateCli.java b/devtools/cli/src/main/java/io/quarkus/cli/CreateCli.java index 9369dcbec9e2ae..b240342b504d54 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/CreateCli.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/CreateCli.java @@ -5,7 +5,7 @@ import io.quarkus.cli.common.DataOptions; import io.quarkus.cli.common.PropertiesOptions; -import io.quarkus.cli.common.TargetQuarkusVersionGroup; +import io.quarkus.cli.common.TargetQuarkusPlatformGroup; import io.quarkus.cli.create.BaseCreateCommand; import io.quarkus.cli.create.CodeGenerationGroup; import io.quarkus.cli.create.TargetBuildToolGroup; @@ -40,7 +40,7 @@ public class CreateCli extends BaseCreateCommand { String description; @CommandLine.ArgGroup(order = 4, heading = "%nQuarkus version:%n") - TargetQuarkusVersionGroup targetQuarkusVersion = new TargetQuarkusVersionGroup(); + TargetQuarkusPlatformGroup targetQuarkusVersion = new TargetQuarkusPlatformGroup(); @CommandLine.ArgGroup(order = 5, heading = "%nBuild tool (Maven):%n") TargetBuildToolGroup targetBuildTool = new TargetBuildToolGroup(); diff --git a/devtools/cli/src/main/java/io/quarkus/cli/CreateExtension.java b/devtools/cli/src/main/java/io/quarkus/cli/CreateExtension.java index d5ab9e6769faa2..9cbbc9889bd349 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/CreateExtension.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/CreateExtension.java @@ -5,7 +5,7 @@ import io.quarkus.cli.common.OutputOptionMixin; import io.quarkus.cli.common.PropertiesOptions; -import io.quarkus.cli.common.TargetQuarkusVersionGroup; +import io.quarkus.cli.common.TargetQuarkusPlatformGroup; import io.quarkus.cli.create.BaseCreateCommand; import io.quarkus.cli.create.ExtensionCodeGenerationGroup; import io.quarkus.cli.create.ExtensionGAVMixin; @@ -75,7 +75,7 @@ public class CreateExtension extends BaseCreateCommand { ExtensionGAVMixin gav = new ExtensionGAVMixin(); @CommandLine.ArgGroup(order = 1, heading = "%nQuarkus version:%n") - TargetQuarkusVersionGroup targetQuarkusVersion = new TargetQuarkusVersionGroup(); + TargetQuarkusPlatformGroup targetQuarkusVersion = new TargetQuarkusPlatformGroup(); @CommandLine.ArgGroup(order = 2, exclusive = false, heading = "%nGenerated artifacts%n") ExtensionNameGenerationGroup nameGeneration = new ExtensionNameGenerationGroup(); diff --git a/devtools/cli/src/main/java/io/quarkus/cli/Info.java b/devtools/cli/src/main/java/io/quarkus/cli/Info.java index 6303b3b646c321..e66017860210b6 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/Info.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/Info.java @@ -16,7 +16,7 @@ public class Info extends BaseBuildCommand implements Callable { public Integer call() throws Exception { try { final BuildSystemRunner runner = getRunner(); - return runner.info(perModule); + return runner.projectInfo(perModule); } catch (Exception e) { return output.handleCommandException(e, "Unable to collect Quarkus project information: " + e.getMessage()); } diff --git a/devtools/cli/src/main/java/io/quarkus/cli/ProjectExtensionsCategories.java b/devtools/cli/src/main/java/io/quarkus/cli/ProjectExtensionsCategories.java index bc053769e82bf6..ce62f10d90dce7 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/ProjectExtensionsCategories.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/ProjectExtensionsCategories.java @@ -8,7 +8,7 @@ import io.quarkus.cli.build.BuildSystemRunner; import io.quarkus.cli.common.CategoryListFormatOptions; import io.quarkus.cli.common.RunModeOption; -import io.quarkus.cli.common.TargetQuarkusVersionGroup; +import io.quarkus.cli.common.TargetQuarkusPlatformGroup; import io.quarkus.devtools.commands.ListCategories; import io.quarkus.devtools.commands.data.QuarkusCommandException; import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; @@ -28,7 +28,7 @@ public class ProjectExtensionsCategories extends BaseBuildCommand implements Cal CategoryListFormatOptions format = new CategoryListFormatOptions(); @CommandLine.ArgGroup(order = 2, heading = "%nQuarkus version:%n") - TargetQuarkusVersionGroup targetQuarkusVersion = new TargetQuarkusVersionGroup(); + TargetQuarkusPlatformGroup targetQuarkusVersion = new TargetQuarkusPlatformGroup(); @Override public Integer call() { diff --git a/devtools/cli/src/main/java/io/quarkus/cli/ProjectExtensionsList.java b/devtools/cli/src/main/java/io/quarkus/cli/ProjectExtensionsList.java index d113eba3ed67bd..a43de5b2b6af16 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/ProjectExtensionsList.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/ProjectExtensionsList.java @@ -8,7 +8,7 @@ import io.quarkus.cli.build.BuildSystemRunner; import io.quarkus.cli.common.ListFormatOptions; import io.quarkus.cli.common.RunModeOption; -import io.quarkus.cli.common.TargetQuarkusVersionGroup; +import io.quarkus.cli.common.TargetQuarkusPlatformGroup; import io.quarkus.devtools.commands.ListExtensions; import io.quarkus.devtools.commands.data.QuarkusCommandException; import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; @@ -32,7 +32,7 @@ public class ProjectExtensionsList extends BaseBuildCommand implements Callable< RunModeOption runMode; @CommandLine.ArgGroup(order = 2, heading = "%nQuarkus version (absolute):%n") - TargetQuarkusVersionGroup targetQuarkusVersion = new TargetQuarkusVersionGroup(); + TargetQuarkusPlatformGroup targetQuarkusVersion = new TargetQuarkusPlatformGroup(); @CommandLine.Option(names = { "-i", "--installable" }, defaultValue = "false", order = 2, description = "List extensions that can be installed (relative)") 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 5a6882a64187b9..ed078eaf6e879f 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/Update.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/Update.java @@ -4,27 +4,23 @@ import io.quarkus.cli.build.BaseBuildCommand; import io.quarkus.cli.build.BuildSystemRunner; +import io.quarkus.cli.common.TargetQuarkusVersionGroup; import picocli.CommandLine; @CommandLine.Command(name = "update", sortOptions = false, showDefaultValues = true, mixinStandardHelpOptions = false, header = "Display recommended Quarkus updates.", headerHeading = "%n", commandListHeading = "%nCommands:%n", synopsisHeading = "%nUsage: ", parameterListHeading = "%n", optionListHeading = "%nOptions:%n") public class Update extends BaseBuildCommand implements Callable { - @CommandLine.Option(names = { - "--rectify" }, description = "Display platform and/or extension version alignment recommendations.") - public boolean rectify = false; + @CommandLine.ArgGroup(order = 0, heading = "%nTarget Quarkus version:%n", multiplicity = "0..1") + TargetQuarkusVersionGroup targetQuarkusVersion = new TargetQuarkusVersionGroup(); - @CommandLine.Option(names = { - "--recommended-state" }, description = "Display the state of the project as if the recommended updates were applied.") - public boolean recommendedState = false; - - @CommandLine.Option(names = { "--per-module" }, description = "Display information per project module.") + @CommandLine.Option(order = 1, 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.update(rectify, recommendedState, perModule); + return runner.updateProject(targetQuarkusVersion.platformVersion, targetQuarkusVersion.streamId, perModule); } catch (Exception e) { return output.handleCommandException(e, "Unable to collect Quarkus project information: " + e.getMessage()); } diff --git a/devtools/cli/src/main/java/io/quarkus/cli/Version.java b/devtools/cli/src/main/java/io/quarkus/cli/Version.java index d578bb2d43d560..d4a9a21912eba3 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/Version.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/Version.java @@ -14,7 +14,7 @@ import picocli.CommandLine; import picocli.CommandLine.Model.CommandSpec; -@CommandLine.Command(name = "version", header = "Display version information.") +@CommandLine.Command(name = "version", header = "Display version information.", hidden = true) public class Version implements CommandLine.IVersionProvider, Callable { private static String version; @@ -31,10 +31,6 @@ public class Version implements CommandLine.IVersionProvider, Callable @CommandLine.Spec CommandSpec spec; - @CommandLine.Option(order = 3, names = { - "--dependencies" }, description = "Show project dependency versions") - boolean dependencies = false; - @Override public Integer call() throws Exception { // Gather/interpolate the usual version information via IVersionProvider handling 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 14bdf78bfa432f..0eb225acab46fc 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 @@ -100,9 +100,9 @@ Integer listExtensions(RunModeOption runMode, ListFormatOptions format, boolean Integer removeExtension(RunModeOption runMode, Set extensions) throws Exception; - Integer info(boolean perModule) throws Exception; + Integer projectInfo(boolean perModule) throws Exception; - Integer update(boolean rectify, boolean recommendedState, boolean perModule) throws Exception; + Integer updateProject(String targetPlatformVersion, String targetPlatformStreamId, 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 ef3050e3bb3750..6d48f0bc3a1326 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 @@ -22,6 +22,7 @@ import io.quarkus.cli.registry.RegistryClientMixin; import io.quarkus.devtools.project.BuildTool; import io.quarkus.registry.config.RegistriesConfigLocator; +import io.quarkus.runtime.util.StringUtil; public class GradleRunner implements BuildSystemRunner { public static final String[] windowsWrapper = { "gradlew.cmd", "gradlew.bat" }; @@ -125,7 +126,7 @@ public Integer removeExtension(RunModeOption runMode, Set extensions) { } @Override - public Integer info(boolean perModule) { + public Integer projectInfo(boolean perModule) { ArrayDeque args = new ArrayDeque<>(); args.add("quarkusInfo"); if (perModule) { @@ -135,14 +136,17 @@ public Integer info(boolean perModule) { } @Override - public Integer update(boolean rectify, boolean recommendedState, boolean perModule) throws Exception { + public Integer updateProject(String targetPlatformVersion, String targetPlatformStreamId, boolean perModule) + throws Exception { ArrayDeque args = new ArrayDeque<>(); args.add("quarkusUpdate"); - if (rectify) { - args.add("--rectify"); + if (!StringUtil.isNullOrEmpty(targetPlatformVersion)) { + args.add("--platform-version"); + args.add(targetPlatformVersion); } - if (recommendedState) { - args.add("--recommended-state"); + if (!StringUtil.isNullOrEmpty(targetPlatformStreamId)) { + args.add("--stream-id"); + args.add(targetPlatformStreamId); } if (perModule) { args.add("--per-module"); 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 94f1ae8f444dd0..4f891ebcd70665 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 @@ -70,12 +70,13 @@ public Integer removeExtension(RunModeOption runMode, Set extensions) { } @Override - public Integer info(boolean perModule) { + public Integer projectInfo(boolean perModule) { throw new UnsupportedOperationException("Not there yet. ;)"); } @Override - public Integer update(boolean rectify, boolean recommendedState, boolean perModule) { + public Integer updateProject(String targetPlatformVersion, String targetPlatformStreamId, 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 89641c9ffca839..0ed6d062f920b5 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 @@ -23,7 +23,9 @@ import io.quarkus.devtools.commands.AddExtensions; import io.quarkus.devtools.commands.ListCategories; import io.quarkus.devtools.commands.ListExtensions; +import io.quarkus.devtools.commands.ProjectInfo; import io.quarkus.devtools.commands.RemoveExtensions; +import io.quarkus.devtools.commands.UpdateProject; import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; import io.quarkus.devtools.project.BuildTool; import io.quarkus.devtools.project.QuarkusProject; @@ -130,33 +132,23 @@ public Integer removeExtension(RunModeOption runMode, Set extensions) th } @Override - public Integer info(boolean perModule) throws Exception { - ArrayDeque args = new ArrayDeque<>(); - setMavenProperties(args, true); - args.add("quarkus:info"); - if (perModule) { - args.add("-DperModule"); - } - args.add("-ntp"); - return run(prependExecutable(args)); + public Integer projectInfo(boolean perModule) throws Exception { + final ProjectInfo invoker = new ProjectInfo(quarkusProject()); + invoker.perModule(perModule); + invoker.appModel(MavenProjectBuildFile.resolveApplicationModel(projectRoot)); + return invoker.execute().isSuccess() ? CommandLine.ExitCode.OK : CommandLine.ExitCode.SOFTWARE; } @Override - public Integer update(boolean rectify, boolean recommendedState, boolean perModule) { - ArrayDeque args = new ArrayDeque<>(); - setMavenProperties(args, true); - args.add("quarkus:update"); - if (rectify) { - args.add("-Drectify"); - } - if (recommendedState) { - args.add("-DrecommendedState"); - } - if (perModule) { - args.add("-DperModule"); - } - args.add("-ntp"); - return run(prependExecutable(args)); + public Integer updateProject(String targetPlatformVersion, String targetPlatformStreamId, boolean perModule) + throws Exception { + final UpdateProject invoker = new UpdateProject(quarkusProject()); + invoker.latestCatalog(quarkusProject().getExtensionsCatalog()); + // TODO ALEXEY: resolve targetPlatformVersion from targetPlatformStreamId if needed or from latest version + invoker.targetPlatformVersion(targetPlatformVersion); + invoker.perModule(perModule); + invoker.appModel(MavenProjectBuildFile.resolveApplicationModel(projectRoot)); + return invoker.execute().isSuccess() ? CommandLine.ExitCode.OK : CommandLine.ExitCode.SOFTWARE; } @Override diff --git a/devtools/cli/src/main/java/io/quarkus/cli/common/TargetQuarkusPlatformGroup.java b/devtools/cli/src/main/java/io/quarkus/cli/common/TargetQuarkusPlatformGroup.java new file mode 100644 index 00000000000000..113d3c3d2a4ae0 --- /dev/null +++ b/devtools/cli/src/main/java/io/quarkus/cli/common/TargetQuarkusPlatformGroup.java @@ -0,0 +1,123 @@ +package io.quarkus.cli.common; + +import io.quarkus.cli.Version; +import io.quarkus.maven.dependency.ArtifactCoords; +import io.quarkus.platform.tools.ToolsConstants; +import io.quarkus.registry.catalog.PlatformStreamCoords; +import picocli.CommandLine; +import picocli.CommandLine.Model.CommandSpec; + +public class TargetQuarkusPlatformGroup { + static final String FULL_EXAMPLE = ToolsConstants.DEFAULT_PLATFORM_BOM_GROUP_ID + ":" + + ToolsConstants.DEFAULT_PLATFORM_BOM_ARTIFACT_ID + ":2.2.0.Final"; + PlatformStreamCoords streamCoords = null; + String validStream = null; + + ArtifactCoords platformBom = null; + String validPlatformBom = null; + + @CommandLine.Spec + CommandSpec spec; + + @CommandLine.Option(paramLabel = "platformKey:streamId", names = { "-S", + "--stream" }, description = "A target stream, for example:%n io.quarkus.platform:2.0") + void setStream(String stream) { + stream = stream.trim(); + if (!stream.isEmpty()) { + try { + streamCoords = PlatformStreamCoords.fromString(stream); + validStream = stream; + } catch (IllegalArgumentException iex) { + throw new CommandLine.ParameterException(spec.commandLine(), + String.format("Invalid value '%s' for option '--stream'. " + + "Value should be specified as 'platformKey:streamId'. %s", stream, iex.getMessage())); + } + } + } + + @CommandLine.Option(paramLabel = "groupId:artifactId:version", names = { "-P", + "--platform-bom" }, description = "A specific Quarkus platform BOM, for example:%n" + + " " + FULL_EXAMPLE + "%n" + + " io.quarkus::999-SNAPSHOT" + + " 2.2.0.Final%n" + + "Default groupId: " + ToolsConstants.DEFAULT_PLATFORM_BOM_GROUP_ID + "%n" + + "Default artifactId: " + ToolsConstants.DEFAULT_PLATFORM_BOM_ARTIFACT_ID + "%n") + void setPlatformBom(String bom) { + bom = bom.replaceFirst("^::", "").trim(); + if (!bom.isEmpty()) { + try { + int firstPos = bom.indexOf(":"); + int lastPos = bom.lastIndexOf(":"); + if (lastPos <= 0) { + // no : at all, use default group and artifact id + setBom(ToolsConstants.DEFAULT_PLATFORM_BOM_GROUP_ID, + ToolsConstants.DEFAULT_PLATFORM_BOM_ARTIFACT_ID, + bom); + } else if (lastPos == firstPos + 1) { // We have :: somewhere + if (lastPos == bom.length() - 1) { + // some.group::, use default artifact and client version + setBom(bom.substring(0, firstPos), + ToolsConstants.DEFAULT_PLATFORM_BOM_ARTIFACT_ID, + Version.clientVersion()); + } else { + // some.group::version, use default artifact id + setBom(bom.substring(0, firstPos), + ToolsConstants.DEFAULT_PLATFORM_BOM_ARTIFACT_ID, + bom.substring(lastPos + 1)); + } + } else if (firstPos == 0 && lastPos == bom.length() - 1) { + // :my-bom:, use default group and version + setBom(ToolsConstants.DEFAULT_PLATFORM_BOM_GROUP_ID, + bom.substring(1, lastPos), + Version.clientVersion()); + } else { + platformBom = ArtifactCoords.fromString(bom); + validPlatformBom = bom; // keep original (valid) string (dryrun) + } + } catch (IllegalArgumentException iex) { + throw new CommandLine.ParameterException(spec.commandLine(), + String.format("Invalid value '%s' for option '--platform-bom'. " + + "Value should be specified as 'GROUP-ID:ARTIFACT-ID:VERSION'. %s", bom, iex.getMessage())); + } + } + } + + public boolean isPlatformSpecified() { + return platformBom != null; + } + + public ArtifactCoords getPlatformBom() { + return platformBom; + } + + public boolean isStreamSpecified() { + return streamCoords != null; + } + + public PlatformStreamCoords getStream() { + return streamCoords; + } + + public String dryRun() { + if (streamCoords != null) { + return "stream " + validStream; + } else if (platformBom != null) { + return "platform " + validPlatformBom; + } else { + return "same as project"; + } + } + + @Override + public String toString() { + return "TargetQuarkusVersionGroup{" + + "stream=" + streamCoords + + ", platformBom=" + platformBom + + '}'; + } + + private void setBom(String groupId, String artifactId, String version) { + platformBom = ArtifactCoords.pom(groupId, artifactId, version); + validPlatformBom = groupId + ":" + artifactId + ":" + version; + } +} diff --git a/devtools/cli/src/main/java/io/quarkus/cli/common/TargetQuarkusVersionGroup.java b/devtools/cli/src/main/java/io/quarkus/cli/common/TargetQuarkusVersionGroup.java index 4e276b3a2f644a..4d06943b483042 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/common/TargetQuarkusVersionGroup.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/common/TargetQuarkusVersionGroup.java @@ -1,123 +1,15 @@ package io.quarkus.cli.common; -import io.quarkus.cli.Version; -import io.quarkus.maven.dependency.ArtifactCoords; -import io.quarkus.platform.tools.ToolsConstants; -import io.quarkus.registry.catalog.PlatformStreamCoords; import picocli.CommandLine; -import picocli.CommandLine.Model.CommandSpec; public class TargetQuarkusVersionGroup { - static final String FULL_EXAMPLE = ToolsConstants.DEFAULT_PLATFORM_BOM_GROUP_ID + ":" - + ToolsConstants.DEFAULT_PLATFORM_BOM_ARTIFACT_ID + ":2.2.0.Final"; - PlatformStreamCoords streamCoords = null; - String validStream = null; - ArtifactCoords platformBom = null; - String validPlatformBom = null; + @CommandLine.Option(paramLabel = "targetStreamId", names = { "-S", + "--stream-id" }, description = "A target stream id, for example:%n 2.0") + public String streamId; - @CommandLine.Spec - CommandSpec spec; - - @CommandLine.Option(paramLabel = "platformKey:streamId", names = { "-S", - "--stream" }, description = "A target stream, for example:%n io.quarkus.platform:2.0") - void setStream(String stream) { - stream = stream.trim(); - if (!stream.isEmpty()) { - try { - streamCoords = PlatformStreamCoords.fromString(stream); - validStream = stream; - } catch (IllegalArgumentException iex) { - throw new CommandLine.ParameterException(spec.commandLine(), - String.format("Invalid value '%s' for option '--stream'. " + - "Value should be specified as 'platformKey:streamId'. %s", stream, iex.getMessage())); - } - } - } - - @CommandLine.Option(paramLabel = "groupId:artifactId:version", names = { "-P", - "--platform-bom" }, description = "A specific Quarkus platform BOM, for example:%n" - + " " + FULL_EXAMPLE + "%n" - + " io.quarkus::999-SNAPSHOT" - + " 2.2.0.Final%n" - + "Default groupId: " + ToolsConstants.DEFAULT_PLATFORM_BOM_GROUP_ID + "%n" - + "Default artifactId: " + ToolsConstants.DEFAULT_PLATFORM_BOM_ARTIFACT_ID + "%n") - void setPlatformBom(String bom) { - bom = bom.replaceFirst("^::", "").trim(); - if (!bom.isEmpty()) { - try { - int firstPos = bom.indexOf(":"); - int lastPos = bom.lastIndexOf(":"); - if (lastPos <= 0) { - // no : at all, use default group and artifact id - setBom(ToolsConstants.DEFAULT_PLATFORM_BOM_GROUP_ID, - ToolsConstants.DEFAULT_PLATFORM_BOM_ARTIFACT_ID, - bom); - } else if (lastPos == firstPos + 1) { // We have :: somewhere - if (lastPos == bom.length() - 1) { - // some.group::, use default artifact and client version - setBom(bom.substring(0, firstPos), - ToolsConstants.DEFAULT_PLATFORM_BOM_ARTIFACT_ID, - Version.clientVersion()); - } else { - // some.group::version, use default artifact id - setBom(bom.substring(0, firstPos), - ToolsConstants.DEFAULT_PLATFORM_BOM_ARTIFACT_ID, - bom.substring(lastPos + 1)); - } - } else if (firstPos == 0 && lastPos == bom.length() - 1) { - // :my-bom:, use default group and version - setBom(ToolsConstants.DEFAULT_PLATFORM_BOM_GROUP_ID, - bom.substring(1, lastPos), - Version.clientVersion()); - } else { - platformBom = ArtifactCoords.fromString(bom); - validPlatformBom = bom; // keep original (valid) string (dryrun) - } - } catch (IllegalArgumentException iex) { - throw new CommandLine.ParameterException(spec.commandLine(), - String.format("Invalid value '%s' for option '--platform-bom'. " + - "Value should be specified as 'GROUP-ID:ARTIFACT-ID:VERSION'. %s", bom, iex.getMessage())); - } - } - } - - public boolean isPlatformSpecified() { - return platformBom != null; - } - - public ArtifactCoords getPlatformBom() { - return platformBom; - } - - public boolean isStreamSpecified() { - return streamCoords != null; - } - - public PlatformStreamCoords getStream() { - return streamCoords; - } - - public String dryRun() { - if (streamCoords != null) { - return "stream " + validStream; - } else if (platformBom != null) { - return "platform " + validPlatformBom; - } else { - return "same as project"; - } - } - - @Override - public String toString() { - return "TargetQuarkusVersionGroup{" - + "stream=" + streamCoords - + ", platformBom=" + platformBom - + '}'; - } - - private void setBom(String artifactId, String groupId, String version) { - platformBom = ArtifactCoords.pom(artifactId, groupId, version); - validPlatformBom = artifactId + ":" + groupId + ":" + version; - } + @CommandLine.Option(paramLabel = "targetPlatformVersion", names = { "-V", + "--platform-version" }, description = "A specific target Quarkus platform version, for example:%n" + + " 2.2.0.Final%n") + public String platformVersion; } diff --git a/devtools/cli/src/main/java/io/quarkus/cli/create/BaseCreateCommand.java b/devtools/cli/src/main/java/io/quarkus/cli/create/BaseCreateCommand.java index f4d84b9834e5b6..ef87e60871c700 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/create/BaseCreateCommand.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/create/BaseCreateCommand.java @@ -13,7 +13,7 @@ import io.quarkus.cli.common.HelpOption; import io.quarkus.cli.common.OutputOptionMixin; import io.quarkus.cli.common.RunModeOption; -import io.quarkus.cli.common.TargetQuarkusVersionGroup; +import io.quarkus.cli.common.TargetQuarkusPlatformGroup; import io.quarkus.cli.registry.ToggleRegistryClientMixin; import io.quarkus.devtools.commands.CreateProject.CreateProjectKey; import io.quarkus.devtools.commands.CreateProjectHelper; @@ -201,7 +201,7 @@ protected void setValue(String name, Object value) { * @return Quarkus command invocation that can be printed (dry-run) or run to create the project * @throws RegistryResolutionException */ - public QuarkusCommandInvocation build(BuildTool buildTool, TargetQuarkusVersionGroup targetVersion, + public QuarkusCommandInvocation build(BuildTool buildTool, TargetQuarkusPlatformGroup targetVersion, Map properties, Collection extensions) throws RegistryResolutionException { @@ -230,7 +230,7 @@ public QuarkusCommandInvocation build(BuildTool buildTool, TargetQuarkusVersionG * @return Resolved QuarkusProject for the given build tool and target quarkus version * @throws RegistryResolutionException */ - public QuarkusProject getExtensionVersions(BuildTool buildTool, TargetQuarkusVersionGroup targetVersion) + public QuarkusProject getExtensionVersions(BuildTool buildTool, TargetQuarkusPlatformGroup targetVersion) throws RegistryResolutionException { return registryClient.createQuarkusProject(outputDirectory(), targetVersion, buildTool, output); } diff --git a/devtools/cli/src/main/java/io/quarkus/cli/registry/RegistryClientMixin.java b/devtools/cli/src/main/java/io/quarkus/cli/registry/RegistryClientMixin.java index 49a16ba8e1d7c6..635a851c27ec4e 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/registry/RegistryClientMixin.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/registry/RegistryClientMixin.java @@ -6,7 +6,7 @@ import io.quarkus.cli.Version; import io.quarkus.cli.common.OutputOptionMixin; -import io.quarkus.cli.common.TargetQuarkusVersionGroup; +import io.quarkus.cli.common.TargetQuarkusPlatformGroup; import io.quarkus.devtools.commands.CreateProjectHelper; import io.quarkus.devtools.project.BuildTool; import io.quarkus.devtools.project.QuarkusProject; @@ -49,12 +49,12 @@ public RegistriesConfig resolveConfig() throws RegistryResolutionException { : RegistriesConfig.resolveFromFile(Path.of(config)); } - public QuarkusProject createQuarkusProject(Path projectRoot, TargetQuarkusVersionGroup targetVersion, BuildTool buildTool, + public QuarkusProject createQuarkusProject(Path projectRoot, TargetQuarkusPlatformGroup targetVersion, BuildTool buildTool, OutputOptionMixin log) throws RegistryResolutionException { return createQuarkusProject(projectRoot, targetVersion, buildTool, log, List.of()); } - public QuarkusProject createQuarkusProject(Path projectRoot, TargetQuarkusVersionGroup targetVersion, BuildTool buildTool, + public QuarkusProject createQuarkusProject(Path projectRoot, TargetQuarkusPlatformGroup targetVersion, BuildTool buildTool, OutputOptionMixin log, Collection extensions) throws RegistryResolutionException { ExtensionCatalog catalog = getExtensionCatalog(targetVersion, log); if (VALIDATE && catalog.getQuarkusCoreVersion().startsWith("1.")) { @@ -65,7 +65,7 @@ public QuarkusProject createQuarkusProject(Path projectRoot, TargetQuarkusVersio return QuarkusProjectHelper.getProject(projectRoot, catalog, buildTool, log); } - ExtensionCatalog getExtensionCatalog(TargetQuarkusVersionGroup targetVersion, OutputOptionMixin log) + ExtensionCatalog getExtensionCatalog(TargetQuarkusPlatformGroup targetVersion, OutputOptionMixin log) throws RegistryResolutionException { log.debug("Resolving Quarkus extension catalog for " + targetVersion); QuarkusProjectHelper.setMessageWriter(log); diff --git a/devtools/cli/src/test/java/io/quarkus/cli/common/TargetQuarkusVersionGroupTest.java b/devtools/cli/src/test/java/io/quarkus/cli/common/TargetQuarkusPlatformGroupTest.java similarity index 96% rename from devtools/cli/src/test/java/io/quarkus/cli/common/TargetQuarkusVersionGroupTest.java rename to devtools/cli/src/test/java/io/quarkus/cli/common/TargetQuarkusPlatformGroupTest.java index 5d02900a262395..62864e1c7b7def 100644 --- a/devtools/cli/src/test/java/io/quarkus/cli/common/TargetQuarkusVersionGroupTest.java +++ b/devtools/cli/src/test/java/io/quarkus/cli/common/TargetQuarkusPlatformGroupTest.java @@ -8,10 +8,10 @@ import io.quarkus.platform.tools.ToolsConstants; import io.quarkus.registry.catalog.PlatformStreamCoords; -public class TargetQuarkusVersionGroupTest { +public class TargetQuarkusPlatformGroupTest { final static String clientVersion = Version.clientVersion(); - TargetQuarkusVersionGroup qvg = new TargetQuarkusVersionGroup(); + TargetQuarkusPlatformGroup qvg = new TargetQuarkusPlatformGroup(); @Test void testPlatformFullyQualified() { diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusInfo.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusInfo.java index 4c99131567bc6c..35d6c402b0dcdf 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusInfo.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusInfo.java @@ -1,17 +1,13 @@ package io.quarkus.gradle.tasks; -import java.util.HashMap; -import java.util.Map; - import org.gradle.api.GradleException; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.TaskAction; import org.gradle.api.tasks.options.Option; -import io.quarkus.devtools.commands.data.QuarkusCommandInvocation; +import io.quarkus.devtools.commands.ProjectInfo; import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; -import io.quarkus.devtools.commands.handlers.InfoCommandHandler; -import io.quarkus.devtools.commands.handlers.UpdateCommandHandler; +import io.quarkus.devtools.commands.handlers.ProjectInfoCommandHandler; import io.quarkus.devtools.project.QuarkusProject; public class QuarkusInfo extends QuarkusPlatformTask { @@ -38,17 +34,16 @@ public void logInfo() { getProject().getLogger().warn(getName() + " is experimental, its options and output might change in future versions"); final QuarkusProject quarkusProject = getQuarkusProject(false); - final Map params = new HashMap<>(); - params.put(UpdateCommandHandler.APP_MODEL, extension().getApplicationModel()); - params.put(UpdateCommandHandler.LOG_STATE_PER_MODULE, perModule); - final QuarkusCommandInvocation invocation = new QuarkusCommandInvocation(quarkusProject, params); final QuarkusCommandOutcome outcome; + final ProjectInfo invoker = new ProjectInfo(quarkusProject); + invoker.perModule(perModule); + invoker.appModel(extension().getApplicationModel()); try { - outcome = new InfoCommandHandler().execute(invocation); + outcome = invoker.execute(); } catch (Exception e) { throw new GradleException("Failed to collect Quarkus project information", e); } - if (outcome.getValue(InfoCommandHandler.RECOMMENDATIONS_AVAILABLE, false)) { + if (outcome.getValue(ProjectInfoCommandHandler.RECOMMENDATIONS_AVAILABLE, false)) { this.getProject().getLogger().warn( "Non-recommended Quarkus platform BOM and/or extension versions were found. For more details, please, execute 'gradle quarkusUpdate --rectify'"); } 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 81eff76a65af84..99085f65796dc2 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 @@ -1,23 +1,19 @@ package io.quarkus.gradle.tasks; -import java.util.HashMap; -import java.util.Map; - import org.gradle.api.GradleException; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.TaskAction; import org.gradle.api.tasks.options.Option; -import io.quarkus.devtools.commands.data.QuarkusCommandInvocation; -import io.quarkus.devtools.commands.handlers.UpdateCommandHandler; +import io.quarkus.devtools.commands.UpdateProject; import io.quarkus.devtools.project.QuarkusProject; import io.quarkus.registry.RegistryResolutionException; public class QuarkusUpdate extends QuarkusPlatformTask { private boolean perModule = false; - private boolean recommendedState = false; - private boolean rectify = false; + private String targetStreamId; + private String targetPlatformVersion; @Input public boolean getPerModule() { @@ -30,23 +26,23 @@ public void setPerModule(boolean perModule) { } @Input - public boolean getRecommendedState() { - return recommendedState; + public String getTargetStreamId() { + return targetStreamId; } - @Option(description = "Log the new recommended project state.", option = "recommendedState") - public void setRecommendedState(boolean recommendedState) { - this.recommendedState = recommendedState; + @Option(description = "A target stream id, for example: 2.0", option = "streamId") + public void setStreamId(String targetStreamId) { + this.targetStreamId = targetStreamId; } @Input - public boolean getRectify() { - return rectify; + public String getTargetPlatformVersion() { + return targetPlatformVersion; } - @Option(description = "Log the rectified state of the current project according to the recommendations based on the currently enforced Quarkus platforms.", option = "rectify") - public void setRectify(boolean rectify) { - this.rectify = rectify; + @Option(description = "A target platform version, for example: 2.0.0.Final", option = "platformVersion") + public void setTargetPlatformVersion(String targetPlatformVersion) { + this.targetPlatformVersion = targetPlatformVersion; } public QuarkusUpdate() { @@ -59,21 +55,20 @@ public void logUpdates() { getProject().getLogger().warn(getName() + " is experimental, its options and output might change in future versions"); final QuarkusProject quarkusProject = getQuarkusProject(false); - final Map params = new HashMap<>(); + final UpdateProject invoker = new UpdateProject(quarkusProject); try { - params.put(UpdateCommandHandler.LATEST_CATALOG, - getExtensionCatalogResolver(quarkusProject.log()).resolveExtensionCatalog()); + invoker.latestCatalog(getExtensionCatalogResolver(quarkusProject.log()).resolveExtensionCatalog()); } catch (RegistryResolutionException e) { throw new GradleException( "Failed to resolve the latest Quarkus extension catalog from the configured extension registries", e); } - params.put(UpdateCommandHandler.APP_MODEL, extension().getApplicationModel()); - params.put(UpdateCommandHandler.LOG_STATE_PER_MODULE, perModule); - params.put(UpdateCommandHandler.LOG_RECOMMENDED_STATE, recommendedState); - params.put(UpdateCommandHandler.RECTIFY, rectify); - final QuarkusCommandInvocation invocation = new QuarkusCommandInvocation(quarkusProject, params); + + // TODO ALEXEY: resolve targetPlatformVersion from targetPlatformStreamId if needed or from latest version + invoker.targetPlatformVersion(targetPlatformVersion); + invoker.perModule(perModule); + invoker.appModel(extension().getApplicationModel()); try { - new UpdateCommandHandler().execute(invocation); + invoker.execute(); } catch (Exception e) { throw new GradleException("Failed to resolve recommended updates", e); } diff --git a/devtools/maven/src/main/java/io/quarkus/maven/InfoMojo.java b/devtools/maven/src/main/java/io/quarkus/maven/InfoMojo.java index 7ae0a1aa86b091..3bd71e3ce8acf2 100644 --- a/devtools/maven/src/main/java/io/quarkus/maven/InfoMojo.java +++ b/devtools/maven/src/main/java/io/quarkus/maven/InfoMojo.java @@ -1,16 +1,12 @@ package io.quarkus.maven; -import java.util.HashMap; -import java.util.Map; - import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugins.annotations.Mojo; +import io.quarkus.devtools.commands.ProjectInfo; import io.quarkus.devtools.commands.data.QuarkusCommandException; -import io.quarkus.devtools.commands.data.QuarkusCommandInvocation; import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; -import io.quarkus.devtools.commands.handlers.InfoCommandHandler; -import io.quarkus.devtools.commands.handlers.UpdateCommandHandler; +import io.quarkus.devtools.commands.handlers.ProjectInfoCommandHandler; import io.quarkus.devtools.project.QuarkusProject; /** @@ -28,20 +24,19 @@ protected void validateParameters() throws MojoExecutionException { @Override protected void processProjectState(QuarkusProject quarkusProject) throws MojoExecutionException { + final ProjectInfo invoker = new ProjectInfo(quarkusProject); - final Map params = new HashMap<>(); - params.put(UpdateCommandHandler.APP_MODEL, resolveApplicationModel()); - params.put(UpdateCommandHandler.LOG_STATE_PER_MODULE, perModule); + invoker.perModule(perModule); + invoker.appModel(resolveApplicationModel()); - final QuarkusCommandInvocation invocation = new QuarkusCommandInvocation(quarkusProject, params); QuarkusCommandOutcome outcome; try { - outcome = new InfoCommandHandler().execute(invocation); + outcome = invoker.execute(); } catch (QuarkusCommandException e) { throw new MojoExecutionException("Failed to resolve the available updates", e); } - if (outcome.getValue(InfoCommandHandler.RECOMMENDATIONS_AVAILABLE, false)) { + if (outcome.getValue(ProjectInfoCommandHandler.RECOMMENDATIONS_AVAILABLE, false)) { getLog().warn( "Non-recommended Quarkus platform BOM and/or extension versions were found. For more details, please, execute 'mvn quarkus:update -Drectify'"); } 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 93f2aa59f6abe7..fc37b58fca7df3 100644 --- a/devtools/maven/src/main/java/io/quarkus/maven/UpdateMojo.java +++ b/devtools/maven/src/main/java/io/quarkus/maven/UpdateMojo.java @@ -1,15 +1,11 @@ package io.quarkus.maven; -import java.util.HashMap; -import java.util.Map; - import org.apache.maven.plugin.MojoExecutionException; 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.QuarkusCommandInvocation; -import io.quarkus.devtools.commands.handlers.UpdateCommandHandler; import io.quarkus.devtools.project.QuarkusProject; import io.quarkus.registry.RegistryResolutionException; @@ -21,18 +17,28 @@ public class UpdateMojo extends QuarkusProjectStateMojoBase { /** - * If true, the state of the project will be logged as if the recommended updates - * were applied + * Display information per project module. */ - @Parameter(property = "recommendedState") - boolean recommendedState; + @Parameter(property = "perModule") + boolean perModule; /** * If true, instead of checking and recommending the latest available Quarkus platform version, * recommendations to properly align the current project configuration will be logged (if any) */ - @Parameter(property = "rectify") - boolean rectify; + + /** + * Version of the target platform (e.g: 2.0.0.Final) + * You may instead use streamId to target the latest version of a specific platform stream. + */ + @Parameter(property = "platformVersion", required = false) + private String platformVersion; + + /** + * Target streamId (e.g: 2.0) + */ + @Parameter(property = "streamId", required = false) + private String streamId; @Override protected void validateParameters() throws MojoExecutionException { @@ -43,21 +49,21 @@ protected void validateParameters() throws MojoExecutionException { @Override protected void processProjectState(QuarkusProject quarkusProject) throws MojoExecutionException { - final Map params = new HashMap<>(); + final UpdateProject invoker = new UpdateProject(quarkusProject); try { - params.put(UpdateCommandHandler.LATEST_CATALOG, getExtensionCatalogResolver().resolveExtensionCatalog()); + invoker.latestCatalog(getExtensionCatalogResolver().resolveExtensionCatalog()); } catch (RegistryResolutionException e) { throw new MojoExecutionException( "Failed to resolve the latest Quarkus extension catalog from the configured extension registries", e); } - params.put(UpdateCommandHandler.APP_MODEL, resolveApplicationModel()); - params.put(UpdateCommandHandler.LOG_RECOMMENDED_STATE, recommendedState); - params.put(UpdateCommandHandler.LOG_STATE_PER_MODULE, perModule); - params.put(UpdateCommandHandler.RECTIFY, rectify); - final QuarkusCommandInvocation invocation = new QuarkusCommandInvocation(quarkusProject, params); + // TODO ALEXEY: resolve targetPlatformVersion from streamId if needed or from latest version + invoker.targetPlatformVersion(platformVersion); + invoker.perModule(perModule); + invoker.appModel(resolveApplicationModel()); + try { - new UpdateCommandHandler().execute(invocation); + invoker.execute(); } 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/ProjectInfo.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/ProjectInfo.java new file mode 100644 index 00000000000000..d7a42cbac00fe0 --- /dev/null +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/ProjectInfo.java @@ -0,0 +1,47 @@ +package io.quarkus.devtools.commands; + +import static java.util.Objects.requireNonNull; + +import java.util.HashMap; + +import io.quarkus.bootstrap.model.ApplicationModel; +import io.quarkus.devtools.commands.data.QuarkusCommandException; +import io.quarkus.devtools.commands.data.QuarkusCommandInvocation; +import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; +import io.quarkus.devtools.commands.handlers.ProjectInfoCommandHandler; +import io.quarkus.devtools.messagewriter.MessageWriter; +import io.quarkus.devtools.project.QuarkusProject; + +/** + * Instances of this class are not thread-safe. They are created per single invocation. + */ +public class ProjectInfo { + + public static final String APP_MODEL = "quarkus.project-info.app-model"; + public static final String PER_MODULE = "quarkus.project-info.per-module"; + + private final QuarkusCommandInvocation invocation; + private final ProjectInfoCommandHandler handler = new ProjectInfoCommandHandler(); + + public ProjectInfo(final QuarkusProject quarkusProject) { + this.invocation = new QuarkusCommandInvocation(quarkusProject); + } + + public ProjectInfo(final QuarkusProject quarkusProject, final MessageWriter messageWriter) { + this.invocation = new QuarkusCommandInvocation(quarkusProject, new HashMap<>(), messageWriter); + } + + public ProjectInfo appModel(ApplicationModel applicationModel) { + invocation.setValue(APP_MODEL, requireNonNull(applicationModel, "applicationModel is required")); + return this; + } + + public ProjectInfo perModule(boolean perModule) { + invocation.setValue(PER_MODULE, perModule); + return this; + } + + public QuarkusCommandOutcome execute() throws QuarkusCommandException { + return handler.execute(invocation); + } +} 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 new file mode 100644 index 00000000000000..13ccd0cf77c1d8 --- /dev/null +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/UpdateProject.java @@ -0,0 +1,67 @@ +package io.quarkus.devtools.commands; + +import static java.util.Objects.requireNonNull; + +import java.util.HashMap; + +import io.quarkus.bootstrap.model.ApplicationModel; +import io.quarkus.devtools.commands.data.QuarkusCommandException; +import io.quarkus.devtools.commands.data.QuarkusCommandInvocation; +import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; +import io.quarkus.devtools.commands.handlers.UpdateProjectCommandHandler; +import io.quarkus.devtools.messagewriter.MessageWriter; +import io.quarkus.devtools.project.QuarkusProject; +import io.quarkus.registry.catalog.ExtensionCatalog; + +/** + * Instances of this class are not thread-safe. They are created per single invocation. + */ +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 TARGET_PLATFORM_VERSION = "quarkus.update-project.target-platform-version"; + + private final QuarkusCommandInvocation invocation; + private final UpdateProjectCommandHandler handler = new UpdateProjectCommandHandler(); + + public UpdateProject(final QuarkusProject quarkusProject) { + this.invocation = new QuarkusCommandInvocation(quarkusProject); + } + + public UpdateProject(final QuarkusProject quarkusProject, final MessageWriter messageWriter) { + this.invocation = new QuarkusCommandInvocation(quarkusProject, new HashMap<>(), messageWriter); + } + + public UpdateProject latestCatalog(ExtensionCatalog latestCatalog) { + invocation.setValue(LATEST_CATALOG, requireNonNull(latestCatalog, "latestCatalog is required")); + return this; + } + + public UpdateProject appModel(ApplicationModel applicationModel) { + invocation.setValue(APP_MODEL, requireNonNull(applicationModel, "applicationModel is required")); + return this; + } + + public UpdateProject generateRewriteConfig(boolean generateUpdateConfig) { + invocation.setValue(GENERATE_REWRITE_CONFIG, generateUpdateConfig); + return this; + } + + public UpdateProject perModule(boolean perModule) { + invocation.setValue(PER_MODULE, perModule); + return this; + } + + public UpdateProject targetPlatformVersion(String targetPlatformVersion) { + invocation.setValue(TARGET_PLATFORM_VERSION, targetPlatformVersion); + return this; + } + + public QuarkusCommandOutcome execute() throws QuarkusCommandException { + return handler.execute(invocation); + } +} diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/InfoCommandHandler.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/ProjectInfoCommandHandler.java similarity index 92% rename from independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/InfoCommandHandler.java rename to independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/ProjectInfoCommandHandler.java index 99a566d96bb2a9..a3ca52a6b36100 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/InfoCommandHandler.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/ProjectInfoCommandHandler.java @@ -13,6 +13,7 @@ import io.quarkus.bootstrap.model.ApplicationModel; import io.quarkus.bootstrap.workspace.WorkspaceModule; import io.quarkus.bootstrap.workspace.WorkspaceModuleId; +import io.quarkus.devtools.commands.ProjectInfo; import io.quarkus.devtools.commands.data.QuarkusCommandException; import io.quarkus.devtools.commands.data.QuarkusCommandInvocation; import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; @@ -29,17 +30,17 @@ import io.quarkus.registry.catalog.ExtensionCatalog; import io.quarkus.registry.catalog.ExtensionOrigin; -public class InfoCommandHandler implements QuarkusCommandHandler { +public class ProjectInfoCommandHandler implements QuarkusCommandHandler { - public static final String APP_MODEL = "app-model"; - public static final String LOG_STATE_PER_MODULE = "log-state-per-module"; public static final String RECOMMENDATIONS_AVAILABLE = "recommendations-available"; @Override public QuarkusCommandOutcome execute(QuarkusCommandInvocation invocation) throws QuarkusCommandException { - final ApplicationModel appModel = invocation.getValue(APP_MODEL); - final boolean logStatePerModule = invocation.getValue(UpdateCommandHandler.LOG_STATE_PER_MODULE, false); + // TODO ALEXEY: info about the project (versions and extensions) and hint on how to repair if broken (or to update if available - nice to have) + + final ApplicationModel appModel = invocation.getValue(ProjectInfo.APP_MODEL); + final boolean logStatePerModule = invocation.getValue(ProjectInfo.PER_MODULE, false); final boolean recommendationsAvailable = logState( resolveProjectState(appModel, invocation.getExtensionsCatalog()), logStatePerModule, false, @@ -79,8 +80,8 @@ protected static boolean logState(ProjectState projectState, boolean perModule, final StringBuilder sb = new StringBuilder(); if (platform.recommended == null) { if (rectify) { - sb.append(String.format(UpdateCommandHandler.PLATFORM_RECTIFY_FORMAT, - UpdateCommandHandler.REMOVE, platform.imported.toCompactCoords())); + sb.append(String.format(UpdateProjectCommandHandler.PLATFORM_RECTIFY_FORMAT, + UpdateProjectCommandHandler.REMOVE, platform.imported.toCompactCoords())); recommendationsAvailable = true; } else { sb.append(" "); @@ -94,8 +95,8 @@ protected static boolean logState(ProjectState projectState, boolean perModule, } } else if (platform.isVersionUpdateRecommended()) { if (rectify) { - sb.append(String.format(UpdateCommandHandler.PLATFORM_RECTIFY_FORMAT, - UpdateCommandHandler.UPDATE, platform.imported.toCompactCoords())); + sb.append(String.format(UpdateProjectCommandHandler.PLATFORM_RECTIFY_FORMAT, + UpdateProjectCommandHandler.UPDATE, platform.imported.toCompactCoords())); sb.append(platform.imported.toCompactCoords()).append(" -> ") .append(platform.getRecommendedVersion()); } else { @@ -105,7 +106,7 @@ protected static boolean logState(ProjectState projectState, boolean perModule, recommendationsAvailable = true; } else { if (rectify) { - sb.append(String.format(UpdateCommandHandler.PLATFORM_RECTIFY_FORMAT, "", + sb.append(String.format(UpdateProjectCommandHandler.PLATFORM_RECTIFY_FORMAT, "", platform.imported.toCompactCoords())); } else { sb.append(" ").append(platform.imported.toCompactCoords()); @@ -116,7 +117,8 @@ protected static boolean logState(ProjectState projectState, boolean perModule, if (rectify && recommendExtraImports) { for (PlatformInfo platform : providerInfo.values()) { if (platform.imported == null) { - log.info(String.format(UpdateCommandHandler.PLATFORM_RECTIFY_FORMAT, UpdateCommandHandler.ADD, + log.info(String.format(UpdateProjectCommandHandler.PLATFORM_RECTIFY_FORMAT, + UpdateProjectCommandHandler.ADD, platform.recommended.toCompactCoords())); } } @@ -177,10 +179,10 @@ private static boolean logExtensionInfo(TopExtensionDependency dep, boolean rect if (dep.isPlatformExtension()) { if (rectify) { if (dep.isNonRecommendedVersion()) { - sb.append(String.format(UpdateCommandHandler.PLATFORM_RECTIFY_FORMAT, - UpdateCommandHandler.UPDATE, "")); + sb.append(String.format(UpdateProjectCommandHandler.PLATFORM_RECTIFY_FORMAT, + UpdateProjectCommandHandler.UPDATE, "")); } else { - sb.append(String.format(UpdateCommandHandler.PLATFORM_RECTIFY_FORMAT, "", "")); + sb.append(String.format(UpdateProjectCommandHandler.PLATFORM_RECTIFY_FORMAT, "", "")); } sb.append(dep.getArtifact().getGroupId()).append(':') .append(dep.getArtifact().getArtifactId()); @@ -208,7 +210,7 @@ private static boolean logExtensionInfo(TopExtensionDependency dep, boolean rect } } else { if (rectify) { - sb.append(String.format(UpdateCommandHandler.PLATFORM_RECTIFY_FORMAT, "", "")); + sb.append(String.format(UpdateProjectCommandHandler.PLATFORM_RECTIFY_FORMAT, "", "")); } else { sb.append(" "); } diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/UpdateCommandHandler.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/UpdateProjectCommandHandler.java similarity index 79% rename from independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/UpdateCommandHandler.java rename to independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/UpdateProjectCommandHandler.java index 1ce5f9895b124a..1edbac0da5cad8 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/UpdateCommandHandler.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/UpdateProjectCommandHandler.java @@ -1,5 +1,8 @@ package io.quarkus.devtools.commands.handlers; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -7,20 +10,25 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.stream.Collectors; import io.quarkus.bootstrap.model.ApplicationModel; +import io.quarkus.devtools.commands.UpdateProject; import io.quarkus.devtools.commands.data.QuarkusCommandException; import io.quarkus.devtools.commands.data.QuarkusCommandInvocation; import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; -import io.quarkus.devtools.commands.handlers.InfoCommandHandler.PlatformInfo; +import io.quarkus.devtools.commands.handlers.ProjectInfoCommandHandler.PlatformInfo; import io.quarkus.devtools.messagewriter.MessageWriter; +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.QuarkusUpdates; import io.quarkus.maven.dependency.ArtifactCoords; import io.quarkus.maven.dependency.ArtifactKey; +import io.quarkus.platform.tools.ToolsConstants; import io.quarkus.registry.catalog.Extension; import io.quarkus.registry.catalog.ExtensionCatalog; import io.quarkus.registry.catalog.ExtensionOrigin; @@ -29,46 +37,67 @@ import io.quarkus.registry.catalog.selection.OriginPreference; import io.quarkus.registry.catalog.selection.OriginSelector; -public class UpdateCommandHandler implements QuarkusCommandHandler { - - public static final String APP_MODEL = "app-model"; - public static final String LATEST_CATALOG = "latest-catalog"; - public static final String LOG_RECOMMENDED_STATE = "log-recommended-state"; - public static final String LOG_STATE_PER_MODULE = "log-state-per-module"; - public static final String RECTIFY = "rectify"; - +public class UpdateProjectCommandHandler implements QuarkusCommandHandler { public static final String ADD = "Add:"; public static final String REMOVE = "Remove:"; public static final String UPDATE = "Update:"; - public static final String PLATFORM_RECTIFY_FORMAT = "%-7s %s"; @Override public QuarkusCommandOutcome execute(QuarkusCommandInvocation invocation) throws QuarkusCommandException { + final ApplicationModel appModel = invocation.getValue(UpdateProject.APP_MODEL); + final ExtensionCatalog latestCatalog = invocation.getValue(UpdateProject.LATEST_CATALOG); + final String targetPlatformVersion = invocation.getValue(UpdateProject.TARGET_PLATFORM_VERSION); - final ApplicationModel appModel = invocation.getValue(APP_MODEL); - final ExtensionCatalog latestCatalog = invocation.getValue(UpdateCommandHandler.LATEST_CATALOG); - final boolean logRecommendedState = invocation.getValue(UpdateCommandHandler.LOG_RECOMMENDED_STATE, false); - final boolean logStatePerModule = invocation.getValue(UpdateCommandHandler.LOG_STATE_PER_MODULE, false); - final boolean rectify = invocation.getValue(UpdateCommandHandler.RECTIFY, false); - - if (logStatePerModule && !rectify) { - throw new QuarkusCommandException("Update per module isn't supported yet!"); - } + final boolean perModule = invocation.getValue(UpdateProject.PER_MODULE, false); + final boolean generateRewriteConfig = invocation.getValue(UpdateProject.GENERATE_REWRITE_CONFIG, true); - final ProjectState currentState = InfoCommandHandler.resolveProjectState(appModel, + final ProjectState currentState = ProjectInfoCommandHandler.resolveProjectState(appModel, invocation.getQuarkusProject().getExtensionsCatalog()); + final ArtifactCoords projectQuarkusPlatformBom = getProjectQuarkusPlatformBOM(currentState); + + if (projectQuarkusPlatformBom == null) { + invocation.log().error("The project does not import any Quarkus platform BOM"); + return QuarkusCommandOutcome.failure(); + } + if (Objects.equals(projectQuarkusPlatformBom.getVersion(), targetPlatformVersion)) { + invocation.log().info("Instructions to repair this project:"); + // TODO ALEXEY: if the targetPlatformVersion is equal to the project platform version, we should give instructions repair the project + ProjectInfoCommandHandler.logState(currentState, perModule, true, invocation.getQuarkusProject().log()); - if (rectify) { - InfoCommandHandler.logState(currentState, logStatePerModule, rectify, invocation.getQuarkusProject().log()); } else { - logUpdates(currentState, latestCatalog, logRecommendedState, logStatePerModule, + invocation.log().info("Instructions to update this project from '%s' to '%s':", + projectQuarkusPlatformBom.getVersion(), targetPlatformVersion); + // TODO ALEXEY: instructions to update the project + logUpdates(currentState, latestCatalog, false, perModule, invocation.getQuarkusProject().log()); } + if (generateRewriteConfig) { + QuarkusUpdates.ProjectUpdateRequest request = new QuarkusUpdates.ProjectUpdateRequest( + projectQuarkusPlatformBom.getVersion(), targetPlatformVersion); + 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: %s", tempFile.toString()); + } catch (IOException e) { + throw new QuarkusCommandException("Failed to create project update recipe", e); + } + } + return QuarkusCommandOutcome.success(); } + private static ArtifactCoords getProjectQuarkusPlatformBOM(ProjectState currentState) { + for (ArtifactCoords c : currentState.getPlatformBoms()) { + if (c.getArtifactId().equals(ToolsConstants.DEFAULT_PLATFORM_BOM_ARTIFACT_ID)) { + return c; + } + } + return null; + } + private static void logUpdates(ProjectState currentState, ExtensionCatalog recommendedCatalog, boolean recommendState, boolean perModule, MessageWriter log) { if (currentState.getPlatformBoms().isEmpty()) { @@ -86,7 +115,7 @@ private static void logUpdates(ProjectState currentState, ExtensionCatalog recom } if (recommendState) { - InfoCommandHandler.logState(recommendedState, perModule, false, log); + ProjectInfoCommandHandler.logState(recommendedState, perModule, false, log); return; } @@ -117,22 +146,22 @@ private static void logUpdates(ProjectState currentState, ExtensionCatalog recom log.info("Recommended Quarkus platform BOM updates:"); if (!importVersionUpdates.isEmpty()) { for (PlatformInfo importInfo : importVersionUpdates) { - log.info(String.format(UpdateCommandHandler.PLATFORM_RECTIFY_FORMAT, - UpdateCommandHandler.UPDATE, importInfo.imported.toCompactCoords()) + " -> " + log.info(String.format(UpdateProjectCommandHandler.PLATFORM_RECTIFY_FORMAT, + UpdateProjectCommandHandler.UPDATE, importInfo.imported.toCompactCoords()) + " -> " + importInfo.getRecommendedVersion()); } } if (!newImports.isEmpty()) { for (PlatformInfo importInfo : newImports) { - log.info(String.format(UpdateCommandHandler.PLATFORM_RECTIFY_FORMAT, - UpdateCommandHandler.ADD, importInfo.recommended.toCompactCoords())); + log.info(String.format(UpdateProjectCommandHandler.PLATFORM_RECTIFY_FORMAT, + UpdateProjectCommandHandler.ADD, importInfo.recommended.toCompactCoords())); } } if (importsToBeRemoved) { for (PlatformInfo importInfo : platformImports.values()) { if (importInfo.recommended == null) { - log.info(String.format(UpdateCommandHandler.PLATFORM_RECTIFY_FORMAT, - UpdateCommandHandler.REMOVE, importInfo.imported.toCompactCoords())); + log.info(String.format(UpdateProjectCommandHandler.PLATFORM_RECTIFY_FORMAT, + UpdateProjectCommandHandler.REMOVE, importInfo.imported.toCompactCoords())); } } } @@ -205,17 +234,17 @@ private static void logUpdates(ProjectState currentState, ExtensionCatalog recom log.info("Extensions from " + platform.getRecommendedProviderKey() + ":"); for (ExtensionInfo e : versionedManagedExtensions.getOrDefault(provider, Collections.emptyList())) { final StringBuilder sb = new StringBuilder(); - sb.append(String.format(UpdateCommandHandler.PLATFORM_RECTIFY_FORMAT, - UpdateCommandHandler.UPDATE, e.currentDep.getArtifact().toCompactCoords())); + sb.append(String.format(UpdateProjectCommandHandler.PLATFORM_RECTIFY_FORMAT, + UpdateProjectCommandHandler.UPDATE, e.currentDep.getArtifact().toCompactCoords())); sb.append(" -> remove version (managed)"); log.info(sb.toString()); } for (ArtifactCoords e : addedExtensions.getOrDefault(provider, Collections.emptyList())) { - log.info(String.format(UpdateCommandHandler.PLATFORM_RECTIFY_FORMAT, UpdateCommandHandler.ADD, + log.info(String.format(UpdateProjectCommandHandler.PLATFORM_RECTIFY_FORMAT, UpdateProjectCommandHandler.ADD, e.getKey().toGacString())); } for (ArtifactCoords e : removedExtensions.getOrDefault(provider, Collections.emptyList())) { - log.info(String.format(UpdateCommandHandler.PLATFORM_RECTIFY_FORMAT, UpdateCommandHandler.REMOVE, + log.info(String.format(UpdateProjectCommandHandler.PLATFORM_RECTIFY_FORMAT, UpdateProjectCommandHandler.REMOVE, e.getKey().toGacString())); } log.info(""); @@ -226,14 +255,15 @@ private static void logUpdates(ProjectState currentState, ExtensionCatalog recom log.info("Extensions from " + provider.getKey() + ":"); for (ExtensionInfo info : provider.getValue()) { if (info.currentDep.isPlatformExtension()) { - log.info(String.format(UpdateCommandHandler.PLATFORM_RECTIFY_FORMAT, - UpdateCommandHandler.ADD, info.getRecommendedDependency().getArtifact().toCompactCoords())); + log.info(String.format(UpdateProjectCommandHandler.PLATFORM_RECTIFY_FORMAT, + UpdateProjectCommandHandler.ADD, + info.getRecommendedDependency().getArtifact().toCompactCoords())); } else if (info.getRecommendedDependency().isPlatformExtension()) { - log.info(String.format(UpdateCommandHandler.PLATFORM_RECTIFY_FORMAT, - UpdateCommandHandler.REMOVE, info.currentDep.getArtifact().toCompactCoords())); + log.info(String.format(UpdateProjectCommandHandler.PLATFORM_RECTIFY_FORMAT, + UpdateProjectCommandHandler.REMOVE, info.currentDep.getArtifact().toCompactCoords())); } else { - log.info(String.format(UpdateCommandHandler.PLATFORM_RECTIFY_FORMAT, - UpdateCommandHandler.UPDATE, info.currentDep.getArtifact().toCompactCoords() + " -> " + log.info(String.format(UpdateProjectCommandHandler.PLATFORM_RECTIFY_FORMAT, + UpdateProjectCommandHandler.UPDATE, info.currentDep.getArtifact().toCompactCoords() + " -> " + info.getRecommendedDependency().getVersion())); } } diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/buildfile/MavenProjectBuildFile.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/buildfile/MavenProjectBuildFile.java index 9758736e8e320e..a4f963720b8b5b 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/buildfile/MavenProjectBuildFile.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/buildfile/MavenProjectBuildFile.java @@ -21,10 +21,14 @@ import org.apache.maven.model.Dependency; import org.apache.maven.model.DependencyManagement; import org.apache.maven.model.Model; +import org.apache.maven.plugin.MojoExecutionException; import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.artifact.DefaultArtifact; import org.eclipse.aether.resolution.ArtifactDescriptorResult; +import io.quarkus.bootstrap.model.ApplicationModel; +import io.quarkus.bootstrap.resolver.AppModelResolverException; +import io.quarkus.bootstrap.resolver.BootstrapAppModelResolver; import io.quarkus.bootstrap.resolver.maven.BootstrapMavenException; import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver; import io.quarkus.bootstrap.resolver.maven.workspace.LocalProject; @@ -67,6 +71,21 @@ public static QuarkusProject getProject(Path projectDir, MessageWriter log, Supp defaultQuarkusVersion); } + public static ApplicationModel resolveApplicationModel(Path projectDir) throws MojoExecutionException { + final MavenArtifactResolver mvnResolver = getMavenResolver(projectDir); + final LocalProject currentProject = mvnResolver.getMavenContext().getCurrentProject(); + try { + return new BootstrapAppModelResolver(mvnResolver) + .resolveModel(ArtifactCoords.pom(currentProject.getGroupId(), currentProject.getArtifactId(), + currentProject.getVersion())); + } catch (AppModelResolverException e) { + throw new MojoExecutionException("Failed to resolve the Quarkus application model for project " + + ArtifactCoords.pom(currentProject.getGroupId(), currentProject.getArtifactId(), + currentProject.getVersion()), + e); + } + } + public static QuarkusProject getProject(Artifact projectPom, Model projectModel, Path projectDir, Properties projectProps, MavenArtifactResolver artifactResolver, MessageWriter log, Supplier defaultQuarkusVersion) throws RegistryResolutionException { diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/QuarkusUpdateRecipe.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/QuarkusUpdateRecipe.java new file mode 100644 index 00000000000000..321cef54a3ffca --- /dev/null +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/QuarkusUpdateRecipe.java @@ -0,0 +1,73 @@ +package io.quarkus.devtools.project.update; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +import io.quarkus.devtools.project.BuildTool; + +public class QuarkusUpdateRecipe { + + public static final Map QUARKUS_RECIPE = Map.of( + "type", "specs.openrewrite.org/v1beta/recipe", + "name", "io.quarkus.openrewrite.Quarkus", + "displayName", "Migrate quarkus project to a new version", + "description", "Update Quarkus version and refactor imports and resources if needed.", + "tags", List.of("quarkus")); + + public static final String RECIPE_LIST_KEY = "recipeList"; + private BuildTool buildTool = BuildTool.MAVEN; + private final List operations = new ArrayList<>(); + private final List> recipes = new ArrayList<>(); + + public QuarkusUpdateRecipe() { + } + + public QuarkusUpdateRecipe buildTool(BuildTool buildTool) { + this.buildTool = buildTool; + return this; + } + + public QuarkusUpdateRecipe addOperation(RewriteOperation operation) { + this.operations.add(operation); + return this; + } + + @SuppressWarnings("unchecked") + public QuarkusUpdateRecipe addRecipes(List recipe) { + for (Object r : recipe) { + if (r instanceof Map) { + addRecipe((Map) r); + } + } + return this; + } + + public QuarkusUpdateRecipe addRecipe(Map recipe) { + Objects.requireNonNull(recipe, "recipe is required"); + if (!recipe.containsKey("name") || !(recipe.get("name") instanceof String)) { + throw new IllegalArgumentException("Recipe name is required"); + } + this.recipes.add(recipe); + return this; + } + + public BuildTool getBuildTool() { + return buildTool; + } + + public List getOperations() { + return operations; + } + + public List> getRecipes() { + return recipes; + } + + public List getOtherRecipeNames() { + return recipes.stream().map(r -> (String) r.get("name")).collect(Collectors.toList()); + } + +} diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/QuarkusUpdateRecipeIO.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/QuarkusUpdateRecipeIO.java new file mode 100644 index 00000000000000..964e0e44822ba5 --- /dev/null +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/QuarkusUpdateRecipeIO.java @@ -0,0 +1,63 @@ +package io.quarkus.devtools.project.update; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.Yaml; + +public class QuarkusUpdateRecipeIO { + + /** + * Read recipe to be used with {@link QuarkusUpdateRecipe#addRecipes(List)} + * + * @param recipeYaml the recipe yaml + * @return + */ + public static List readRecipesYaml(String recipeYaml) { + Objects.requireNonNull(recipeYaml, "inputStream is required"); + Yaml yaml = new Yaml(); + List otherRecipes = new ArrayList<>(); + yaml.loadAll(recipeYaml).iterator().forEachRemaining(otherRecipes::add); + return List.copyOf(otherRecipes); + } + + /** + * Write a Quarkus project update recipe on the disk + * + * @param target + * @param recipe + * @throws IOException + */ + public static void write(Path target, QuarkusUpdateRecipe recipe) throws IOException { + Objects.requireNonNull(target, "target is required"); + Objects.requireNonNull(recipe, "recipe is required"); + Files.writeString(target, toYaml(recipe)); + } + + static String toYaml(QuarkusUpdateRecipe recipe) { + Objects.requireNonNull(recipe, "recipe is required"); + Map q = new HashMap<>(); + q.putAll(QuarkusUpdateRecipe.QUARKUS_RECIPE); + List recipeList = new ArrayList<>(); + for (RewriteOperation o : recipe.getOperations()) { + recipeList.add(o.toMap(recipe.getBuildTool())); + } + recipeList.addAll(recipe.getOtherRecipeNames()); + q.put(QuarkusUpdateRecipe.RECIPE_LIST_KEY, recipeList); + List output = new ArrayList<>(); + output.add(q); + output.addAll(recipe.getRecipes()); + final DumperOptions options = new DumperOptions(); + options.setPrettyFlow(true); + Yaml yaml = new Yaml(options); + return yaml.dumpAll(output.iterator()); + } + +} 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 new file mode 100644 index 00000000000000..9a73585606c561 --- /dev/null +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/QuarkusUpdates.java @@ -0,0 +1,41 @@ +package io.quarkus.devtools.project.update; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.List; + +import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver; +import io.quarkus.devtools.project.BuildTool; +import io.quarkus.devtools.project.update.operations.UpdatePropertyOperation; + +public final class QuarkusUpdates { + + private QuarkusUpdates() { + } + + public static void createRecipe(Path target, MavenArtifactResolver artifactResolver, ProjectUpdateRequest request) + throws IOException { + final List recipes = QuarkusUpdatesRepository.fetchLatestRecipes(artifactResolver, request.currentVersion, + request.targetVersion); + QuarkusUpdateRecipe recipe = new QuarkusUpdateRecipe() + .buildTool(request.buildTool) + .addOperation(new UpdatePropertyOperation("quarkus.platform.version", request.targetVersion)) + .addOperation(new UpdatePropertyOperation("quarkus.version", request.targetVersion)); + for (String s : recipes) { + recipe.addRecipes(QuarkusUpdateRecipeIO.readRecipesYaml(s)); + } + QuarkusUpdateRecipeIO.write(target, recipe); + } + + public static class ProjectUpdateRequest { + + public BuildTool buildTool = BuildTool.MAVEN; + public String currentVersion; + public String targetVersion; + + public ProjectUpdateRequest(String currentVersion, String targetVersion) { + this.currentVersion = currentVersion; + this.targetVersion = targetVersion; + } + } +} 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 new file mode 100644 index 00000000000000..0db05e1be9351d --- /dev/null +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/QuarkusUpdatesRepository.java @@ -0,0 +1,59 @@ +package io.quarkus.devtools.project.update; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; + +import io.quarkus.bootstrap.resolver.maven.BootstrapMavenException; +import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver; +import io.quarkus.bootstrap.util.DependencyUtils; +import io.quarkus.platform.descriptor.loader.json.ResourceLoader; +import io.quarkus.platform.descriptor.loader.json.ResourceLoaders; + +public final class QuarkusUpdatesRepository { + + private QuarkusUpdatesRepository() { + } + + private static final String QUARKUS_RECIPE_GAV = "io.quarkus:quarkus-update-recipes:LATEST"; + + public static List fetchLatestRecipes(MavenArtifactResolver artifactResolver, String currentVersion, + String targetVersion) { + try { + final ResourceLoader resourceLoader = ResourceLoaders.resolveFileResourceLoader( + artifactResolver.resolve(DependencyUtils.toArtifact(QUARKUS_RECIPE_GAV)).getArtifact().getFile()); + return resourceLoader.loadResourceAsPath("quarkus-updates/core", + path -> { + try (final Stream pathStream = Files.walk(path)) { + return pathStream + .filter(p -> p.getFileName().toString().matches("^\\d\\.\\d(\\.\\d)?\\.ya?ml$")) + .filter(p -> shouldApplyRecipe(p.getFileName().toString(), currentVersion, targetVersion)) + .map(p -> { + try { + return new String(Files.readAllBytes(p)); + } catch (IOException e) { + throw new RuntimeException(e); + } + }).collect(Collectors.toList()); + } + }); + } catch (BootstrapMavenException e) { + throw new RuntimeException("Failed to resolve artifact " + QUARKUS_RECIPE_GAV, e); + } catch (IOException e) { + throw new RuntimeException("Failed to load recipes in artifact " + QUARKUS_RECIPE_GAV, e); + } + } + + static boolean shouldApplyRecipe(String recipeFileName, String currentVersion, String targetVersion) { + String recipeVersion = recipeFileName.replaceFirst("[.][^.]+$", ""); + final DefaultArtifactVersion recipeAVersion = new DefaultArtifactVersion(recipeVersion); + final DefaultArtifactVersion currentAVersion = new DefaultArtifactVersion(currentVersion); + final DefaultArtifactVersion targetAVersion = new DefaultArtifactVersion(targetVersion); + return currentAVersion.compareTo(recipeAVersion) < 0 && targetAVersion.compareTo(recipeAVersion) >= 0; + } +} diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/RewriteOperation.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/RewriteOperation.java new file mode 100644 index 00000000000000..1cd866a5ef55ef --- /dev/null +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/RewriteOperation.java @@ -0,0 +1,21 @@ +package io.quarkus.devtools.project.update; + +import java.util.Map; + +import io.quarkus.devtools.project.BuildTool; + +/** + * A rewrite operation to be used in a QuarkusUpdateRecipe + */ +public interface RewriteOperation { + + /** + * Return the OpenRewrite operation name and the parameters as a map + * + * { "operation-name": { "p1": "v1" }} + * + * @param buildTool + * @return + */ + Map toMap(BuildTool buildTool); +} diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/operations/UpdateDependencyVersionOperation.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/operations/UpdateDependencyVersionOperation.java new file mode 100644 index 00000000000000..8f6c63b65e4fd6 --- /dev/null +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/operations/UpdateDependencyVersionOperation.java @@ -0,0 +1,34 @@ +package io.quarkus.devtools.project.update.operations; + +import java.util.Map; + +import io.quarkus.devtools.project.BuildTool; +import io.quarkus.devtools.project.update.RewriteOperation; + +public class UpdateDependencyVersionOperation implements RewriteOperation { + + private final String groupId; + private final String artifactId; + private final String newVersion; + + public UpdateDependencyVersionOperation(String groupId, String artifactId, String newVersion) { + this.groupId = groupId; + this.artifactId = artifactId; + this.newVersion = newVersion; + } + + @Override + public Map toMap(BuildTool buildTool) { + switch (buildTool) { + case MAVEN: + return Map.of("org.openrewrite.maven.UpgradeDependencyVersion", + Map.of( + "groupId", groupId, + "artifactId", artifactId, + "newVersion", newVersion)); + default: + return Map.of(); + } + } + +} diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/operations/UpdateManagedDependencyVersionOperation.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/operations/UpdateManagedDependencyVersionOperation.java new file mode 100644 index 00000000000000..4959ee7d2e8328 --- /dev/null +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/operations/UpdateManagedDependencyVersionOperation.java @@ -0,0 +1,36 @@ +package io.quarkus.devtools.project.update.operations; + +import java.util.Map; + +import io.quarkus.devtools.project.BuildTool; +import io.quarkus.devtools.project.update.RewriteOperation; + +public class UpdateManagedDependencyVersionOperation implements RewriteOperation { + + private final String groupId; + private final String artifactId; + private final String newVersion; + + public UpdateManagedDependencyVersionOperation(String groupId, String artifactId, String newVersion) { + this.groupId = groupId; + this.artifactId = artifactId; + this.newVersion = newVersion; + } + + @Override + public Map toMap(BuildTool buildTool) { + switch (buildTool) { + case MAVEN: + return Map.of("org.openrewrite.maven.ChangeManagedDependencyGroupIdAndArtifactId", + Map.of( + "oldGroupId", groupId, + "oldArtifactId", artifactId, + "newGroupId", groupId, + "newArtifactId", artifactId, + "newVersion", newVersion)); + default: + return Map.of(); + } + } + +} diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/operations/UpdatePropertyOperation.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/operations/UpdatePropertyOperation.java new file mode 100644 index 00000000000000..464f625ec42f3b --- /dev/null +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/operations/UpdatePropertyOperation.java @@ -0,0 +1,32 @@ +package io.quarkus.devtools.project.update.operations; + +import java.util.Map; + +import io.quarkus.devtools.project.BuildTool; +import io.quarkus.devtools.project.update.RewriteOperation; + +public class UpdatePropertyOperation implements RewriteOperation { + + public String key; + public String newValue; + + public UpdatePropertyOperation(String key, String newValue) { + this.key = key; + this.newValue = newValue; + } + + @Override + public Map toMap(BuildTool buildTool) { + switch (buildTool) { + case MAVEN: + return Map.of("org.openrewrite.maven.ChangePropertyValue", + Map.of("key", key, "newValue", newValue)); + case GRADLE: + return Map.of( + "org.openrewrite.gradle.AddProperty", + Map.of("key", key, "value", newValue, "overwrite", true)); + default: + return Map.of(); + } + } +} diff --git a/independent-projects/tools/devtools-common/src/test/java/io/quarkus/devtools/project/update/QuarkusUpdateRecipeIOTest.java b/independent-projects/tools/devtools-common/src/test/java/io/quarkus/devtools/project/update/QuarkusUpdateRecipeIOTest.java new file mode 100644 index 00000000000000..54b7b599869848 --- /dev/null +++ b/independent-projects/tools/devtools-common/src/test/java/io/quarkus/devtools/project/update/QuarkusUpdateRecipeIOTest.java @@ -0,0 +1,36 @@ +package io.quarkus.devtools.project.update; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; + +import org.junit.jupiter.api.Test; + +import io.quarkus.devtools.project.update.operations.UpdateManagedDependencyVersionOperation; +import io.quarkus.devtools.project.update.operations.UpdatePropertyOperation; + +class QuarkusUpdateRecipeIOTest { + + @Test + void shouldGenerateYamlCorrectly() throws IOException { + final QuarkusUpdateRecipe recipe = new QuarkusUpdateRecipe(); + + recipe.addRecipes(QuarkusUpdateRecipeIO + .readRecipesYaml( + new String(QuarkusUpdateRecipeIOTest.class.getResourceAsStream("/other-recipes.yaml").readAllBytes()))); + recipe.addOperation(new UpdatePropertyOperation("quarkus.platform.version", "3.0.0.Alpha1")); + recipe.addOperation(new UpdatePropertyOperation("quarkus.version", "3.0.0.Alpha1")); + // Just an example + recipe.addOperation(new UpdateManagedDependencyVersionOperation("io.quarkus", "quarkus-bom", "example")); + final String output = QuarkusUpdateRecipeIO.toYaml(recipe); + System.out.println(output); + assertThat(output) + .contains("org.openrewrite.maven.ChangePropertyValue") + .contains("newValue: 3.0.0.Alpha1") + .contains("newVersion: example") + .contains("name: org.openrewrite.java.migrate.JavaxActivationMigrationToJakartaActivation") + .contains("- org.openrewrite.java.migrate.JavaxActivationMigrationToJakartaActivation") + .contains("name: org.openrewrite.java.migrate.JavaxXmlSoapToJakartaXmlSoap") + .contains("- org.openrewrite.java.migrate.JavaxXmlSoapToJakartaXmlSoap"); + } +} diff --git a/independent-projects/tools/devtools-common/src/test/resources/other-recipes.yaml b/independent-projects/tools/devtools-common/src/test/resources/other-recipes.yaml new file mode 100644 index 00000000000000..ef80aa3b3b458d --- /dev/null +++ b/independent-projects/tools/devtools-common/src/test/resources/other-recipes.yaml @@ -0,0 +1,913 @@ +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.java.migrate.JavaxActivationMigrationToJakartaActivation +displayName: Migrate deprecated `javax.activation` packages to `jakarta.activation` +description: Java EE has been rebranded to Jakarta EE, necessitating a package relocation. +tags: + - activation + - javax + - jakarta +recipeList: +# - org.openrewrite.maven.AddDependency: +# groupId: jakarta.activation +# artifactId: jakarta.activation-api +# version: 2.x +# onlyIfUsing: javax.activation.* + + # Upgrade the dependency to use the jakarta namespace if an older version already exists. + - org.openrewrite.maven.UpgradeDependencyVersion: + groupId: jakarta.activation + artifactId: jakarta.activation-api + newVersion: 2.x + + # Note: ChangePackage does not update java doc references. + # f(x).transaction + - org.openrewrite.java.ChangePackage: + oldPackageName: javax.activation + newPackageName: jakarta.activation + recursive: true + + # Remove Javax Batch API(x) + - org.openrewrite.maven.RemoveDependency: + groupId: javax.activation + artifactId: javax.activation-api + +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.java.migrate.JavaxAnnotationMigrationToJakartaAnnotation +displayName: Migrate deprecated `javax.annotation` packages to `jakarta.annotation` +description: Java EE has been rebranded to Jakarta EE, necessitating a package relocation. +tags: + - annotation + - javax + - jakarta + +recipeList: +# - org.openrewrite.maven.AddDependency: +# groupId: jakarta.annotation +# artifactId: jakarta.annotation-api +# version: 2.x +# onlyIfUsing: javax.annotation.* + + # Upgrade the dependency to use the jakarta namespace if an older version already exists. + - org.openrewrite.maven.UpgradeDependencyVersion: + groupId: jakarta.annotation + artifactId: jakarta.annotation-api + newVersion: 2.x + + - org.openrewrite.java.migrate.ChangeJavaxAnnotationToJakarta + + # Remove Javax Batch API(x) + - org.openrewrite.maven.RemoveDependency: + groupId: javax.annotation + artifactId: javax.annotation-api + +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.java.migrate.ChangeJavaxAnnotationToJakarta +displayName: Migrate deprecated `javax.annotation` packages to `jakarta.annotation` +description: Java EE has been rebranded to Jakarta EE, necessitating a package relocation. Excludes `javax.annotation.processing`. +tags: + - batch + - javax + - jakarta + +recipeList: + - org.openrewrite.java.migrate.JavaxAnnotationPackageToJakarta + - org.openrewrite.java.migrate.JavaxAnnotationSecurityPackageToJakarta + - org.openrewrite.java.migrate.JavaxAnnotationSqlPackageToJakarta + +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.java.migrate.JavaxAnnotationPackageToJakarta +displayName: Migrate deprecated `javax.annotation` packages to `jakarta.annotation` +description: Change type of classes in the `javax.annotation` package to jakarta. +tags: + - batch + - javax + - jakarta + +recipeList: + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: javax.annotation.Generated + newFullyQualifiedTypeName: jakarta.annotation.Generated + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: javax.annotation.ManagedBean + newFullyQualifiedTypeName: jakarta.annotation.ManagedBean + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: javax.annotation.PostConstruct + newFullyQualifiedTypeName: jakarta.annotation.PostConstruct + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: javax.annotation.PreDestroy + newFullyQualifiedTypeName: jakarta.annotation.PreDestroy + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: javax.annotation.Priority + newFullyQualifiedTypeName: jakarta.annotation.Priority + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: javax.annotation.Resource + newFullyQualifiedTypeName: jakarta.annotation.Resource + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: javax.annotation.Resources + newFullyQualifiedTypeName: jakarta.annotation.Resources + +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.java.migrate.JavaxAnnotationSecurityPackageToJakarta +displayName: Migrate deprecated `javax.annotation.security` packages to `jakarta.annotation.security` +description: Change type of classes in the `javax.annotation.security` package to jakarta. +tags: + - batch + - javax + - jakarta + +recipeList: + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: javax.annotation.security.DeclareRoles + newFullyQualifiedTypeName: jakarta.annotation.security.DeclareRoles + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: javax.annotation.security.DenyAll + newFullyQualifiedTypeName: jakarta.annotation.security.DenyAll + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: javax.annotation.security.PermitAll + newFullyQualifiedTypeName: jakarta.annotation.security.PermitAll + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: javax.annotation.security.RolesAllowed + newFullyQualifiedTypeName: jakarta.annotation.security.RolesAllowed + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: javax.annotation.security.RunAs + newFullyQualifiedTypeName: jakarta.annotation.security.RunAs + +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.java.migrate.JavaxAnnotationSqlPackageToJakarta +displayName: Migrate deprecated `javax.annotation.sql` packages to `jakarta.annotation.sql` +description: Change type of classes in the `javax.annotation.sql` package to jakarta. +tags: + - batch + - javax + - jakarta + +recipeList: + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: javax.annotation.sql.DataSourceDefinition + newFullyQualifiedTypeName: jakarta.annotation.sql.DataSourceDefinition + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: javax.annotation.sql.DataSourceDefinitions + newFullyQualifiedTypeName: jakarta.annotation.sql.DataSourceDefinitions + +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.java.migrate.JavaxBatchMigrationToJakartaBatch +displayName: Migrate deprecated `javax.batch` packages to `jakarta.batch` +description: Java EE has been rebranded to Jakarta EE, necessitating a package relocation. +tags: + - batch + - javax + - jakarta + +recipeList: +# - org.openrewrite.maven.AddDependency: +# groupId: jakarta.batch +# artifactId: jakarta.batch-api +# version: 2.x +# onlyIfUsing: javax.batch.* + + # Upgrade the dependency to use the jakarta namespace if an older version already exists. + - org.openrewrite.maven.UpgradeDependencyVersion: + groupId: jakarta.batch + artifactId: jakarta.batch-api + newVersion: 2.x + + # Note: ChangePackage does not update java doc references. + # f(x).batch + - org.openrewrite.java.ChangePackage: + oldPackageName: javax.batch + newPackageName: jakarta.batch + recursive: true + + # Remove Javax Batch API(x) + - org.openrewrite.maven.RemoveDependency: + groupId: javax.batch + artifactId: javax.batch-api +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.java.migrate.JavaxValidationMigrationToJakartaValidation +displayName: Migrate deprecated `javax.validation` packages to `jakarta.validation` +description: Java EE has been rebranded to Jakarta EE, necessitating a package relocation. +tags: + - validation + - javax + - jakarta + +recipeList: +# - org.openrewrite.maven.AddDependency: +# groupId: jakarta.validation +# artifactId: jakarta.validation-api +# version: 3.x +# onlyIfUsing: +# - javax.batch.* + + - org.openrewrite.maven.UpgradeDependencyVersion: + groupId: jakarta.validation + artifactId: jakarta.validation-api + newVersion: 3.x + + - org.openrewrite.java.ChangePackage: + oldPackageName: javax.validation + newPackageName: jakarta.validation + recursive: true + + # Remove Javax Validation API + - org.openrewrite.maven.RemoveDependency: + groupId: javax.validation + artifactId: validation-api + + +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.java.migrate.JavaxDecoratorToJakartaDecorator +displayName: Migrate deprecated `javax.decorator` packages to `jakarta.decorator` +description: Java EE has been rebranded to Jakarta EE, necessitating a package relocation. +recipeList: +# - org.openrewrite.maven.AddDependency: +# groupId: jakarta.enterprise +# artifactId: jakarta.enterprise.cdi-api +# version: 2.x +# onlyIfUsing: javax.decorator.+ + + # Upgrade the dependency to use the jakarta namespace if an older version already exists. + - org.openrewrite.maven.UpgradeDependencyVersion: + groupId: jakarta.enterprise + artifactId: jakarta.enterprise.cdi-api + newVersion: 2.x + + - org.openrewrite.java.ChangePackage: + oldPackageName: javax.decorator + newPackageName: jakarta.decorator + recursive: true + + # Remove Javax API(x) + - org.openrewrite.maven.RemoveDependency: + groupId: javax.enterprise + artifactId: cdi-api + + +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.java.migrate.JavaxEjbToJakartaEjb +displayName: Migrate deprecated `javax.ejb` packages to `jakarta.ejb` +description: Java EE has been rebranded to Jakarta EE, necessitating a package relocation. +recipeList: +# - org.openrewrite.maven.AddDependency: +# groupId: jakarta.ejb +# artifactId: jakarta.ejb-api +# version: 4.x +# onlyIfUsing: javax.ejb.+ + + # Upgrade the dependency to use the jakarta namespace if an older version already exists. + - org.openrewrite.maven.UpgradeDependencyVersion: + groupId: jakarta.ejb + artifactId: jakarta.ejb-api + newVersion: 4.x + + - org.openrewrite.java.ChangePackage: + oldPackageName: javax.ejb + newPackageName: jakarta.ejb + recursive: true + + # Remove Javax API(x) + - org.openrewrite.maven.RemoveDependency: + groupId: javax.ejb + artifactId: javax.ejb-api + +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.java.migrate.JavaxElToJakartaEl +displayName: Migrate deprecated `javax.el` packages to `jakarta.el` +description: Java EE has been rebranded to Jakarta EE, necessitating a package relocation. +recipeList: +# - org.openrewrite.maven.AddDependency: +# groupId: jakarta.el +# artifactId: jakarta.el-api +# version: 4.x +# onlyIfUsing: javax.el.* + + # Upgrade the dependency to use the jakarta namespace if an older version already exists. + - org.openrewrite.maven.UpgradeDependencyVersion: + groupId: jakarta.el + artifactId: jakarta.el-api + newVersion: 4.x + + - org.openrewrite.java.ChangePackage: + oldPackageName: javax.el + newPackageName: jakarta.el + recursive: true + + # Remove Javax API(x) + - org.openrewrite.maven.RemoveDependency: + groupId: javax.el + artifactId: javax.el-api + +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.java.migrate.JavaxEnterpriseToJakartaEnterprise +displayName: Migrate deprecated `javax.enterprise` packages to `jakarta.enterprise` +description: Java EE has been rebranded to Jakarta EE, necessitating a package relocation. +recipeList: +# - org.openrewrite.maven.AddDependency: +# groupId: jakarta.enterprise +# artifactId: jakarta.enterprise.cdi-api +# version: 3.0.1 +# onlyIfUsing: javax.enterprise.+ + + # Upgrade the dependency to use the jakarta namespace if an older version already exists. + - org.openrewrite.maven.UpgradeDependencyVersion: + groupId: jakarta.enterprise + artifactId: jakarta.enterprise.cdi-api + newVersion: 3.0.1 + + - org.openrewrite.java.ChangePackage: + oldPackageName: javax.enterprise + newPackageName: jakarta.enterprise + recursive: true + + # Remove Javax API(x) + - org.openrewrite.maven.RemoveDependency: + groupId: javax.enterprise + artifactId: cdi-api + +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.java.migrate.JavaxFacesToJakartaFaces +displayName: Migrate deprecated `javax.faces` packages to `jakarta.faces` +description: Java EE has been rebranded to Jakarta EE, necessitating a package relocation. +recipeList: +# - org.openrewrite.maven.AddDependency: +# groupId: jakarta.faces +# artifactId: jakarta.faces-api +# version: 3.x +# onlyIfUsing: javax.faces.+ + + # Upgrade the dependency to use the jakarta namespace if an older version already exists. + - org.openrewrite.maven.UpgradeDependencyVersion: + groupId: jakarta.faces + artifactId: jakarta.faces-api + newVersion: 3.x + + - org.openrewrite.java.ChangePackage: + oldPackageName: javax.faces + newPackageName: jakarta.faces + recursive: true + + # Remove Javax API(x) + - org.openrewrite.maven.RemoveDependency: + groupId: javax.faces + artifactId: javax.faces-api + + # Remove Javax API(x) + - org.openrewrite.maven.RemoveDependency: + groupId: org.glassfish + artifactId: javax.faces + +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.java.migrate.JavaxInjectMigrationToJakartaInject +displayName: Migrate deprecated `javax.inject` packages to `jakarta.inject` +description: Java EE has been rebranded to Jakarta EE, necessitating a package relocation. +tags: + - inject + - javax + - jakarta + +recipeList: +# - org.openrewrite.maven.AddDependency: +# groupId: jakarta.inject +# artifactId: jakarta.inject-api +# version: 2.x +# onlyIfUsing: javax.batch.* + + # Upgrade the dependency to use the jakarta namespace if an older version already exists. + - org.openrewrite.maven.UpgradeDependencyVersion: + groupId: jakarta.inject + artifactId: jakarta.inject-api + newVersion: 2.x + + # Note: ChangePackage does not update java doc references. + # f(x).batch + - org.openrewrite.java.ChangePackage: + oldPackageName: javax.inject + newPackageName: jakarta.inject + recursive: true + + # Remove Javax Inject API(x) + - org.openrewrite.maven.RemoveDependency: + groupId: javax.inject + artifactId: javax.inject-api + +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.java.migrate.JavaxInterceptorToJakartaInterceptor +displayName: Migrate deprecated `javax.interceptor` packages to `jakarta.interceptor` +description: Java EE has been rebranded to Jakarta EE, necessitating a package relocation. +recipeList: +# - org.openrewrite.maven.AddDependency: +# groupId: jakarta.interceptor +# artifactId: jakarta.interceptor-api +# version: 2.x +# onlyIfUsing: javax.interceptor.* + + # Upgrade the dependency to use the jakarta namespace if an older version already exists. + - org.openrewrite.maven.UpgradeDependencyVersion: + groupId: jakarta.interceptor + artifactId: jakarta.interceptor-api + newVersion: 2.x + + - org.openrewrite.java.ChangePackage: + oldPackageName: javax.interceptor + newPackageName: jakarta.interceptor + recursive: true + + # Remove Javax Inject API(x) + - org.openrewrite.maven.RemoveDependency: + groupId: javax.interceptor + artifactId: javax.interceptor-api + +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.java.migrate.JavaxJmsToJakartaJms +displayName: Migrate deprecated `javax.jms` packages to `jakarta.jms` +description: Java EE has been rebranded to Jakarta EE, necessitating a package relocation. +recipeList: +# - org.openrewrite.maven.AddDependency: +# groupId: jakarta.jms +# artifactId: jakarta.jms-api +# version: 3.x +# onlyIfUsing: javax.jms.* + + # Upgrade the dependency to use the jakarta namespace if an older version already exists. + - org.openrewrite.maven.UpgradeDependencyVersion: + groupId: jakarta.jms + artifactId: jakarta.jms-api + newVersion: 3.x + + - org.openrewrite.java.ChangePackage: + oldPackageName: javax.jms + newPackageName: jakarta.jms + recursive: true + + # Remove Javax Inject API(x) + - org.openrewrite.maven.RemoveDependency: + groupId: javax.jms + artifactId: javax.jms-api + +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.java.migrate.JavaxJsonToJakartaJson +displayName: Migrate deprecated `javax.json` packages to `jakarta.json` +description: Java EE has been rebranded to Jakarta EE, necessitating a package relocation. +recipeList: +# - org.openrewrite.maven.AddDependency: +# groupId: jakarta.json +# artifactId: jakarta.json-api +# version: 2.x +# onlyIfUsing: javax.json.* + + # Upgrade the dependency to use the jakarta namespace if an older version already exists. + - org.openrewrite.maven.UpgradeDependencyVersion: + groupId: jakarta.json + artifactId: jakarta.json-api + newVersion: 2.x + + - org.openrewrite.java.ChangePackage: + oldPackageName: javax.json + newPackageName: jakarta.json + recursive: true + + # Remove Javax Inject API(x) + - org.openrewrite.maven.RemoveDependency: + groupId: javax.json + artifactId: javax.json-api + +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.java.migrate.JavaxJwsToJakartaJws +displayName: Migrate deprecated `javax.jws` packages to `jakarta.jws` +description: Java EE has been rebranded to Jakarta EE, necessitating a package relocation. +recipeList: +# - org.openrewrite.maven.AddDependency: +# groupId: jakarta.jws +# artifactId: jakarta.jws-api +# version: 3.x +# onlyIfUsing: javax.jws.* + + # Upgrade the dependency to use the jakarta namespace if an older version already exists. + - org.openrewrite.maven.UpgradeDependencyVersion: + groupId: jakarta.jws + artifactId: jakarta.jws-api + newVersion: 3.x + + - org.openrewrite.java.ChangePackage: + oldPackageName: javax.jws + newPackageName: jakarta.jws + recursive: true + + # Remove Javax Inject API(x) + - org.openrewrite.maven.RemoveDependency: + groupId: javax.jws + artifactId: javax.jws-api + +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.java.migrate.JavaxMailToJakartaMail +displayName: Migrate deprecated `javax.mail` packages to `jakarta.mail` +description: Java EE has been rebranded to Jakarta EE, necessitating a package relocation. +recipeList: +# - org.openrewrite.maven.AddDependency: +# groupId: jakarta.mail +# artifactId: jakarta.mail-api +# version: 2.x +# onlyIfUsing: javax.mail.* + + # Upgrade the dependency to use the jakarta namespace if an older version already exists. + - org.openrewrite.maven.UpgradeDependencyVersion: + groupId: jakarta.mail + artifactId: jakarta.mail-api + newVersion: 2.x + + - org.openrewrite.java.ChangePackage: + oldPackageName: javax.mail + newPackageName: jakarta.mail + recursive: true + + # Remove Javax Inject API(x) + - org.openrewrite.maven.RemoveDependency: + groupId: javax.mail + artifactId: javax.mail-api + +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.java.migrate.JavaxPersistenceToJakartaPersistence +displayName: Migrate deprecated `javax.persistence` packages to `jakarta.persistence` +description: Java EE has been rebranded to Jakarta EE, necessitating a package relocation +recipeList: +# - org.openrewrite.maven.AddDependency: +# groupId: jakarta.persistence +# artifactId: jakarta.persistence-api +# version: 3.x +# onlyIfUsing: javax.persistence.* + + # Upgrade the dependency to use the jakarta namespace if an older version already exists. + - org.openrewrite.maven.UpgradeDependencyVersion: + groupId: jakarta.persistence + artifactId: jakarta.persistence-api + newVersion: 3.x + + - org.openrewrite.java.ChangePackage: + oldPackageName: javax.persistence + newPackageName: jakarta.persistence + recursive: true + + # Remove Javax Inject API(x) + - org.openrewrite.maven.RemoveDependency: + groupId: javax.persistence + artifactId: javax.persistence-api + + # Remove Javax Inject API(x) + - org.openrewrite.maven.RemoveDependency: + groupId: org.eclipse.persistence + artifactId: javax.persistence + +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.java.migrate.JavaxResourceToJakartaResource +displayName: Migrate deprecated `javax.resource` packages to `jakarta.resource` +description: Java EE has been rebranded to Jakarta EE, necessitating a package relocation. +recipeList: +# - org.openrewrite.maven.AddDependency: +# groupId: jakarta.resource +# artifactId: jakarta.resource-api +# version: 2.x +# onlyIfUsing: javax.resource.* + + # Upgrade the dependency to use the jakarta namespace if an older version already exists. + - org.openrewrite.maven.UpgradeDependencyVersion: + groupId: jakarta.resource + artifactId: jakarta.resource-api + newVersion: 2.x + + - org.openrewrite.java.ChangePackage: + oldPackageName: javax.resource + newPackageName: jakarta.resource + recursive: true + + # Remove Javax Inject API(x) + - org.openrewrite.maven.RemoveDependency: + groupId: javax.resource + artifactId: javax.resource-api + +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.java.migrate.JavaxSecurityToJakartaSecurity +displayName: Migrate deprecated `javax.security` packages to `jakarta.security` +description: Java EE has been rebranded to Jakarta EE, necessitating a package relocation. +recipeList: +# - org.openrewrite.maven.AddDependency: +# groupId: jakarta.security.enterprise +# artifactId: jakarta.security.enterprise-api +# version: 2.x +# onlyIfUsing: javax.security.* + + # Upgrade the dependency to use the jakarta namespace if an older version already exists. + - org.openrewrite.maven.UpgradeDependencyVersion: + groupId: jakarta.security.enterprise + artifactId: jakarta.security.enterprise-api + newVersion: 2.x + + - org.openrewrite.java.ChangePackage: + oldPackageName: javax.security + newPackageName: jakarta.security + recursive: true + + # Remove Javax Inject API(x) + - org.openrewrite.maven.RemoveDependency: + groupId: javax.security.enterprise + artifactId: javax.security.enterprise-api + +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.java.migrate.JavaxServletToJakartaServlet +displayName: Migrate deprecated `javax.servlet` packages to `jakarta.servlet` +description: Java EE has been rebranded to Jakarta EE, necessitating a package relocation. +recipeList: +# - org.openrewrite.maven.AddDependency: +# groupId: jakarta.servlet +# artifactId: jakarta.servlet-api +# version: 5.x +# onlyIfUsing: javax.servlet.* + + # Upgrade the dependency to use the jakarta namespace if an older version already exists. + - org.openrewrite.maven.UpgradeDependencyVersion: + groupId: jakarta.servlet + artifactId: jakarta.servlet-api + newVersion: 5.x + + - org.openrewrite.java.ChangePackage: + oldPackageName: javax.servlet + newPackageName: jakarta.servlet + recursive: true + + # Remove Javax Transaction API(x) + - org.openrewrite.maven.RemoveDependency: + groupId: javax.servlet + artifactId: javax.servlet-api + +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.java.migrate.JavaxTransactionMigrationToJakartaTransaction +displayName: Migrate deprecated `javax.transaction` packages to `jakarta.transaction` +description: Java EE has been rebranded to Jakarta EE, necessitating a package relocation. +tags: + - transaction + - javax + - jakarta + +recipeList: +# - org.openrewrite.maven.AddDependency: +# groupId: jakarta.transaction +# artifactId: jakarta.transaction-api +# version: 2.x +# onlyIfUsing: javax.transaction.* + + # Upgrade the dependency to use the jakarta namespace if an older version already exists. + - org.openrewrite.maven.UpgradeDependencyVersion: + groupId: jakarta.transaction + artifactId: jakarta.transaction-api + newVersion: 2.x + + # f(x).transaction + - org.openrewrite.java.ChangePackage: + oldPackageName: javax.transaction + newPackageName: jakarta.transaction + recursive: true + + # Remove Javax Transaction API(x) + - org.openrewrite.maven.RemoveDependency: + groupId: javax.transaction + artifactId: javax.transaction-api + +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.java.migrate.JavaxWebsocketToJakartaWebsocket +displayName: Migrate deprecated `javax.websocket` packages to `jakarta.websocket` +description: Java EE has been rebranded to Jakarta EE, necessitating a package relocation. +recipeList: +# - org.openrewrite.maven.AddDependency: +# groupId: jakarta.websocket +# artifactId: jakarta.websocket-api +# version: 2.x +# onlyIfUsing: javax.websocket.* + + # Upgrade the dependency to use the jakarta namespace if an older version already exists. + - org.openrewrite.maven.UpgradeDependencyVersion: + groupId: jakarta.websocket + artifactId: jakarta.websocket-api + newVersion: 2.x + + - org.openrewrite.java.ChangePackage: + oldPackageName: javax.websocket + newPackageName: jakarta.websocket + recursive: true + + # Remove Javax Transaction API(x) + - org.openrewrite.maven.RemoveDependency: + groupId: javax.websocket + artifactId: javax.websocket-api + +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.java.migrate.JavaxWsToJakartaWs +displayName: Migrate deprecated `javax.ws` packages to `jakarta.ws` +description: Java EE has been rebranded to Jakarta EE, necessitating a package relocation. +recipeList: +# - org.openrewrite.maven.AddDependency: +# groupId: jakarta.ws.rs +# artifactId: jakarta.ws.rs-api +# version: 3.x +# onlyIfUsing: javax.ws.+ + + # Upgrade the dependency to use the jakarta namespace if an older version already exists. + - org.openrewrite.maven.UpgradeDependencyVersion: + groupId: jakarta.ws.rs + artifactId: jakarta.ws.rs-api + newVersion: 3.x + + - org.openrewrite.java.ChangePackage: + oldPackageName: javax.ws + newPackageName: jakarta.ws + recursive: true + + # Remove Javax Transaction API(x) + - org.openrewrite.maven.RemoveDependency: + groupId: javax.ws.rs + artifactId: javax.ws.rs-api + +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.java.migrate.JavaxXmlBindMigrationToJakartaXmlBind +displayName: Migrate deprecated `javax.xml.bind` packages to `jakarta.xml.bind` +description: Java EE has been rebranded to Jakarta EE, necessitating a package relocation. +tags: + - jaxb + - javax + - jakarta + +recipeList: + # JaxB API(x) .. update +# - org.openrewrite.maven.AddDependency: +# groupId: jakarta.xml.bind +# artifactId: jakarta.xml.bind-api +# version: 3.x +# onlyIfUsing: javax.xml.bind.* + + # Upgrade the dependency to use the jakarta namespace if an older version already exists. + - org.openrewrite.maven.UpgradeDependencyVersion: + groupId: jakarta.xml.bind + artifactId: jakarta.xml.bind-api + newVersion: 3.x + + # JaxB Runtime(x) .. update +# - org.openrewrite.maven.AddDependency: +# groupId: org.glassfish.jaxb +# artifactId: jaxb-runtime +# version: 3.x +# scope: runtime +# onlyIfUsing: javax.xml.bind.* + + # Upgrade the dependency to use the jakarta namespace if an older version already exists. + - org.openrewrite.maven.UpgradeDependencyVersion: + groupId: org.glassfish.jaxb + artifactId: jaxb-runtime + newVersion: 3.x + + # f(x).xml.bind + - org.openrewrite.java.ChangePackage: + oldPackageName: javax.xml.bind + newPackageName: jakarta.xml.bind + recursive: true + + # Remove the legacy Javax JAXB API in favor of the the jakarta artifact. + - org.openrewrite.maven.RemoveDependency: + groupId: javax.xml.bind + artifactId: jaxb-api + + # Remove the legacy sun JAXB runtime in favor of the glassfish artifact. + - org.openrewrite.maven.RemoveDependency: + groupId: com.sun.xml.bind + artifactId: jaxb-impl + +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.java.migrate.JavaxXmlSoapToJakartaXmlSoap +displayName: Migrate deprecated `javax.soap` packages to `jakarta.soap` +description: Java EE has been rebranded to Jakarta EE, necessitating a package relocation. +recipeList: +# - org.openrewrite.maven.AddDependency: +# groupId: jakarta.xml.soap +# artifactId: jakarta.xml.soap-api +# version: 2.x +# onlyIfUsing: javax.xml.soap.* + + - org.openrewrite.maven.UpgradeDependencyVersion: + groupId: jakarta.xml.soap + artifactId: jakarta.xml.soap-api + newVersion: 2.x + + - org.openrewrite.java.ChangePackage: + oldPackageName: javax.xml.soap + newPackageName: jakarta.xml.soap + recursive: true + + # Remove Javax API + - org.openrewrite.maven.RemoveDependency: + groupId: javax.xml.soap + artifactId: javax.xml.soap-api + +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.java.migrate.JavaxXmlWsMigrationToJakartaXmlWs +displayName: Migrate deprecated `javax.xml.ws` packages to `jakarta.xml.ws` +description: Java EE has been rebranded to Jakarta EE, necessitating a package relocation. +tags: + - jaxws + - javax + - jakarta + +recipeList: + # JaxWS API(x) .. update +# - org.openrewrite.maven.AddDependency: +# groupId: jakarta.xml.ws +# artifactId: jakarta.xml.ws-api +# version: 3.x +# onlyIfUsing: javax.xml.ws.* + + # Upgrade the dependency to use the jakarta namespace if an older version already exists. + - org.openrewrite.maven.UpgradeDependencyVersion: + groupId: jakarta.xml.ws + artifactId: jakarta.xml.ws-api + newVersion: 3.x + + # JaxWS Runtime(x) .. update +# - org.openrewrite.maven.AddDependency: +# groupId: com.sun.xml.ws +# artifactId: jaxws-rt +# version: 3.x +# scope: runtime +# onlyIfUsing: javax.xml.ws.* + + # Upgrade the dependency to use the jakarta namespace if an older version already exists. + - org.openrewrite.maven.UpgradeDependencyVersion: + groupId: com.sun.xml.ws + artifactId: jaxws-rt + newVersion: 3.x + + # f(x).xml.ws + - org.openrewrite.java.ChangePackage: + oldPackageName: javax.xml.ws + newPackageName: jakarta.xml.ws + recursive: true + + # Remove Javax JaxWs API(x) + - org.openrewrite.maven.RemoveDependency: + groupId: javax.xml.ws + artifactId: jaxws-api + +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.java.migrate.JavaxPeristenceXmlToJakartaPersistenceXml +displayName: migrate persistence.xml files +recipeList: + - org.openrewrite.xml.ChangeTagAttribute: + attributeName: name + elementName: //property + oldValue: javax.persistence + newValue: jakarta.persistence + fileMatcher: "**/persistence.xml" + + - org.openrewrite.xml.ChangeTagAttribute: + attributeName: version + elementName: persistence + newValue: 3.0 + fileMatcher: "**/persistence.xml" + + - org.openrewrite.xml.ChangeTagAttribute: + attributeName: xmlns + elementName: persistence + oldValue: http://xmlns.jcp.org + newValue: https://jakarta.ee + fileMatcher: "**/persistence.xml" + + - org.openrewrite.xml.ChangeTagAttribute: + attributeName: xsi:schemaLocation + elementName: persistence + newValue: https://jakarta.ee/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd + fileMatcher: "**/persistence.xml" diff --git a/independent-projects/tools/devtools-common/src/test/resources/should_apply_recipe_test_cases.csv b/independent-projects/tools/devtools-common/src/test/resources/should_apply_recipe_test_cases.csv new file mode 100644 index 00000000000000..8b1cfc47e3635e --- /dev/null +++ b/independent-projects/tools/devtools-common/src/test/resources/should_apply_recipe_test_cases.csv @@ -0,0 +1,13 @@ +recipeFileName,currentVersion,targetVersion,expectedResult +3.0.yaml,2.0.0.Final,3.0.0.Final,true +2.9.yaml,2.0.0.Final,3.0.0.Final,true +2.7.yaml,2.0.0.Final,3.0.0.Final,true +2.0.yaml,2.0.0.Final,3.0.0.Final,false +3.1.yaml,2.7.0.Final,3.1.0.Final,true +3.0.yaml,2.7.0.Final,3.1.0.Final,true +2.9.yaml,2.7.0.Final,3.0.0.Final,true +2.7.yaml,2.7.0.Final,3.0.0.Final,false +1.1.yaml,1.0,2.0,true +2.0.yaml,1.0,1.5,false +0.5.yaml,1.0,0.8,false +1.0.yaml,1.0,1.2,false \ No newline at end of file diff --git a/integration-tests/maven/src/test/resources-filtered/projects/classic-2.x/.env b/integration-tests/maven/src/test/resources-filtered/projects/classic-2.x/.env new file mode 100644 index 00000000000000..98fb9ae1398c6c --- /dev/null +++ b/integration-tests/maven/src/test/resources-filtered/projects/classic-2.x/.env @@ -0,0 +1 @@ +OTHER_GREETING=Hola \ No newline at end of file diff --git a/integration-tests/maven/src/test/resources-filtered/projects/classic-2.x/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/classic-2.x/pom.xml new file mode 100644 index 00000000000000..775568aec7a7b2 --- /dev/null +++ b/integration-tests/maven/src/test/resources-filtered/projects/classic-2.x/pom.xml @@ -0,0 +1,92 @@ + + + 4.0.0 + org.acme + acme + 1.0-SNAPSHOT + + io.quarkus + quarkus-bom + 2.14.3.Final + 2.14.3.Final + 11 + UTF-8 + 11 + + + 1.13.0 + + + + + + ${quarkus.platform.group-id} + ${quarkus.platform.artifact-id} + ${quarkus.platform.version} + pom + import + + + + + + + io.quarkus + quarkus-resteasy + + + io.quarkus + quarkus-smallrye-context-propagation + + + io.quarkus + quarkus-websockets + + + org.webjars + jquery-ui + ${webjar.jquery-ui.version} + + + commons-io + commons-io + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + + + + io.quarkus + quarkus-maven-plugin + ${quarkus-plugin.version} + + + + generate-code + generate-code-tests + build + + + + + + + + + native + + native + + + + diff --git a/integration-tests/maven/src/test/resources-filtered/projects/classic-2.x/src/main/java/org/acme/ClasspathResources.java b/integration-tests/maven/src/test/resources-filtered/projects/classic-2.x/src/main/java/org/acme/ClasspathResources.java new file mode 100644 index 00000000000000..1928f47531c2a4 --- /dev/null +++ b/integration-tests/maven/src/test/resources-filtered/projects/classic-2.x/src/main/java/org/acme/ClasspathResources.java @@ -0,0 +1,195 @@ +package org.acme; + +import javax.ws.rs.QueryParam; +import org.apache.commons.io.IOUtils; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import java.io.InputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Paths; +import java.util.Collections; +import java.util.List; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.function.Supplier; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +@Path("/classpathResources") +public class ClasspathResources { + + private static final String SUCCESS = "success"; + + @GET + public String readClassPathResources() { + return runAssertions( + () -> assertInvalidExactFileLocation(), + () -> assertCorrectExactFileLocation(), + () -> assertInvalidDirectory(), + () -> assertCorrectDirectory(), + () -> assertTopLevelDirectory(), + () -> assertMultiRelease() + ); + } + + private String runAssertions(Supplier... assertions) { + String result; + for (Supplier assertion : assertions) { + result = assertion.get(); + if (!SUCCESS.equals(result)) { + return result; + } + } + return SUCCESS; + } + + private String assertInvalidExactFileLocation() { + final String testType = "invalid-exact-location"; + try { + Enumeration exactFileLocationEnumeration = this.getClass().getClassLoader().getResources("db/location/test2.sql"); + List exactFileLocationList = urlList(exactFileLocationEnumeration); + if (exactFileLocationList.size() != 0) { + return errorResult(testType, "wrong number of urls"); + } + return SUCCESS; + } catch (Exception e) { + e.printStackTrace(); + return errorResult(testType, "exception during resolution of resource"); + } + } + + private String assertMultiRelease() { + final String testType = "assert-multi-release-jar"; + if (System.getProperty("java.version").startsWith("1.")) { + return SUCCESS; + } + try { + //this class is only present in multi release jars + //for fast-jar we need to make sure it is loaded correctly + Class clazz = this.getClass().getClassLoader().loadClass("io.smallrye.context.Jdk9CompletableFutureWrapper"); + if (clazz.getClassLoader() == getClass().getClassLoader()) { + return SUCCESS; + } + return errorResult(testType, "Incorrect ClassLoader for " + clazz); + } catch (Exception e) { + e.printStackTrace(); + return errorResult(testType, "exception during resolution of resource"); + } + } + private String assertCorrectExactFileLocation() { + final String testType = "correct-exact-location"; + try { + Enumeration exactFileLocationEnumeration = this.getClass().getClassLoader().getResources("db/location/test.sql"); + List exactFileLocationList = urlList(exactFileLocationEnumeration); + if (exactFileLocationList.size() != 1) { + return errorResult(testType, "wrong number of urls"); + } + String fileContent = IOUtils.toString(exactFileLocationList.get(0).toURI(), StandardCharsets.UTF_8); + if (!fileContent.contains("CREATE TABLE")) { + return errorResult(testType, "wrong file content"); + } + return SUCCESS; + } catch (Exception e) { + e.printStackTrace(); + return errorResult(testType, "exception during resolution of resource"); + } + } + + private String assertInvalidDirectory() { + final String testType = "invalid-directory"; + try { + Enumeration exactFileLocationEnumeration = this.getClass().getClassLoader().getResources("db/location2"); + List exactFileLocationList = urlList(exactFileLocationEnumeration); + if (exactFileLocationList.size() != 0) { + return errorResult(testType, "wrong number of urls"); + } + return SUCCESS; + } catch (Exception e) { + e.printStackTrace(); + return errorResult(testType, "exception during resolution of resource"); + } + } + + private String assertCorrectDirectory() { + final String testType = "correct-directory"; + try { + Enumeration directoryEnumeration = this.getClass().getClassLoader().getResources("db/location"); + List directoryURLList = urlList(directoryEnumeration); + if (directoryURLList.size() != 1) { + return errorResult(testType, "wrong number of directory urls"); + } + + URL singleURL = directoryURLList.get(0); + + int separatorIndex = singleURL.getPath().lastIndexOf('!'); + String jarPath = singleURL.getPath().substring(0, separatorIndex); + String directoryName = singleURL.getPath().substring(separatorIndex + 2) + "/"; + + try (JarFile jarFile = new JarFile(Paths.get(new URI(jarPath)).toFile())) { + Enumeration entries = jarFile.entries(); + List entriesInDirectory = new ArrayList<>(); + while (entries.hasMoreElements()) { + JarEntry currentEntry = entries.nextElement(); + String entryName = currentEntry.getName(); + if (entryName.startsWith(directoryName) && !entryName.equals(directoryName)) { + entriesInDirectory.add(currentEntry); + } + } + + if (entriesInDirectory.size() != 1) { + return errorResult(testType, "wrong number of entries in jar directory"); + } + + try (InputStream is = jarFile.getInputStream(entriesInDirectory.get(0))) { + String fileContent = IOUtils.toString(is, StandardCharsets.UTF_8); + if (!fileContent.contains("CREATE TABLE")) { + return errorResult(testType, "wrong file content"); + } + return SUCCESS; + } + } + + + } catch (Exception e) { + e.printStackTrace(); + return errorResult(testType, "exception during resolution of resource"); + } + } + + private String assertTopLevelDirectory() { + final String testType = "top-level-directory"; + try { + Enumeration directoryEnumeration = this.getClass().getClassLoader().getResources("assets"); + List directoryURLList = urlList(directoryEnumeration); + if (directoryURLList.size() != 1) { + return errorResult(testType, "wrong number of directory urls"); + } + + return SUCCESS; + } catch (Exception e) { + e.printStackTrace(); + return errorResult(testType, "exception during resolution of resource"); + } + } + + private List urlList(Enumeration enumeration) { + if (enumeration == null) { + return Collections.emptyList(); + } + List result = new ArrayList<>(); + while (enumeration.hasMoreElements()) { + result.add(enumeration.nextElement()); + } + return result; + } + + private String errorResult(String testType, String message) { + return testType + " / " + message; + } +} diff --git a/integration-tests/maven/src/test/resources-filtered/projects/classic-2.x/src/main/java/org/acme/HelloResource.java b/integration-tests/maven/src/test/resources-filtered/projects/classic-2.x/src/main/java/org/acme/HelloResource.java new file mode 100644 index 00000000000000..4b26e9a74e6ec5 --- /dev/null +++ b/integration-tests/maven/src/test/resources-filtered/projects/classic-2.x/src/main/java/org/acme/HelloResource.java @@ -0,0 +1,74 @@ +package org.acme; + +import org.eclipse.microprofile.config.inject.ConfigProperty; + +import javax.inject.Inject; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +@Path("/hello") +public class HelloResource { + + @ConfigProperty(name = "greeting") + String greeting; + + @ConfigProperty(name = "quarkus.application.version") + String applicationVersion; + + @ConfigProperty(name = "quarkus.application.name") + String applicationName; + + @ConfigProperty(name = "other.greeting", defaultValue = "other") + String otherGreeting; + + @ConfigProperty(name = "quarkus.profile") + String profile; + + @GET + @Produces(MediaType.TEXT_PLAIN) + public String hello() { + return "hello"; + } + + @GET + @Path("/greeting") + @Produces(MediaType.TEXT_PLAIN) + public String greeting() { + return greeting; + } + + @GET + @Path("/package") + @Produces(MediaType.TEXT_PLAIN) + public String pkg() { + return Blah.class.getPackage().getName(); + } + + @GET + @Path("/nameAndVersion") + @Produces(MediaType.TEXT_PLAIN) + public String nameAndVersion() { + return applicationName + "/" + applicationVersion; + } + + @GET + @Path("/otherGreeting") + @Produces(MediaType.TEXT_PLAIN) + public String otherGreeting() { + return otherGreeting; + } + + @GET + @Path("/profile") + @Produces(MediaType.TEXT_PLAIN) + public String profile() { + return profile; + } + + + public static class Blah { + + } +} diff --git a/integration-tests/maven/src/test/resources-filtered/projects/classic-2.x/src/main/java/org/acme/MyApplication.java b/integration-tests/maven/src/test/resources-filtered/projects/classic-2.x/src/main/java/org/acme/MyApplication.java new file mode 100644 index 00000000000000..2b41cd0385afb8 --- /dev/null +++ b/integration-tests/maven/src/test/resources-filtered/projects/classic-2.x/src/main/java/org/acme/MyApplication.java @@ -0,0 +1,9 @@ +package org.acme; + +import javax.ws.rs.ApplicationPath; +import javax.ws.rs.core.Application; + +@ApplicationPath("/app") +public class MyApplication extends Application { + +} diff --git a/integration-tests/maven/src/test/resources-filtered/projects/classic-2.x/src/main/java/org/acme/ProtectionDomain.java b/integration-tests/maven/src/test/resources-filtered/projects/classic-2.x/src/main/java/org/acme/ProtectionDomain.java new file mode 100644 index 00000000000000..1ff398c3bc2628 --- /dev/null +++ b/integration-tests/maven/src/test/resources-filtered/projects/classic-2.x/src/main/java/org/acme/ProtectionDomain.java @@ -0,0 +1,77 @@ +package org.acme; + +import org.apache.commons.io.IOUtils; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.jar.JarInputStream; +import java.util.jar.Manifest; +import java.nio.charset.StandardCharsets; +import java.nio.file.Paths; +import java.util.Collections; +import java.util.List; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.function.Supplier; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarInputStream; +import java.util.jar.Manifest; + +@Path("/protectionDomain") +public class ProtectionDomain { + + private static final String SUCCESS = "success"; + + @GET + public String useProtectionDomain() { + return runAssertions( + () -> assertReadManifestFromJar() + ); + } + + private String runAssertions(Supplier... assertions) { + String result; + for (Supplier assertion : assertions) { + result = assertion.get(); + if (!SUCCESS.equals(result)) { + return result; + } + } + return SUCCESS; + } + + private String assertReadManifestFromJar() { + final String testType = "manifest-from-jar"; + try { + URL location = org.apache.commons.io.Charsets.class.getProtectionDomain().getCodeSource().getLocation(); + if (location == null) { + return errorResult(testType, "location should not be null"); + } + + try (InputStream inputStream = location.openStream()) { + try (JarInputStream jarInputStream = new JarInputStream(inputStream)) { + Manifest manifest = jarInputStream.getManifest(); + if (manifest == null) { + return errorResult(testType, "manifest should not be null"); + } + String implementationVersion = manifest.getMainAttributes().getValue("Implementation-Version"); + if (implementationVersion == null) { + return errorResult(testType, "implementation-version should not be null"); + } + } + } + return SUCCESS; + } catch (Exception e) { + e.printStackTrace(); + return errorResult(testType, "exception during resolution of resource"); + } + } + + private String errorResult(String testType, String message) { + return testType + " / " + message; + } +} diff --git a/integration-tests/maven/src/test/resources-filtered/projects/classic-2.x/src/main/resources/META-INF/resources/index.html b/integration-tests/maven/src/test/resources-filtered/projects/classic-2.x/src/main/resources/META-INF/resources/index.html new file mode 100644 index 00000000000000..c09bb5c96b8694 --- /dev/null +++ b/integration-tests/maven/src/test/resources-filtered/projects/classic-2.x/src/main/resources/META-INF/resources/index.html @@ -0,0 +1,156 @@ + + + + + acme - 1.0-SNAPSHOT + + + + + + +
+
+

Congratulations, you have created a new Quarkus application.

+ +

Why do you see this?

+ +

This page is served by Quarkus. The source is in + src/main/resources/META-INF/resources/index.html.

+ +

What can I do from here?

+ +

If not already done, run the application in dev mode using: mvn compile quarkus:dev. +

+
    +
  • Add REST resources, Servlets, functions and other services in src/main/java.
  • +
  • Your static assets are located in src/main/resources/META-INF/resources.
  • +
  • Configure your application in src/main/resources/application.properties. +
  • +
+ +

Do you like Quarkus?

+

Go give it a star on GitHub.

+ +

How do I get rid of this page?

+

Just delete the src/main/resources/META-INF/resources/index.html file.

+
+
+
+

Application

+
    +
  • GroupId: org.acme
  • +
  • ArtifactId: acme
  • +
  • Version: 1.0-SNAPSHOT
  • +
  • Quarkus Version: 999-SNAPSHOT
  • +
+
+
+

Next steps

+ +
+
+
+ + + + \ No newline at end of file diff --git a/integration-tests/maven/src/test/resources-filtered/projects/classic-2.x/src/main/resources/application.properties b/integration-tests/maven/src/test/resources-filtered/projects/classic-2.x/src/main/resources/application.properties new file mode 100644 index 00000000000000..9f58e0badff944 --- /dev/null +++ b/integration-tests/maven/src/test/resources-filtered/projects/classic-2.x/src/main/resources/application.properties @@ -0,0 +1,10 @@ +# Configuration file +key = value +greeting=bonjour +quarkus.live-reload.password=secret +quarkus.live-reload.url=http://localhost:8080/ +quarkus.log.level=INFO +quarkus.log.file.enable=false +quarkus.log.console.format=%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c{3.}] (%t) %s%e%n +quarkus.log.file.format=%d{yyyy-MM-dd HH:mm:ss,SSS} %h %N[%i] %-5p [%c{3.}] (%t) %s%e%n +quarkus.log.category."io.quarkus".level=INFO diff --git a/integration-tests/maven/src/test/resources-filtered/projects/classic-2.x/src/main/resources/assets/test.txt b/integration-tests/maven/src/test/resources-filtered/projects/classic-2.x/src/main/resources/assets/test.txt new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/integration-tests/maven/src/test/resources-filtered/projects/classic-2.x/src/main/resources/db/location/test.sql b/integration-tests/maven/src/test/resources-filtered/projects/classic-2.x/src/main/resources/db/location/test.sql new file mode 100644 index 00000000000000..cddc725179c67e --- /dev/null +++ b/integration-tests/maven/src/test/resources-filtered/projects/classic-2.x/src/main/resources/db/location/test.sql @@ -0,0 +1,7 @@ +CREATE TABLE TEST_SCHEMA.quarkus_table2 +( + id INT, + name VARCHAR(20) +); +INSERT INTO TEST_SCHEMA.quarkus_table2(id, name) +VALUES (1, '1.0.1 QUARKED'); diff --git a/integration-tests/maven/src/test/resources-filtered/projects/classic-2.x/src/test/java/org/acme/HelloResourceTest.java b/integration-tests/maven/src/test/resources-filtered/projects/classic-2.x/src/test/java/org/acme/HelloResourceTest.java new file mode 100644 index 00000000000000..c2f29e2c9f7114 --- /dev/null +++ b/integration-tests/maven/src/test/resources-filtered/projects/classic-2.x/src/test/java/org/acme/HelloResourceTest.java @@ -0,0 +1,21 @@ +package org.acme; + +import io.quarkus.test.junit.QuarkusTest; +import org.junit.jupiter.api.Test; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.is; + +@QuarkusTest +public class HelloResourceTest { + + @Test + public void testHelloEndpoint() { + given() + .when().get("/app/hello") + .then() + .statusCode(200) + .body(is("hello")); + } + +}