From 97f9981d7091c6355121c33b9aa333d9b81a4e12 Mon Sep 17 00:00:00 2001 From: Andy Damevin Date: Tue, 2 Jun 2020 09:11:07 +0200 Subject: [PATCH] Refactor and Introduce ExtensionsManager in CodeGen This is part of #8178 - Improved packages names and content - Improved general readability and consistency - Added ExtensionManager to define a high level way of managing (read/write) extensions in any QuarkusProject - Removed most unsafe Gradle operations which were outside of the Gradle plugin (we need to figure out a way to improve the "generic" gradle support to make it compatible again..) - Removed existing project support with the create command (throws an error) -> #9875 - Removed compatibility with project without the Quarkus platform bom defined - Remove SetupIT which mostly duplicate CreateProjectMojoIT --- .../cli/commands/AddExtensionCommand.java | 2 + .../cli/commands/CreateProjectCommand.java | 1 + .../cli/commands/ListExtensionsCommand.java | 1 + .../cli/commands/RemoveExtensionCommand.java | 2 + ...nsionToModuleInMultiModuleProjectTest.java | 1 - .../gradle/QuarkusPluginFunctionalTest.java | 4 +- .../gradle/GradleBuildFileFromConnector.java | 35 +- .../gradle/tasks/QuarkusAddExtension.java | 2 +- .../gradle/tasks/QuarkusListExtensions.java | 11 +- .../gradle/tasks/QuarkusPlatformTask.java | 18 +- .../gradle/tasks/QuarkusRemoveExtension.java | 2 +- .../quarkus/gradle/GradleBuildFileTest.java | 24 +- .../io/quarkus/maven/AddExtensionMojo.java | 6 +- .../io/quarkus/maven/CreateProjectMojo.java | 25 +- .../io/quarkus/maven/ListExtensionsMojo.java | 4 +- ...oBase.java => QuarkusProjectMojoBase.java} | 74 ++-- .../io/quarkus/maven/RemoveExtensionMojo.java | 6 +- .../bootstrap/model/AppArtifactKey.java | 4 +- .../commands/CreateProjectCommandHandler.java | 65 ---- .../ListExtensionsCommandHandler.java | 167 --------- .../java/io/quarkus/cli/commands/Printer.java | 24 -- .../quarkus/cli/commands/QuarkusCommand.java | 6 - .../RemoveExtensionsCommandHandler.java | 79 ---- .../quarkus/devtools/buildfile/BuildFile.java | 170 --------- .../devtools/buildfile/GradleBuildFile.java | 350 ------------------ .../devtools/buildfile/MavenBuildFile.java | 261 ------------- .../commands/AddExtensions.java | 18 +- .../commands/CreateProject.java | 32 +- .../commands/ListExtensions.java | 38 +- .../commands/RemoveExtensions.java | 15 +- .../data}/QuarkusCommandException.java | 2 +- .../data}/QuarkusCommandInvocation.java | 9 +- .../commands/data}/QuarkusCommandOutcome.java | 2 +- .../commands/data}/SelectionResult.java | 2 +- .../commands/data}/ValueMap.java | 14 +- .../handlers/AddExtensionsCommandHandler.java | 47 +++ .../handlers/CreateProjectCommandHandler.java | 81 ++++ .../ListExtensionsCommandHandler.java | 161 ++++++++ .../handlers/QuarkusCommandHandler.java | 10 + .../handlers/QuarkusCommandHandlers.java} | 131 +++---- .../RemoveExtensionsCommandHandler.java | 49 +++ .../quarkus/devtools/project/BuildTool.java | 19 +- .../devtools/project/QuarkusProject.java | 48 +-- .../buildfile/AbstractGradleBuildFile.java | 245 ++++++++++++ .../devtools/project/buildfile/BuildFile.java | 161 ++++++++ .../buildfile/GenericGradleBuildFile.java | 40 ++ .../buildfile/GradleBuildFilesCreator.java | 215 +++++++++++ .../project/buildfile/MavenBuildFile.java | 121 ++++++ .../project/codegen}/ProjectGenerator.java | 8 +- .../codegen}/ProjectGeneratorRegistry.java | 2 +- .../project/codegen}/SourceType.java | 2 +- .../rest/BasicRestProjectGenerator.java | 23 +- .../codegen}/writer/FileProjectWriter.java | 24 +- .../codegen}/writer/ProjectWriter.java | 6 +- .../project/extensions/ExtensionManager.java | 108 ++++++ .../project/extensions/Extensions.java | 49 +++ ....devtools.project.codegen.ProjectGenerator | 1 + .../io.quarkus.generators.ProjectGenerator | 1 - .../cli/commands/CreateProjectTest.java | 314 ---------------- .../commands/AbstractAddExtensionsTest.java | 16 +- .../AbstractRemoveExtensionsTest.java | 8 +- .../commands/AddGradleExtensionsTest.java | 43 ++- .../commands/AddMavenExtensionsTest.java | 6 +- .../devtools/commands/CreateProjectTest.java | 142 +++++++ .../commands/ListExtensionsTest.java | 58 +-- .../commands/PlatformAwareTestBase.java | 2 +- .../commands/RemoveGradleExtensionsTest.java | 11 +- .../commands/RemoveMavenExtensionsTest.java | 7 +- .../handlers/QuarkusCommandHandlersTest.java} | 25 +- .../rest/BasicRestProjectGeneratorTest.java | 37 +- .../compress/QuarkusProjectCompressTest.java | 10 +- ...CombinedQuarkusPlatformDescriptorTest.java | 2 +- .../io/quarkus/dependencies/Extension.java | 3 + .../platform/tools/ConsoleMessageFormats.java | 23 ++ .../quarkus/maven/it/CreateProjectMojoIT.java | 84 +---- .../java/io/quarkus/maven/it/SetupIT.java | 103 ------ .../resources/projects/simple-pom-it/pom.xml | 17 + 77 files changed, 1918 insertions(+), 2021 deletions(-) rename devtools/maven/src/main/java/io/quarkus/maven/{BuildFileMojoBase.java => QuarkusProjectMojoBase.java} (75%) delete mode 100644 independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/CreateProjectCommandHandler.java delete mode 100644 independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/ListExtensionsCommandHandler.java delete mode 100644 independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/Printer.java delete mode 100644 independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/QuarkusCommand.java delete mode 100644 independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/RemoveExtensionsCommandHandler.java delete mode 100644 independent-projects/tools/common/src/main/java/io/quarkus/devtools/buildfile/BuildFile.java delete mode 100644 independent-projects/tools/common/src/main/java/io/quarkus/devtools/buildfile/GradleBuildFile.java delete mode 100644 independent-projects/tools/common/src/main/java/io/quarkus/devtools/buildfile/MavenBuildFile.java rename independent-projects/tools/common/src/main/java/io/quarkus/{cli => devtools}/commands/AddExtensions.java (52%) rename independent-projects/tools/common/src/main/java/io/quarkus/{cli => devtools}/commands/CreateProject.java (81%) rename independent-projects/tools/common/src/main/java/io/quarkus/{cli => devtools}/commands/ListExtensions.java (65%) rename independent-projects/tools/common/src/main/java/io/quarkus/{cli => devtools}/commands/RemoveExtensions.java (58%) rename independent-projects/tools/common/src/main/java/io/quarkus/{cli/commands => devtools/commands/data}/QuarkusCommandException.java (87%) rename independent-projects/tools/common/src/main/java/io/quarkus/{cli/commands => devtools/commands/data}/QuarkusCommandInvocation.java (87%) rename independent-projects/tools/common/src/main/java/io/quarkus/{cli/commands => devtools/commands/data}/QuarkusCommandOutcome.java (91%) rename independent-projects/tools/common/src/main/java/io/quarkus/{cli/commands => devtools/commands/data}/SelectionResult.java (94%) rename independent-projects/tools/common/src/main/java/io/quarkus/{cli/commands => devtools/commands/data}/ValueMap.java (83%) create mode 100644 independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/AddExtensionsCommandHandler.java create mode 100644 independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/CreateProjectCommandHandler.java create mode 100644 independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/ListExtensionsCommandHandler.java create mode 100644 independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/QuarkusCommandHandler.java rename independent-projects/tools/common/src/main/java/io/quarkus/{cli/commands/AddExtensionsCommandHandler.java => devtools/commands/handlers/QuarkusCommandHandlers.java} (56%) create mode 100644 independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/RemoveExtensionsCommandHandler.java create mode 100644 independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/buildfile/AbstractGradleBuildFile.java create mode 100644 independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/buildfile/BuildFile.java create mode 100644 independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/buildfile/GenericGradleBuildFile.java create mode 100644 independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/buildfile/GradleBuildFilesCreator.java create mode 100644 independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/buildfile/MavenBuildFile.java rename independent-projects/tools/common/src/main/java/io/quarkus/{generators => devtools/project/codegen}/ProjectGenerator.java (77%) rename independent-projects/tools/common/src/main/java/io/quarkus/{generators => devtools/project/codegen}/ProjectGeneratorRegistry.java (96%) rename independent-projects/tools/common/src/main/java/io/quarkus/{generators => devtools/project/codegen}/SourceType.java (98%) rename independent-projects/tools/common/src/main/java/io/quarkus/{generators => devtools/project/codegen}/rest/BasicRestProjectGenerator.java (92%) rename independent-projects/tools/common/src/main/java/io/quarkus/devtools/{ => project/codegen}/writer/FileProjectWriter.java (74%) rename independent-projects/tools/common/src/main/java/io/quarkus/devtools/{ => project/codegen}/writer/ProjectWriter.java (84%) create mode 100644 independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/extensions/ExtensionManager.java create mode 100644 independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/extensions/Extensions.java create mode 100644 independent-projects/tools/common/src/main/resources/META-INF/services/io.quarkus.devtools.project.codegen.ProjectGenerator delete mode 100644 independent-projects/tools/common/src/main/resources/META-INF/services/io.quarkus.generators.ProjectGenerator delete mode 100644 independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/CreateProjectTest.java rename independent-projects/tools/common/src/test/java/io/quarkus/{cli => devtools}/commands/AbstractAddExtensionsTest.java (93%) rename independent-projects/tools/common/src/test/java/io/quarkus/{cli => devtools}/commands/AbstractRemoveExtensionsTest.java (93%) rename independent-projects/tools/common/src/test/java/io/quarkus/{cli => devtools}/commands/AddGradleExtensionsTest.java (53%) rename independent-projects/tools/common/src/test/java/io/quarkus/{cli => devtools}/commands/AddMavenExtensionsTest.java (87%) create mode 100644 independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/CreateProjectTest.java rename independent-projects/tools/common/src/test/java/io/quarkus/{cli => devtools}/commands/ListExtensionsTest.java (72%) rename independent-projects/tools/common/src/test/java/io/quarkus/{cli => devtools}/commands/PlatformAwareTestBase.java (98%) rename independent-projects/tools/common/src/test/java/io/quarkus/{cli => devtools}/commands/RemoveGradleExtensionsTest.java (80%) rename independent-projects/tools/common/src/test/java/io/quarkus/{cli => devtools}/commands/RemoveMavenExtensionsTest.java (88%) rename independent-projects/tools/common/src/test/java/io/quarkus/{cli/commands/AddExtensionsSelectTest.java => devtools/commands/handlers/QuarkusCommandHandlersTest.java} (86%) rename independent-projects/tools/common/src/test/java/io/quarkus/{generators => devtools/project/codegen}/rest/BasicRestProjectGeneratorTest.java (84%) create mode 100644 independent-projects/tools/platform-descriptor-api/src/main/java/io/quarkus/platform/tools/ConsoleMessageFormats.java delete mode 100644 integration-tests/maven/src/test/java/io/quarkus/maven/it/SetupIT.java diff --git a/devtools/aesh/src/main/java/io/quarkus/cli/commands/AddExtensionCommand.java b/devtools/aesh/src/main/java/io/quarkus/cli/commands/AddExtensionCommand.java index f28858c67e830..60d491fcddf4f 100644 --- a/devtools/aesh/src/main/java/io/quarkus/cli/commands/AddExtensionCommand.java +++ b/devtools/aesh/src/main/java/io/quarkus/cli/commands/AddExtensionCommand.java @@ -15,6 +15,8 @@ import org.aesh.io.Resource; import io.quarkus.dependencies.Extension; +import io.quarkus.devtools.commands.AddExtensions; +import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; import io.quarkus.devtools.project.QuarkusProject; import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; import io.quarkus.platform.tools.config.QuarkusPlatformConfig; diff --git a/devtools/aesh/src/main/java/io/quarkus/cli/commands/CreateProjectCommand.java b/devtools/aesh/src/main/java/io/quarkus/cli/commands/CreateProjectCommand.java index 68596a7b528cf..8d8f84279e97d 100644 --- a/devtools/aesh/src/main/java/io/quarkus/cli/commands/CreateProjectCommand.java +++ b/devtools/aesh/src/main/java/io/quarkus/cli/commands/CreateProjectCommand.java @@ -11,6 +11,7 @@ import org.aesh.command.option.Option; import org.aesh.io.Resource; +import io.quarkus.devtools.commands.CreateProject; import io.quarkus.platform.tools.config.QuarkusPlatformConfig; /** diff --git a/devtools/aesh/src/main/java/io/quarkus/cli/commands/ListExtensionsCommand.java b/devtools/aesh/src/main/java/io/quarkus/cli/commands/ListExtensionsCommand.java index 380c4f6d04a95..3420bdc6880ee 100644 --- a/devtools/aesh/src/main/java/io/quarkus/cli/commands/ListExtensionsCommand.java +++ b/devtools/aesh/src/main/java/io/quarkus/cli/commands/ListExtensionsCommand.java @@ -11,6 +11,7 @@ import org.aesh.command.option.Option; import org.aesh.io.Resource; +import io.quarkus.devtools.commands.ListExtensions; import io.quarkus.devtools.project.QuarkusProject; import io.quarkus.platform.tools.config.QuarkusPlatformConfig; diff --git a/devtools/aesh/src/main/java/io/quarkus/cli/commands/RemoveExtensionCommand.java b/devtools/aesh/src/main/java/io/quarkus/cli/commands/RemoveExtensionCommand.java index cc3b718276cfc..45a40b8f81a69 100644 --- a/devtools/aesh/src/main/java/io/quarkus/cli/commands/RemoveExtensionCommand.java +++ b/devtools/aesh/src/main/java/io/quarkus/cli/commands/RemoveExtensionCommand.java @@ -15,6 +15,8 @@ import org.aesh.io.Resource; import io.quarkus.dependencies.Extension; +import io.quarkus.devtools.commands.RemoveExtensions; +import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; import io.quarkus.devtools.project.QuarkusProject; import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; import io.quarkus.platform.tools.config.QuarkusPlatformConfig; diff --git a/devtools/gradle/src/functionalTest/java/io/quarkus/gradle/AddExtensionToModuleInMultiModuleProjectTest.java b/devtools/gradle/src/functionalTest/java/io/quarkus/gradle/AddExtensionToModuleInMultiModuleProjectTest.java index 00b55e2260616..c992eef547c62 100644 --- a/devtools/gradle/src/functionalTest/java/io/quarkus/gradle/AddExtensionToModuleInMultiModuleProjectTest.java +++ b/devtools/gradle/src/functionalTest/java/io/quarkus/gradle/AddExtensionToModuleInMultiModuleProjectTest.java @@ -19,7 +19,6 @@ public class AddExtensionToModuleInMultiModuleProjectTest extends QuarkusGradleT public void testBasicMultiModuleBuild() throws Exception { final File projectDir = getProjectDir("add-extension-multi-module"); - BuildResult build = GradleRunner.create() .forwardOutput() .withPluginClasspath() diff --git a/devtools/gradle/src/functionalTest/java/io/quarkus/gradle/QuarkusPluginFunctionalTest.java b/devtools/gradle/src/functionalTest/java/io/quarkus/gradle/QuarkusPluginFunctionalTest.java index 38cfcc6d75e9e..63026d1af3a88 100644 --- a/devtools/gradle/src/functionalTest/java/io/quarkus/gradle/QuarkusPluginFunctionalTest.java +++ b/devtools/gradle/src/functionalTest/java/io/quarkus/gradle/QuarkusPluginFunctionalTest.java @@ -21,9 +21,9 @@ import com.google.common.collect.ImmutableMap; -import io.quarkus.cli.commands.CreateProject; +import io.quarkus.devtools.commands.CreateProject; import io.quarkus.devtools.project.BuildTool; -import io.quarkus.generators.SourceType; +import io.quarkus.devtools.project.codegen.SourceType; import io.quarkus.platform.tools.config.QuarkusPlatformConfig; import io.quarkus.test.devmode.util.DevModeTestUtils; diff --git a/devtools/gradle/src/main/java/io/quarkus/gradle/GradleBuildFileFromConnector.java b/devtools/gradle/src/main/java/io/quarkus/gradle/GradleBuildFileFromConnector.java index 01c4102868f7b..2cb675e18cd83 100644 --- a/devtools/gradle/src/main/java/io/quarkus/gradle/GradleBuildFileFromConnector.java +++ b/devtools/gradle/src/main/java/io/quarkus/gradle/GradleBuildFileFromConnector.java @@ -1,6 +1,7 @@ package io.quarkus.gradle; import java.io.IOException; +import java.nio.file.Path; import java.util.Collections; import java.util.List; import java.util.Objects; @@ -13,16 +14,20 @@ import org.gradle.tooling.model.eclipse.EclipseExternalDependency; import org.gradle.tooling.model.eclipse.EclipseProject; -import io.quarkus.devtools.buildfile.GradleBuildFile; -import io.quarkus.devtools.writer.ProjectWriter; +import io.quarkus.devtools.project.buildfile.AbstractGradleBuildFile; +import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; -public class GradleBuildFileFromConnector extends GradleBuildFile { +public class GradleBuildFileFromConnector extends AbstractGradleBuildFile { private List dependencies = null; - public GradleBuildFileFromConnector(ProjectWriter writer) { - super(writer); - // we need to initialize here since there is no other single point of entry + public GradleBuildFileFromConnector(final Path projectFolderPath, final QuarkusPlatformDescriptor platformDescriptor) { + super(projectFolderPath, platformDescriptor); + } + + public GradleBuildFileFromConnector(Path projectFolderPath, QuarkusPlatformDescriptor platformDescriptor, + Path rootProjectPath) { + super(projectFolderPath, platformDescriptor, rootProjectPath); } @Override @@ -30,16 +35,14 @@ public List getDependencies() throws IOException { if (dependencies == null) { EclipseProject eclipseProject = null; if (getBuildContent() != null) { - if (getWriter().hasFile()) { - try { - ProjectConnection connection = GradleConnector.newConnector() - .forProjectDirectory(getWriter().getProjectFolder()) - .connect(); - eclipseProject = connection.getModel(EclipseProject.class); - } catch (BuildException e) { - // ignore this error. - e.printStackTrace(); - } + try { + ProjectConnection connection = GradleConnector.newConnector() + .forProjectDirectory(getProjectFolderPath().toFile()) + .connect(); + eclipseProject = connection.getModel(EclipseProject.class); + } catch (BuildException e) { + // ignore this error. + e.printStackTrace(); } } if (eclipseProject != null) { diff --git a/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusAddExtension.java b/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusAddExtension.java index d640818576b84..7037b775355da 100644 --- a/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusAddExtension.java +++ b/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusAddExtension.java @@ -11,7 +11,7 @@ import org.gradle.api.tasks.TaskAction; import org.gradle.api.tasks.options.Option; -import io.quarkus.cli.commands.AddExtensions; +import io.quarkus.devtools.commands.AddExtensions; public class QuarkusAddExtension extends QuarkusPlatformTask { diff --git a/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusListExtensions.java b/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusListExtensions.java index 833679f5c61dd..aad55911e3c29 100644 --- a/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusListExtensions.java +++ b/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusListExtensions.java @@ -6,10 +6,7 @@ import org.gradle.api.tasks.TaskAction; import org.gradle.api.tasks.options.Option; -import io.quarkus.cli.commands.ListExtensions; -import io.quarkus.devtools.project.QuarkusProject; -import io.quarkus.devtools.writer.FileProjectWriter; -import io.quarkus.gradle.GradleBuildFileFromConnector; +import io.quarkus.devtools.commands.ListExtensions; public class QuarkusListExtensions extends QuarkusPlatformTask { @@ -58,11 +55,7 @@ public QuarkusListExtensions() { @TaskAction public void listExtensions() { try { - final GradleBuildFileFromConnector buildFileFromConnector = new GradleBuildFileFromConnector( - new FileProjectWriter(getProject().getProjectDir())); - QuarkusProject quarkusProject = QuarkusProject.of(getProject().getProjectDir().toPath(), platformDescriptor(), - buildFileFromConnector); - new ListExtensions(quarkusProject) + new ListExtensions(getQuarkusProject()) .all(isAll()) .format(getFormat()) .search(getSearchPattern()) diff --git a/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusPlatformTask.java b/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusPlatformTask.java index 3433f111bb2ce..90fdaa9de1487 100644 --- a/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusPlatformTask.java +++ b/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusPlatformTask.java @@ -1,5 +1,6 @@ package io.quarkus.gradle.tasks; +import java.nio.file.Path; import java.util.ArrayList; import java.util.List; @@ -10,10 +11,9 @@ import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.tasks.Internal; -import io.quarkus.devtools.buildfile.GradleBuildFile; import io.quarkus.devtools.project.QuarkusProject; -import io.quarkus.devtools.writer.FileProjectWriter; -import io.quarkus.devtools.writer.ProjectWriter; +import io.quarkus.devtools.project.buildfile.BuildFile; +import io.quarkus.gradle.GradleBuildFileFromConnector; import io.quarkus.platform.descriptor.CombinedQuarkusPlatformDescriptor; import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; import io.quarkus.platform.descriptor.resolver.json.QuarkusJsonPlatformDescriptorResolver; @@ -66,12 +66,12 @@ protected QuarkusPlatformDescriptor platformDescriptor() { } @Internal - protected GradleBuildFile getGradleBuildFile() { - final ProjectWriter writer = new FileProjectWriter(getProject().getProjectDir()); - return getProject().getParent() == null - ? new GradleBuildFile(writer) - : new GradleBuildFile(writer, - new FileProjectWriter(getProject().getRootProject().getProjectDir())); + protected BuildFile getGradleBuildFile() { + final Path projectFolderPath = getProject().getProjectDir().toPath(); + final Path rootProjectPath = getProject().getParent() != null ? getProject().getRootProject().getProjectDir().toPath() + : null; + return new GradleBuildFileFromConnector(projectFolderPath, platformDescriptor(), + rootProjectPath); } @Internal diff --git a/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusRemoveExtension.java b/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusRemoveExtension.java index 482ed669a8cbc..1adf369f5c415 100644 --- a/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusRemoveExtension.java +++ b/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusRemoveExtension.java @@ -11,7 +11,7 @@ import org.gradle.api.tasks.TaskAction; import org.gradle.api.tasks.options.Option; -import io.quarkus.cli.commands.RemoveExtensions; +import io.quarkus.devtools.commands.RemoveExtensions; public class QuarkusRemoveExtension extends QuarkusPlatformTask { diff --git a/devtools/gradle/src/test/java/io/quarkus/gradle/GradleBuildFileTest.java b/devtools/gradle/src/test/java/io/quarkus/gradle/GradleBuildFileTest.java index 899513bec48f5..ea7457f191337 100644 --- a/devtools/gradle/src/test/java/io/quarkus/gradle/GradleBuildFileTest.java +++ b/devtools/gradle/src/test/java/io/quarkus/gradle/GradleBuildFileTest.java @@ -2,7 +2,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import java.io.IOException; @@ -14,13 +13,13 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; -import java.util.Map; import org.apache.maven.model.Dependency; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; -import io.quarkus.devtools.writer.FileProjectWriter; +import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; class GradleBuildFileTest { @@ -31,7 +30,8 @@ public static void beforeAll() throws URISyntaxException { URL url = GradleBuildFileTest.class.getClassLoader().getResource("gradle-project"); URI uri = url.toURI(); Path gradleProjectPath = Paths.get(uri); - buildFile = new GradleBuildFileFromConnector(new FileProjectWriter(gradleProjectPath.toFile())); + final QuarkusPlatformDescriptor descriptor = Mockito.mock(QuarkusPlatformDescriptor.class); + buildFile = new GradleBuildFileFromConnector(gradleProjectPath, descriptor); } @Test @@ -62,20 +62,4 @@ void testGetTestClosure() throws IOException { assertNull(buildFile.getProperty("test")); } - @Test - void testFindInstalled() throws IOException { - Map installed = buildFile.findInstalled(); - assertNotNull(installed); - assertThat(installed).isNotEmpty(); - - Dependency jsonb = installed.get("io.quarkus:quarkus-jsonb"); - assertThat(jsonb).extracting("version").isEqualTo("0.23.2"); - - Dependency jsonp = installed.get("io.quarkus:quarkus-jsonp"); - assertThat(jsonp).extracting("version").isEqualTo("0.23.2"); - - Dependency resteasy = installed.get("io.quarkus:quarkus-resteasy"); - assertThat(resteasy).extracting("version").isEqualTo("0.23.2"); - } - } diff --git a/devtools/maven/src/main/java/io/quarkus/maven/AddExtensionMojo.java b/devtools/maven/src/main/java/io/quarkus/maven/AddExtensionMojo.java index 115d681b095af..4442aa26a4cfb 100644 --- a/devtools/maven/src/main/java/io/quarkus/maven/AddExtensionMojo.java +++ b/devtools/maven/src/main/java/io/quarkus/maven/AddExtensionMojo.java @@ -10,8 +10,8 @@ import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; -import io.quarkus.cli.commands.AddExtensions; -import io.quarkus.cli.commands.QuarkusCommandOutcome; +import io.quarkus.devtools.commands.AddExtensions; +import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; import io.quarkus.devtools.project.QuarkusProject; import io.quarkus.platform.tools.MessageWriter; @@ -22,7 +22,7 @@ * parameters. */ @Mojo(name = "add-extension") -public class AddExtensionMojo extends BuildFileMojoBase { +public class AddExtensionMojo extends QuarkusProjectMojoBase { /** * The list of extensions to be added. diff --git a/devtools/maven/src/main/java/io/quarkus/maven/CreateProjectMojo.java b/devtools/maven/src/main/java/io/quarkus/maven/CreateProjectMojo.java index 2b5cbf79d7064..39290c256041a 100644 --- a/devtools/maven/src/main/java/io/quarkus/maven/CreateProjectMojo.java +++ b/devtools/maven/src/main/java/io/quarkus/maven/CreateProjectMojo.java @@ -45,11 +45,9 @@ import org.fusesource.jansi.Ansi; import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver; -import io.quarkus.cli.commands.AddExtensions; -import io.quarkus.cli.commands.CreateProject; +import io.quarkus.devtools.commands.CreateProject; import io.quarkus.devtools.project.BuildTool; -import io.quarkus.devtools.project.QuarkusProject; -import io.quarkus.generators.SourceType; +import io.quarkus.devtools.project.codegen.SourceType; import io.quarkus.maven.components.MavenVersionEnforcer; import io.quarkus.maven.components.Prompter; import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; @@ -161,19 +159,7 @@ public void execute() throws MojoExecutionException { File pom = new File(projectRoot, "pom.xml"); if (pom.isFile()) { - // Enforce that the GAV are not set - if (!StringUtils.isBlank(projectGroupId) || !StringUtils.isBlank(projectArtifactId) - || !StringUtils.isBlank(projectVersion)) { - throw new MojoExecutionException("Unable to generate the project, the `projectGroupId`, " + - "`projectArtifactId` and `projectVersion` parameters are not supported when applied to an " + - "existing `pom.xml` file"); - } - - // Load the GAV from the existing project - projectGroupId = project.getGroupId(); - projectArtifactId = project.getArtifactId(); - projectVersion = project.getVersion(); - + throw new MojoExecutionException("Unable to generate the project in a directory that already contains a pom.xml"); } else { askTheUserForMissingValues(); projectRoot = new File(outputDirectory, projectArtifactId); @@ -213,11 +199,6 @@ public void execute() throws MojoExecutionException { success = createProject.execute().isSuccess(); File createdDependenciesBuildFile = new File(projectRoot, buildToolEnum.getDependenciesFile()); - if (success) { - success = new AddExtensions(QuarkusProject.of(projectFolderPath, platform, buildToolEnum)) - .extensions(extensions).execute() - .isSuccess(); - } if (BuildTool.MAVEN.equals(buildToolEnum)) { createMavenWrapper(createdDependenciesBuildFile, ToolsUtils.readQuarkusProperties(platform)); } else if (BuildTool.GRADLE.equals(buildToolEnum)) { diff --git a/devtools/maven/src/main/java/io/quarkus/maven/ListExtensionsMojo.java b/devtools/maven/src/main/java/io/quarkus/maven/ListExtensionsMojo.java index 64486a685a6fa..91ae34967b52d 100644 --- a/devtools/maven/src/main/java/io/quarkus/maven/ListExtensionsMojo.java +++ b/devtools/maven/src/main/java/io/quarkus/maven/ListExtensionsMojo.java @@ -4,7 +4,7 @@ import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; -import io.quarkus.cli.commands.ListExtensions; +import io.quarkus.devtools.commands.ListExtensions; import io.quarkus.devtools.project.QuarkusProject; import io.quarkus.platform.tools.MessageWriter; @@ -15,7 +15,7 @@ * You can list all extension or just installable. Choose between 3 output formats: name, concise and full. */ @Mojo(name = "list-extensions", requiresProject = false) -public class ListExtensionsMojo extends BuildFileMojoBase { +public class ListExtensionsMojo extends QuarkusProjectMojoBase { /** * List all extensions or just the installable. diff --git a/devtools/maven/src/main/java/io/quarkus/maven/BuildFileMojoBase.java b/devtools/maven/src/main/java/io/quarkus/maven/QuarkusProjectMojoBase.java similarity index 75% rename from devtools/maven/src/main/java/io/quarkus/maven/BuildFileMojoBase.java rename to devtools/maven/src/main/java/io/quarkus/maven/QuarkusProjectMojoBase.java index 961abc3440c53..c3fdcbb89d463 100644 --- a/devtools/maven/src/main/java/io/quarkus/maven/BuildFileMojoBase.java +++ b/devtools/maven/src/main/java/io/quarkus/maven/QuarkusProjectMojoBase.java @@ -1,11 +1,13 @@ package io.quarkus.maven; -import java.io.File; import java.io.IOException; +import java.nio.file.Path; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import org.apache.maven.model.Dependency; +import org.apache.maven.model.DependencyManagement; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugins.annotations.Component; @@ -19,11 +21,8 @@ import io.quarkus.bootstrap.resolver.BootstrapAppModelResolver; import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver; -import io.quarkus.devtools.buildfile.BuildFile; -import io.quarkus.devtools.buildfile.MavenBuildFile; import io.quarkus.devtools.project.BuildTool; import io.quarkus.devtools.project.QuarkusProject; -import io.quarkus.devtools.writer.FileProjectWriter; import io.quarkus.platform.descriptor.CombinedQuarkusPlatformDescriptor; import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; import io.quarkus.platform.descriptor.resolver.json.QuarkusJsonPlatformDescriptorResolver; @@ -31,7 +30,7 @@ import io.quarkus.platform.tools.ToolsConstants; import io.quarkus.platform.tools.maven.MojoMessageWriter; -public abstract class BuildFileMojoBase extends AbstractMojo { +public abstract class QuarkusProjectMojoBase extends AbstractMojo { @Parameter(defaultValue = "${project}") protected MavenProject project; @@ -60,37 +59,31 @@ public void execute() throws MojoExecutionException { // Validate Mojo parameters validateParameters(); + final MessageWriter log = new MojoMessageWriter(getLog()); + final Path projectFolderPath = project.getBasedir().toPath(); + final BuildTool buildTool = QuarkusProject.resolveExistingProjectBuildTool(projectFolderPath); + final QuarkusPlatformDescriptor platformDescriptor = resolvePlatformDescriptor(log); + + doExecute(QuarkusProject.of(project.getBasedir().toPath(), platformDescriptor, buildTool), log); + } + + private QuarkusPlatformDescriptor resolvePlatformDescriptor(final MessageWriter log) throws MojoExecutionException { // Resolve and setup the platform descriptor - final MavenArtifactResolver mvn; try { - mvn = MavenArtifactResolver.builder().setRepositorySystem(repoSystem).setRepositorySystemSession(repoSession) + final MavenArtifactResolver mvn = MavenArtifactResolver.builder().setRepositorySystem(repoSystem) + .setRepositorySystemSession(repoSession) .setRemoteRepositories(repos).build(); - } catch (Exception e) { - throw new MojoExecutionException("Failed to initialize maven artifact resolver", e); - } - - final MessageWriter log = new MojoMessageWriter(getLog()); - - QuarkusPlatformDescriptor platformDescr = null; - BuildTool buildTool = null; - BuildFile buildFile = null; - try (FileProjectWriter fileProjectWriter = new FileProjectWriter(project.getBasedir())) { if (project.getFile() != null) { - // Maven project - final MavenBuildFile mvnBuild = new MavenBuildFile(fileProjectWriter); - buildTool = BuildTool.MAVEN; - buildFile = new MavenBuildFile(fileProjectWriter); - final List descrArtifactList = new ArrayList<>(2); - for (Dependency dep : mvnBuild.getManagedDependencies()) { + for (Dependency dep : getManagedDependencies()) { if ((dep.getScope() == null || !dep.getScope().equals("import")) && (dep.getType() == null || !dep.getType().equals("pom"))) { continue; } // We don't know which BOM is the platform one, so we are trying every BOM here - final String bomVersion = resolveValue(dep.getVersion(), buildFile); - final String bomGroupId = resolveValue(dep.getGroupId(), buildFile); - final String bomArtifactId = resolveValue(dep.getArtifactId(), buildFile); + final String bomVersion = resolveValue(dep.getVersion()); + final String bomGroupId = resolveValue(dep.getGroupId()); + final String bomArtifactId = resolveValue(dep.getArtifactId()); if (bomVersion == null || bomGroupId == null || bomArtifactId == null) { continue; } @@ -102,28 +95,19 @@ public void execute() throws MojoExecutionException { } if (!descrArtifactList.isEmpty()) { if (descrArtifactList.size() == 1) { - platformDescr = loadPlatformDescriptor(mvn, log, descrArtifactList.get(0)); + return loadPlatformDescriptor(mvn, log, descrArtifactList.get(0)); } else { final CombinedQuarkusPlatformDescriptor.Builder builder = CombinedQuarkusPlatformDescriptor.builder(); for (Artifact descrArtifact : descrArtifactList) { builder.addPlatform(loadPlatformDescriptor(mvn, log, descrArtifact)); } - platformDescr = builder.build(); + return builder.build(); } } - } else if (new File(project.getBasedir(), "build.gradle").exists() - || new File(project.getBasedir(), "build.gradle.kts").exists()) { - // Gradle project - buildTool = BuildTool.GRADLE; } - - if (platformDescr == null) { - platformDescr = CreateUtils.resolvePlatformDescriptor(bomGroupId, bomArtifactId, bomVersion, mvn, getLog()); - } - - doExecute(QuarkusProject.of(project.getBasedir().toPath(), platformDescr, buildTool), log); - } catch (IOException e) { - throw new MojoExecutionException("Failed to initialize project reading tools", e); + return CreateUtils.resolvePlatformDescriptor(bomGroupId, bomArtifactId, bomVersion, mvn, getLog()); + } catch (Exception e) { + throw new MojoExecutionException("Failed to initialize maven artifact resolver", e); } } @@ -166,10 +150,10 @@ protected void validateParameters() throws MojoExecutionException { protected abstract void doExecute(QuarkusProject quarkusProject, MessageWriter log) throws MojoExecutionException; - private String resolveValue(String expr, BuildFile buildFile) throws IOException { + private String resolveValue(String expr) throws IOException { if (expr.startsWith("${") && expr.endsWith("}")) { final String name = expr.substring(2, expr.length() - 1); - final String v = buildFile.getProperty(name); + final String v = project.getModel().getProperties().getProperty(name); if (v == null) { if (getLog().isDebugEnabled()) { getLog().debug("Failed to resolve property " + name); @@ -179,4 +163,10 @@ private String resolveValue(String expr, BuildFile buildFile) throws IOException } return expr; } + + private List getManagedDependencies() throws IOException { + final DependencyManagement managed = project.getModel().getDependencyManagement(); + return managed != null ? managed.getDependencies() + : Collections.emptyList(); + } } diff --git a/devtools/maven/src/main/java/io/quarkus/maven/RemoveExtensionMojo.java b/devtools/maven/src/main/java/io/quarkus/maven/RemoveExtensionMojo.java index 827f258f59243..165c951bc4cd6 100644 --- a/devtools/maven/src/main/java/io/quarkus/maven/RemoveExtensionMojo.java +++ b/devtools/maven/src/main/java/io/quarkus/maven/RemoveExtensionMojo.java @@ -10,8 +10,8 @@ import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; -import io.quarkus.cli.commands.QuarkusCommandOutcome; -import io.quarkus.cli.commands.RemoveExtensions; +import io.quarkus.devtools.commands.RemoveExtensions; +import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; import io.quarkus.devtools.project.QuarkusProject; import io.quarkus.platform.tools.MessageWriter; @@ -22,7 +22,7 @@ * parameters. */ @Mojo(name = "remove-extension") -public class RemoveExtensionMojo extends BuildFileMojoBase { +public class RemoveExtensionMojo extends QuarkusProjectMojoBase { /** * The list of extensions to be removed. diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/AppArtifactKey.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/AppArtifactKey.java index ad82ec0400e62..a9c1a96ef67b8 100644 --- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/AppArtifactKey.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/model/AppArtifactKey.java @@ -73,12 +73,12 @@ protected static String[] split(String str, String[] parts, int fromIndex) { protected AppArtifactKey(String[] parts) { this.groupId = parts[0]; this.artifactId = parts[1]; - if (parts.length == 2) { + if (parts.length == 2 || parts[2] == null) { this.classifier = ""; } else { this.classifier = parts[2]; } - if (parts.length <= 3) { + if (parts.length <= 3 || parts[3] == null) { this.type = "jar"; } else { this.type = parts[3]; diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/CreateProjectCommandHandler.java b/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/CreateProjectCommandHandler.java deleted file mode 100644 index 3bd04bea6cd84..0000000000000 --- a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/CreateProjectCommandHandler.java +++ /dev/null @@ -1,65 +0,0 @@ -package io.quarkus.cli.commands; - -import static io.quarkus.generators.ProjectGenerator.*; - -import io.quarkus.devtools.buildfile.BuildFile; -import io.quarkus.devtools.writer.FileProjectWriter; -import io.quarkus.devtools.writer.ProjectWriter; -import io.quarkus.generators.ProjectGeneratorRegistry; -import io.quarkus.generators.SourceType; -import io.quarkus.generators.rest.BasicRestProjectGenerator; -import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; -import io.quarkus.platform.tools.ToolsUtils; -import java.io.IOException; -import java.util.Properties; - -/** - * Instances of this class are thread-safe. They create a new project extracting all the necessary properties from an instance - * of {@link QuarkusCommandInvocation}. - */ -public class CreateProjectCommandHandler implements QuarkusCommand { - - @Override - public QuarkusCommandOutcome execute(QuarkusCommandInvocation invocation) throws QuarkusCommandException { - final ProjectWriter projectWriter = new FileProjectWriter( - invocation.getQuarkusProject().getProjectFolderPath().toFile()); - if (!projectWriter.init()) { - return QuarkusCommandOutcome.failure(); - } - - final QuarkusPlatformDescriptor platformDescr = invocation.getPlatformDescriptor(); - invocation.setValue(BOM_GROUP_ID, platformDescr.getBomGroupId()); - invocation.setValue(BOM_ARTIFACT_ID, platformDescr.getBomArtifactId()); - invocation.setValue(QUARKUS_VERSION, platformDescr.getQuarkusVersion()); - invocation.setValue(BOM_VERSION, platformDescr.getBomVersion()); - - final Properties quarkusProps = ToolsUtils.readQuarkusProperties(platformDescr); - quarkusProps.forEach((k, v) -> invocation.setValue(k.toString().replace("-", "_"), v.toString())); - - try (BuildFile buildFile = invocation.getBuildFile()) { - String className = invocation.getStringValue(CLASS_NAME); - if (className != null) { - className = invocation.getValue(SOURCE_TYPE, SourceType.JAVA).stripExtensionFrom(className); - int idx = className.lastIndexOf('.'); - if (idx >= 0) { - String pkgName = invocation.getStringValue(PACKAGE_NAME); - if (pkgName == null) { - invocation.setValue(PACKAGE_NAME, className.substring(0, idx)); - } - className = className.substring(idx + 1); - } - invocation.setValue(CLASS_NAME, className); - } - ProjectGeneratorRegistry.get(BasicRestProjectGenerator.NAME).generate(projectWriter, invocation); - - // call close at the end to save file - buildFile.completeFile(invocation.getStringValue(PROJECT_GROUP_ID), - invocation.getStringValue(PROJECT_ARTIFACT_ID), - invocation.getStringValue(PROJECT_VERSION), - platformDescr, quarkusProps); - } catch (IOException e) { - throw new QuarkusCommandException("Failed to create project", e); - } - return QuarkusCommandOutcome.success(); - } -} diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/ListExtensionsCommandHandler.java b/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/ListExtensionsCommandHandler.java deleted file mode 100644 index 4f5f1fe82ea24..0000000000000 --- a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/ListExtensionsCommandHandler.java +++ /dev/null @@ -1,167 +0,0 @@ -package io.quarkus.cli.commands; - -import io.quarkus.dependencies.Extension; -import io.quarkus.devtools.buildfile.BuildFile; -import io.quarkus.devtools.buildfile.GradleBuildFile; -import java.io.IOException; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.function.Consumer; -import java.util.regex.Pattern; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import org.apache.maven.model.Dependency; - -/** - * Instances of this class are thread-safe. It lists extensions according to the options passed in as properties of - * {@link QuarkusCommandInvocation} - */ -public class ListExtensionsCommandHandler implements QuarkusCommand { - - private static final String FULL_FORMAT = "%-8s %-50s %-50s %-25s%n%s"; - private static final String CONCISE_FORMAT = "%-50s %-50s"; - private static final String NAME_FORMAT = "%-50s"; - - @Override - public QuarkusCommandOutcome execute(QuarkusCommandInvocation invocation) throws QuarkusCommandException { - - final boolean all = invocation.getValue(ListExtensions.ALL, true); - final String format = invocation.getValue(ListExtensions.FORMAT, "concise"); - final String search = invocation.getValue(ListExtensions.SEARCH, "*"); - - Map installed; - try { - installed = findInstalled(invocation); - } catch (IOException e) { - throw new QuarkusCommandException("Failed to determine the list of installed extensions", e); - } - - Stream extensionsStream = invocation.getPlatformDescriptor().getExtensions().stream(); - extensionsStream = extensionsStream.filter(e -> filterUnlisted(e)); - if (search != null && !"*".equalsIgnoreCase(search)) { - final Pattern searchPattern = Pattern.compile(".*" + search + ".*", Pattern.CASE_INSENSITIVE); - extensionsStream = extensionsStream.filter(e -> filterBySearch(searchPattern, e)); - } - List loadedExtensions = extensionsStream.collect(Collectors.toList()); - - if (loadedExtensions.isEmpty()) { - System.out.println("No extension found with this pattern"); - } else { - String extensionStatus = all ? "available" : "installable"; - System.out.println(String.format("%nCurrent Quarkus extensions %s: ", extensionStatus)); - - Consumer currentFormatter; - switch (format.toLowerCase()) { - case "name": - currentFormatter = this::nameFormatter; - break; - case "full": - currentFormatter = this::fullFormatter; - currentFormatter.accept(new String[] { "Status", "Extension", "ArtifactId", "Updated Version", "Guide" }); - break; - case "concise": - default: - currentFormatter = this::conciseFormatter; - } - - final BuildFile buildFile = invocation.getBuildFile(); - loadedExtensions.forEach(extension -> display(extension, installed, all, currentFormatter, buildFile)); - - if (buildFile != null) { - if ("concise".equalsIgnoreCase(format)) { - if (buildFile instanceof GradleBuildFile) { - System.out.println("\nTo get more information, append --format=full to your command line."); - } else { - System.out.println( - "\nTo get more information, append -Dquarkus.extension.format=full to your command line."); - } - } - - if (buildFile instanceof GradleBuildFile) { - System.out.println("\nAdd an extension to your project by adding the dependency to your " + - "build.gradle or use `./gradlew addExtension --extensions=\"artifactId\"`"); - } else { - System.out.println("\nAdd an extension to your project by adding the dependency to your " + - "pom.xml or use `./mvnw quarkus:add-extension -Dextensions=\"artifactId\"`"); - } - } - } - - return QuarkusCommandOutcome.success(); - } - - Map findInstalled(QuarkusCommandInvocation invocation) throws IOException { - return invocation.getBuildFile().findInstalled(); - } - - private boolean filterUnlisted(Extension e) { - return !e.getMetadata().containsKey("unlisted"); - } - - private boolean filterBySearch(final Pattern searchPattern, Extension e) { - return searchPattern.matcher(e.getName()).matches(); - } - - private void conciseFormatter(String[] cols) { - System.out.println(String.format(CONCISE_FORMAT, cols[1], cols[2], cols[4])); - } - - private void fullFormatter(String[] cols) { - System.out.println(String.format(FULL_FORMAT, cols[0], cols[1], cols[2], cols[3], cols[4])); - } - - private void nameFormatter(String[] cols) { - System.out.println(String.format(NAME_FORMAT, cols[2])); - } - - private void display(Extension extension, final Map installed, boolean all, - Consumer formatter, BuildFile buildFile) { - final Dependency dependency = installed.get(extension.getGroupId() + ":" + extension.getArtifactId()); - if (!all && dependency != null) { - return; - } - - String label = ""; - String version = ""; - - final String extracted = extractVersion(dependency, buildFile); - if (extracted != null) { - if (extracted.equalsIgnoreCase(extension.getVersion())) { - label = "current"; - version = String.format("%s", extracted); - } else { - label = "update"; - version = String.format("%s <> %s", extracted, extension.getVersion()); - } - } - - String[] result = new String[] { label, extension.getName(), extension.getArtifactId(), version, extension.getGuide() }; - - for (int i = 0; i < result.length; i++) { - result[i] = Objects.toString(result[i], ""); - } - - formatter.accept(result); - } - - private String extractVersion(final Dependency dependency, BuildFile buildFile) { - String version = dependency != null ? dependency.getVersion() : null; - if (version != null && version.startsWith("$") && buildFile != null) { - String value = null; - try { - value = (String) buildFile.getProperty(propertyName(version)); - } catch (IOException e) { - // ignore this error. - } - if (value != null) { - version = value; - } - } - return version; - } - - private String propertyName(final String variable) { - return variable.substring(2, variable.length() - 1); - } -} diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/Printer.java b/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/Printer.java deleted file mode 100644 index 1b74c43ca230b..0000000000000 --- a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/Printer.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.quarkus.cli.commands; - -public class Printer { - private static final String OK = "\u2705"; - static final String NOK = "\u274c"; - private static final String NOOP = "\uD83D\uDC4D"; - - public void nok(String content) { - print(NOK + content); - } - - public void ok(String content) { - print(OK + content); - } - - public void noop(String content) { - print(NOOP + content); - } - - void print(String message) { - System.out.println(message); - } - -} diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/QuarkusCommand.java b/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/QuarkusCommand.java deleted file mode 100644 index 31025343198fd..0000000000000 --- a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/QuarkusCommand.java +++ /dev/null @@ -1,6 +0,0 @@ -package io.quarkus.cli.commands; - -public interface QuarkusCommand { - - QuarkusCommandOutcome execute(QuarkusCommandInvocation invocation) throws QuarkusCommandException; -} diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/RemoveExtensionsCommandHandler.java b/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/RemoveExtensionsCommandHandler.java deleted file mode 100644 index 24aea2fa73a71..0000000000000 --- a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/RemoveExtensionsCommandHandler.java +++ /dev/null @@ -1,79 +0,0 @@ -package io.quarkus.cli.commands; - -import io.quarkus.dependencies.Extension; -import io.quarkus.devtools.buildfile.BuildFile; -import java.io.IOException; -import java.util.Collections; -import java.util.List; -import java.util.Set; - -/** - * This class is thread-safe. It extracts extensions to be removed from the project from an instance of - * {@link QuarkusCommandInvocation}. - */ -public class RemoveExtensionsCommandHandler implements QuarkusCommand { - - final static Printer PRINTER = new Printer(); - - @Override - public QuarkusCommandOutcome execute(QuarkusCommandInvocation invocation) throws QuarkusCommandException { - final Set extensions = invocation.getValue(RemoveExtensions.EXTENSIONS, Collections.emptySet()); - if (extensions.isEmpty()) { - return QuarkusCommandOutcome.success().setValue(RemoveExtensions.OUTCOME_UPDATED, false); - } - - boolean updated = false; - boolean success = true; - - final List registry = invocation.getPlatformDescriptor().getExtensions(); - final BuildFile buildFile = invocation.getBuildFile(); - - try { - for (String query : extensions) { - - if (query.contains(":")) { - // GAV case. - updated = buildFile.removeExtensionAsGAV(query) || updated; - } else { - SelectionResult result = AddExtensionsCommandHandler.select(query, registry, false); - if (!result.matches()) { - StringBuilder sb = new StringBuilder(); - // We have 3 cases, we can still have a single candidate, but the match is on label - // or we have several candidates, or none - Set candidates = result.getExtensions(); - if (candidates.isEmpty()) { - // No matches at all. - PRINTER.nok(" Cannot find a dependency matching '" + query + "', maybe a typo?"); - success = false; - } else { - sb.append(Printer.NOK).append(" Multiple extensions matching '").append(query).append("'"); - result.getExtensions() - .forEach(extension -> sb.append(System.lineSeparator()).append(" * ") - .append(extension.managementKey())); - sb.append(System.lineSeparator()) - .append(" Be more specific e.g using the exact name"); - PRINTER.print(sb.toString()); - success = false; - } - } else { // Matches. - for (Extension extension : result) { - updated = buildFile.removeDependency(invocation.getPlatformDescriptor(), extension) || updated; - } - } - } - } - } catch (IOException e) { - throw new QuarkusCommandException("Failed to add extensions", e); - } - - if (updated) { - try { - buildFile.close(); - } catch (IOException e) { - throw new QuarkusCommandException("Failed to update the project", e); - } - } - - return new QuarkusCommandOutcome(success).setValue(RemoveExtensions.OUTCOME_UPDATED, updated); - } -} diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/buildfile/BuildFile.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/buildfile/BuildFile.java deleted file mode 100644 index 725e0a2e5e8d1..0000000000000 --- a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/buildfile/BuildFile.java +++ /dev/null @@ -1,170 +0,0 @@ -package io.quarkus.devtools.buildfile; - -import static com.google.common.base.Preconditions.checkNotNull; -import static io.quarkus.maven.utilities.MojoUtils.credentials; -import static java.util.stream.Collectors.toList; - -import io.quarkus.cli.commands.Printer; -import io.quarkus.dependencies.Extension; -import io.quarkus.devtools.project.BuildTool; -import io.quarkus.devtools.writer.ProjectWriter; -import io.quarkus.maven.utilities.MojoUtils; -import io.quarkus.maven.utilities.QuarkusDependencyPredicate; -import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; -import java.io.Closeable; -import java.io.IOException; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.TreeMap; -import org.apache.maven.model.Dependency; - -public abstract class BuildFile implements Closeable { - - protected final static Printer PRINTER = new Printer(); - - private final ProjectWriter writer; - - private final BuildTool buildTool; - - public BuildFile(final ProjectWriter writer, BuildTool buildTool) { - this.writer = checkNotNull(writer, "writer is required"); - this.buildTool = checkNotNull(buildTool, "buildTool is required"); - } - - protected void write(String fileName, String content) throws IOException { - writer.write(fileName, content); - } - - public boolean addDependency(QuarkusPlatformDescriptor platform, Extension extension) throws IOException { - if (!hasDependency(extension)) { - PRINTER.ok(" Adding extension " + extension.managementKey()); - Dependency dep; - if (containsBOM(platform.getBomGroupId(), platform.getBomArtifactId()) - && isDefinedInBom(platform.getManagedDependencies(), extension)) { - dep = extension.toDependency(true); - } else { - dep = extension.toDependency(false); - if (getProperty(MojoUtils.TEMPLATE_PROPERTY_QUARKUS_VERSION_NAME) != null) { - dep.setVersion(MojoUtils.TEMPLATE_PROPERTY_QUARKUS_VERSION_VALUE); - } - } - addDependencyInBuildFile(dep); - return true; - } else { - PRINTER.noop(" Skipping already present extension " + extension.managementKey()); - return false; - } - } - - public boolean removeDependency(QuarkusPlatformDescriptor platform, Extension extension) throws IOException { - if (hasDependency(extension)) { - PRINTER.ok(" Removing extension " + extension.managementKey()); - Dependency dep; - if (containsBOM(platform.getBomGroupId(), platform.getBomArtifactId()) - && isDefinedInBom(platform.getManagedDependencies(), extension)) { - dep = extension.toDependency(true); - } else { - dep = extension.toDependency(false); - if (getProperty(MojoUtils.TEMPLATE_PROPERTY_QUARKUS_VERSION_NAME) != null) { - dep.setVersion(MojoUtils.TEMPLATE_PROPERTY_QUARKUS_VERSION_VALUE); - } - } - removeDependencyFromBuildFile(dep); - return true; - } else { - PRINTER.noop(" Skipping extension that does not exists " + extension.managementKey()); - return false; - } - } - - protected abstract void addDependencyInBuildFile(Dependency dependency) throws IOException; - - protected abstract void removeDependencyFromBuildFile(Dependency dependency) throws IOException; - - protected abstract boolean hasDependency(Extension extension) throws IOException; - - public boolean addExtensionAsGAV(String query) throws IOException { - Dependency parsed = MojoUtils.parse(query.trim()); - boolean alreadyThere = getDependencies().stream() - .anyMatch(d -> d.getManagementKey().equalsIgnoreCase(parsed.getManagementKey())); - if (!alreadyThere) { - PRINTER.ok(" Adding dependency " + parsed.getManagementKey()); - addDependencyInBuildFile(parsed); - return true; - } else { - PRINTER.noop(" Skipping already present dependency " + parsed.getManagementKey()); - return false; - } - } - - public boolean removeExtensionAsGAV(String query) throws IOException { - Dependency parsed = MojoUtils.parse(query.trim()); - PRINTER.ok(" Removing dependency " + parsed.getManagementKey()); - removeDependencyFromBuildFile(parsed); - return true; - } - - protected boolean isDefinedInBom(List dependencies, Extension extension) { - return dependencies.stream().anyMatch(dependency -> dependency.getGroupId().equalsIgnoreCase(extension.getGroupId()) - && dependency.getArtifactId().equalsIgnoreCase(extension.getArtifactId())); - } - - protected abstract boolean containsBOM(String groupId, String artifactId) throws IOException; - - public abstract List getDependencies() throws IOException; - - public Map findInstalled() throws IOException { - return mapDependencies(getDependencies(), loadManaged()); - } - - private Map loadManaged() throws IOException { - final List managedDependencies = getManagedDependencies(); - return managedDependencies.isEmpty() ? Collections.emptyMap() - : mapDependencies(managedDependencies, Collections.emptyMap()); - } - - protected Map mapDependencies(final List dependencies, - final Map managed) { - final Map map = new TreeMap<>(); - - if (dependencies != null) { - final List listed = dependencies.stream() - // THIS ASSUMES EXTENSIONS' groupId is always 'io.quarkus' which is wrong - .filter(new QuarkusDependencyPredicate()) - .collect(toList()); - - listed.forEach(d -> { - if (d.getVersion() == null) { - final Dependency managedDep = managed.get(credentials(d)); - if (managedDep != null) { - final String version = managedDep.getVersion(); - if (version != null) { - d.setVersion(version); - } - } - } - - map.put(credentials(d), d); - }); - } - return map; - } - - public abstract String getProperty(String propertyName) throws IOException; - - protected abstract List getManagedDependencies() throws IOException; - - public abstract void completeFile(String groupId, String artifactId, String version, QuarkusPlatformDescriptor platform, - Properties props) throws IOException; - - public BuildTool getBuildTool() { - return buildTool; - } - - protected ProjectWriter getWriter() { - return writer; - } - -} diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/buildfile/GradleBuildFile.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/buildfile/GradleBuildFile.java deleted file mode 100644 index e148e30bfe82e..0000000000000 --- a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/buildfile/GradleBuildFile.java +++ /dev/null @@ -1,350 +0,0 @@ -package io.quarkus.devtools.buildfile; - -import io.quarkus.dependencies.Extension; -import io.quarkus.devtools.project.BuildTool; -import io.quarkus.devtools.writer.ProjectWriter; -import io.quarkus.maven.utilities.MojoUtils; -import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; -import io.quarkus.platform.tools.ToolsUtils; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.Collections; -import java.util.List; -import java.util.Properties; -import java.util.Scanner; -import java.util.function.Consumer; -import org.apache.maven.model.Dependency; - -public class GradleBuildFile extends BuildFile { - - private static final String BUILD_GRADLE_PATH = "build.gradle"; - private static final String SETTINGS_GRADLE_PATH = "settings.gradle"; - private static final String GRADLE_PROPERTIES_PATH = "gradle.properties"; - - private final ProjectWriter rootWriter; - - private Model model; - - public GradleBuildFile(ProjectWriter writer) { - super(writer, BuildTool.GRADLE); - rootWriter = writer; - } - - public GradleBuildFile(ProjectWriter writer, ProjectWriter rootWriter) { - super(writer, BuildTool.GRADLE); - this.rootWriter = rootWriter; - } - - protected void rootWrite(String fileName, String content) throws IOException { - rootWriter.write(fileName, content); - } - - protected ProjectWriter getRootWriter() { - return rootWriter; - } - - @Override - public void close() throws IOException { - if (getWriter() == getRootWriter()) { - write(SETTINGS_GRADLE_PATH, getModel().getSettingsContent()); - } else { - rootWrite(SETTINGS_GRADLE_PATH, getModel().getSettingsContent()); - } - write(BUILD_GRADLE_PATH, getModel().getBuildContent()); - try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { - getModel().getPropertiesContent().store(out, "Gradle properties"); - if (getModel().isRootProperties()) { - rootWrite(GRADLE_PROPERTIES_PATH, out.toString(StandardCharsets.UTF_8.toString())); - } else { - write(GRADLE_PROPERTIES_PATH, out.toString(StandardCharsets.UTF_8.toString())); - } - } - } - - @Override - public void completeFile(String groupId, String artifactId, String version, QuarkusPlatformDescriptor platform, - Properties props) throws IOException { - completeSettingsContent(artifactId); - completeBuildContent(groupId, version, platform, props); - completeProperties(platform); - } - - private void completeBuildContent(String groupId, String version, QuarkusPlatformDescriptor platform, Properties props) - throws IOException { - final String buildContent = getModel().getBuildContent(); - StringBuilder res = new StringBuilder(buildContent); - if (!buildContent.contains("id 'io.quarkus'")) { - res.append("plugins {"); - res.append(System.lineSeparator()).append(" id 'java'").append(System.lineSeparator()); - res.append(System.lineSeparator()).append(" id 'io.quarkus'").append(System.lineSeparator()); - res.append("}"); - } - if (!containsBOM(platform.getBomGroupId(), platform.getBomArtifactId())) { - res.append(System.lineSeparator()); - res.append("dependencies {").append(System.lineSeparator()); - res.append( - " implementation enforcedPlatform(\"${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}\")") - .append(System.lineSeparator()); - res.append(" implementation 'io.quarkus:quarkus-resteasy'").append(System.lineSeparator()); - res.append(" testImplementation 'io.quarkus:quarkus-junit5'").append(System.lineSeparator()); - res.append(" testImplementation 'io.rest-assured:rest-assured'").append(System.lineSeparator()); - res.append("}").append(System.lineSeparator()); - - } - String groupLine = "group '" + groupId + "'"; - if (!buildContent.contains(groupLine)) { - res.append(System.lineSeparator()).append(groupLine) - .append(System.lineSeparator()); - } - String versionLine = "version '" + version + "'"; - if (!buildContent.contains(versionLine)) { - res.append(System.lineSeparator()).append(versionLine) - .append(System.lineSeparator()); - } - - res.append(System.lineSeparator()) - .append("test {").append(System.lineSeparator()) - .append(" systemProperty \"java.util.logging.manager\", \"org.jboss.logmanager.LogManager\"") - .append(System.lineSeparator()) - .append("}"); - - getModel().setBuildContent(res.toString()); - } - - private void completeSettingsContent(String artifactId) throws IOException { - final String settingsContent = getModel().getSettingsContent(); - final StringBuilder res = new StringBuilder(); - if (!settingsContent.contains("id 'io.quarkus'")) { - res.append(System.lineSeparator()); - res.append("pluginManagement {").append(System.lineSeparator()); - res.append(" repositories {").append(System.lineSeparator()); - res.append(" mavenLocal()").append(System.lineSeparator()); - res.append(" mavenCentral()").append(System.lineSeparator()); - res.append(" gradlePluginPortal()").append(System.lineSeparator()); - res.append(" }").append(System.lineSeparator()); - res.append(" plugins {").append(System.lineSeparator()); - res.append(" id 'io.quarkus' version \"${quarkusPluginVersion}\"").append(System.lineSeparator()); - res.append(" }").append(System.lineSeparator()); - res.append("}").append(System.lineSeparator()); - } - if (!settingsContent.contains("rootProject.name")) { - res.append(System.lineSeparator()).append("rootProject.name='").append(artifactId).append("'") - .append(System.lineSeparator()); - } - res.append(settingsContent); - getModel().setSettingsContent(res.toString()); - } - - private void completeProperties(QuarkusPlatformDescriptor platform) throws IOException { - Properties props = getModel().getPropertiesContent(); - if (props.getProperty("quarkusPluginVersion") == null) { - props.setProperty("quarkusPluginVersion", ToolsUtils.getPluginVersion(ToolsUtils.readQuarkusProperties(platform))); - } - if (props.getProperty("quarkusPlatformGroupId") == null) { - props.setProperty("quarkusPlatformGroupId", platform.getBomGroupId()); - } - if (props.getProperty("quarkusPlatformArtifactId") == null) { - props.setProperty("quarkusPlatformArtifactId", platform.getBomArtifactId()); - } - if (props.getProperty("quarkusPlatformVersion") == null) { - props.setProperty("quarkusPlatformVersion", platform.getBomVersion()); - } - } - - @Override - protected void addDependencyInBuildFile(Dependency dependency) throws IOException { - StringBuilder newBuildContent = new StringBuilder(); - readLineByLine(getModel().getBuildContent(), new AppendDependency(newBuildContent, dependency)); - getModel().setBuildContent(newBuildContent.toString()); - } - - @Override - public boolean removeDependency(QuarkusPlatformDescriptor platform, Extension extension) throws IOException { - PRINTER.ok(" Removing extension " + extension.managementKey()); - Dependency dep; - if (containsBOM(platform.getBomGroupId(), platform.getBomArtifactId()) - && isDefinedInBom(platform.getManagedDependencies(), extension)) { - dep = extension.toDependency(true); - } else { - dep = extension.toDependency(false); - if (getProperty(MojoUtils.TEMPLATE_PROPERTY_QUARKUS_VERSION_NAME) != null) { - dep.setVersion(MojoUtils.TEMPLATE_PROPERTY_QUARKUS_VERSION_VALUE); - } - } - removeDependencyFromBuildFile(dep); - return true; - } - - @Override - protected void removeDependencyFromBuildFile(Dependency dependency) throws IOException { - String depString = new StringBuilder("'").append(dependency.getGroupId()).append(":") - .append(dependency.getArtifactId()).toString(); - StringBuilder newBuildContent = new StringBuilder(); - Scanner scanner = new Scanner(getModel().getBuildContent()); - while (scanner.hasNextLine()) { - String line = scanner.nextLine(); - if (!line.contains(depString)) { - newBuildContent.append(line).append(System.lineSeparator()); - } - } - scanner.close(); - getModel().setBuildContent(newBuildContent.toString()); - } - - private void readLineByLine(String content, Consumer lineConsumer) { - try (Scanner scanner = new Scanner(new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)), - StandardCharsets.UTF_8.name())) { - while (scanner.hasNextLine()) { - String currentLine = scanner.nextLine(); - lineConsumer.accept(currentLine); - } - } - } - - private static class AppendDependency implements Consumer { - - private StringBuilder newContent; - private Dependency dependency; - - public AppendDependency(StringBuilder newContent, Dependency dependency) { - this.newContent = newContent; - this.dependency = dependency; - } - - @Override - public void accept(String currentLine) { - newContent.append(currentLine).append(System.lineSeparator()); - if (currentLine.startsWith("dependencies {")) { - newContent.append(" implementation '") - .append(dependency.getGroupId()) - .append(":") - .append(dependency.getArtifactId()); - if (dependency.getVersion() != null && !dependency.getVersion().isEmpty()) { - newContent.append(":") - .append(dependency.getVersion()); - } - newContent.append("'") - .append(System.lineSeparator()); - } - } - - } - - @Override - protected boolean hasDependency(Extension extension) throws IOException { - return getDependencies().stream() - .anyMatch(d -> extension.getGroupId().equals(d.getGroupId()) - && extension.getArtifactId().equals(d.getArtifactId())); - } - - @Override - protected boolean containsBOM(String groupId, String artifactId) throws IOException { - String buildContent = getModel().getBuildContent(); - return buildContent.contains("enforcedPlatform(\"${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:") - || buildContent.contains("enforcedPlatform(\"" + groupId + ":" + artifactId + ":"); - } - - @Override - public List getDependencies() throws IOException { - return Collections.emptyList(); - } - - @Override - public String getProperty(String propertyName) throws IOException { - return getModel().getPropertiesContent().getProperty(propertyName); - } - - @Override - protected List getManagedDependencies() { - // Gradle tooling API only provide resolved dependencies. - return Collections.emptyList(); - } - - private Model getModel() throws IOException { - if (model == null) { - initModel(); - } - return model; - } - - protected void initModel() throws IOException { - String settingsContent = ""; - String buildContent = ""; - Properties propertiesContent = new Properties(); - boolean isRootProperties = false; - if (getWriter() == getRootWriter()) { - if (getWriter().exists(SETTINGS_GRADLE_PATH)) { - final byte[] settings = getWriter().getContent(SETTINGS_GRADLE_PATH); - settingsContent = new String(settings, StandardCharsets.UTF_8); - } - } else { - if (getRootWriter().exists(SETTINGS_GRADLE_PATH)) { - final byte[] settings = getRootWriter().getContent(SETTINGS_GRADLE_PATH); - settingsContent = new String(settings, StandardCharsets.UTF_8); - } - } - if (getWriter().exists(BUILD_GRADLE_PATH)) { - final byte[] build = getWriter().getContent(BUILD_GRADLE_PATH); - buildContent = new String(build, StandardCharsets.UTF_8); - } - if (getWriter().exists(GRADLE_PROPERTIES_PATH)) { - final byte[] properties = getWriter().getContent(GRADLE_PROPERTIES_PATH); - propertiesContent.load(new ByteArrayInputStream(properties)); - } - if (!propertiesContent.containsKey("quarkusPluginVersion") && - !propertiesContent.containsKey("quarkusPlatformGroupId") && - !propertiesContent.containsKey("quarkusPlatformArtifactId") && - !propertiesContent.containsKey("quarkusPlatformVersion")) { - if (getRootWriter().exists(GRADLE_PROPERTIES_PATH)) { - final byte[] properties = getRootWriter().getContent(GRADLE_PROPERTIES_PATH); - propertiesContent.load(new ByteArrayInputStream(properties)); - } - isRootProperties = true; - } - this.model = new Model(settingsContent, buildContent, propertiesContent, isRootProperties); - } - - protected String getBuildContent() throws IOException { - return getModel().getBuildContent(); - } - - private static class Model { - private String settingsContent; - private String buildContent; - private Properties propertiesContent; - private boolean rootProperties; - - public Model(String settingsContent, String buildContent, Properties propertiesContent, boolean rootProperties) { - this.settingsContent = settingsContent; - this.buildContent = buildContent; - this.propertiesContent = propertiesContent; - this.rootProperties = rootProperties; - } - - public String getSettingsContent() { - return settingsContent; - } - - public String getBuildContent() { - return buildContent; - } - - public Properties getPropertiesContent() { - return propertiesContent; - } - - public void setSettingsContent(String settingsContent) { - this.settingsContent = settingsContent; - } - - public void setBuildContent(String buildContent) { - this.buildContent = buildContent; - } - - public boolean isRootProperties() { - return rootProperties; - } - } -} diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/buildfile/MavenBuildFile.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/buildfile/MavenBuildFile.java deleted file mode 100644 index df55a72c8e6bd..0000000000000 --- a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/buildfile/MavenBuildFile.java +++ /dev/null @@ -1,261 +0,0 @@ -package io.quarkus.devtools.buildfile; - -import static io.quarkus.maven.utilities.MojoUtils.configuration; -import static io.quarkus.maven.utilities.MojoUtils.plugin; - -import io.quarkus.dependencies.Extension; -import io.quarkus.devtools.project.BuildTool; -import io.quarkus.devtools.writer.ProjectWriter; -import io.quarkus.maven.utilities.MojoUtils; -import io.quarkus.maven.utilities.MojoUtils.Element; -import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; -import io.quarkus.platform.tools.ToolsUtils; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Properties; -import org.apache.maven.model.Activation; -import org.apache.maven.model.ActivationProperty; -import org.apache.maven.model.Build; -import org.apache.maven.model.BuildBase; -import org.apache.maven.model.Dependency; -import org.apache.maven.model.DependencyManagement; -import org.apache.maven.model.Model; -import org.apache.maven.model.Plugin; -import org.apache.maven.model.PluginExecution; -import org.apache.maven.model.PluginManagement; -import org.apache.maven.model.Profile; - -public class MavenBuildFile extends BuildFile { - - private Model model; - - public MavenBuildFile(ProjectWriter writer) { - super(writer, BuildTool.MAVEN); - } - - private void initModel() throws IOException { - if (getWriter().exists(BuildTool.MAVEN.getDependenciesFile())) { - byte[] content = getWriter().getContent(BuildTool.MAVEN.getDependenciesFile()); - this.model = MojoUtils.readPom(new ByteArrayInputStream(content)); - } - } - - @Override - public void close() throws IOException { - if (getModel() == null) { - return; - } - try (ByteArrayOutputStream pomOutputStream = new ByteArrayOutputStream()) { - MojoUtils.write(getModel(), pomOutputStream); - write(BuildTool.MAVEN.getDependenciesFile(), pomOutputStream.toString("UTF-8")); - } - } - - @Override - protected void addDependencyInBuildFile(Dependency dependency) throws IOException { - if (getModel() != null) { - getModel().addDependency(dependency); - } - } - - @Override - protected void removeDependencyFromBuildFile(Dependency dependency) throws IOException { - if (getModel() != null) { - getModel().getDependencies() - .removeIf(d -> d.getGroupId().equals(dependency.getGroupId()) - && d.getArtifactId().equals(dependency.getArtifactId())); - } - } - - @Override - protected boolean hasDependency(Extension extension) throws IOException { - return getModel() != null && MojoUtils.hasDependency(getModel(), extension.getGroupId(), extension.getArtifactId()); - } - - @Override - public List getDependencies() throws IOException { - return getModel() == null ? Collections.emptyList() : getModel().getDependencies(); - } - - @Override - protected boolean containsBOM(String groupId, String artifactId) throws IOException { - if (getModel() == null || getModel().getDependencyManagement() == null) { - return false; - } - List dependencies = getModel().getDependencyManagement().getDependencies(); - return dependencies.stream() - // Find bom - .filter(dependency -> "import".equals(dependency.getScope())) - .filter(dependency -> "pom".equals(dependency.getType())) - // Does it matches the bom artifact name - .anyMatch(dependency -> dependency.getArtifactId() - .equals(MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLATFORM_ARTIFACT_ID_VALUE) - && dependency.getGroupId().equals(MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLATFORM_GROUP_ID_VALUE)); - } - - @Override - public void completeFile(String groupId, String artifactId, String version, QuarkusPlatformDescriptor platform, - Properties props) throws IOException { - addQuarkusProperties(platform); - addBom(platform); - final String pluginGroupId = ToolsUtils.getPluginGroupId(props); - final String pluginArtifactId = ToolsUtils.getPluginArtifactId(props); - addMainPluginConfig(pluginGroupId, pluginArtifactId); - addNativeProfile(pluginGroupId, pluginArtifactId); - } - - private void addBom(QuarkusPlatformDescriptor platform) throws IOException { - boolean hasBom = false; - DependencyManagement dm = getModel().getDependencyManagement(); - if (dm == null) { - dm = new DependencyManagement(); - getModel().setDependencyManagement(dm); - } else { - hasBom = containsBOM(platform.getBomGroupId(), platform.getBomArtifactId()); - } - - if (!hasBom) { - Dependency bom = new Dependency(); - bom.setGroupId(MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLATFORM_GROUP_ID_VALUE); - bom.setArtifactId(MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLATFORM_ARTIFACT_ID_VALUE); - bom.setVersion(MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLATFORM_VERSION_VALUE); - bom.setType("pom"); - bom.setScope("import"); - - dm.addDependency(bom); - } - } - - private void addNativeProfile(String pluginGroupId, String pluginArtifactId) throws IOException { - final boolean match = getModel().getProfiles().stream().anyMatch(p -> p.getId().equals("native")); - if (!match) { - PluginExecution exec = new PluginExecution(); - exec.addGoal("native-image"); - exec.setConfiguration(configuration(new Element("enableHttpUrlHandler", "true"))); - - Plugin plg = plugin(pluginGroupId, pluginArtifactId, MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLUGIN_VERSION_VALUE); - plg.addExecution(exec); - - BuildBase buildBase = new BuildBase(); - buildBase.addPlugin(plg); - - Profile profile = new Profile(); - profile.setId("native"); - profile.setBuild(buildBase); - - final Activation activation = new Activation(); - final ActivationProperty property = new ActivationProperty(); - property.setName("native"); - - activation.setProperty(property); - profile.setActivation(activation); - getModel().addProfile(profile); - } - } - - private void addMainPluginConfig(String groupId, String artifactId) throws IOException { - if (!hasPlugin(groupId, artifactId)) { - Build build = createBuildSectionIfRequired(); - Plugin plugin = plugin(groupId, artifactId, MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLUGIN_VERSION_VALUE); - if (isParentPom()) { - addPluginManagementSection(plugin); - //strip the quarkusVersion off - plugin = plugin(groupId, artifactId); - } - PluginExecution pluginExec = new PluginExecution(); - pluginExec.addGoal("build"); - plugin.addExecution(pluginExec); - build.getPlugins().add(plugin); - } - } - - private boolean hasPlugin(String groupId, String artifactId) throws IOException { - if (getModel() == null) { - return false; - } - List plugins = null; - final Build build = getModel().getBuild(); - if (build != null) { - if (isParentPom()) { - final PluginManagement management = build.getPluginManagement(); - if (management != null) { - plugins = management.getPlugins(); - } - } else { - plugins = build.getPlugins(); - } - } - return plugins != null && build.getPlugins() - .stream() - .anyMatch(p -> p.getGroupId().equalsIgnoreCase(groupId) && - p.getArtifactId().equalsIgnoreCase(artifactId)); - } - - private void addPluginManagementSection(Plugin plugin) throws IOException { - final Build build = getModel().getBuild(); - if (build != null && build.getPluginManagement() != null) { - if (build.getPluginManagement().getPlugins() == null) { - build.getPluginManagement().setPlugins(new ArrayList<>()); - } - build.getPluginManagement().getPlugins().add(plugin); - } - } - - private Build createBuildSectionIfRequired() throws IOException { - Build build = getModel().getBuild(); - if (build == null) { - build = new Build(); - getModel().setBuild(build); - } - if (build.getPlugins() == null) { - build.setPlugins(new ArrayList<>()); - } - return build; - } - - private void addQuarkusProperties(QuarkusPlatformDescriptor platform) throws IOException { - Properties properties = getModel().getProperties(); - if (properties == null) { - properties = new Properties(); - getModel().setProperties(properties); - } - properties.putIfAbsent(MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLUGIN_VERSION_NAME, - ToolsUtils.getPluginVersion(ToolsUtils.readQuarkusProperties(platform))); - properties.putIfAbsent(MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLATFORM_GROUP_ID_NAME, platform.getBomGroupId()); - properties.putIfAbsent(MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLATFORM_ARTIFACT_ID_NAME, platform.getBomArtifactId()); - properties.putIfAbsent(MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLATFORM_VERSION_NAME, platform.getBomVersion()); - } - - private boolean isParentPom() throws IOException { - return getModel() != null && "pom".equals(getModel().getPackaging()); - } - - @Override - public List getManagedDependencies() throws IOException { - if (getModel() == null) { - return Collections.emptyList(); - } - final DependencyManagement managed = getModel().getDependencyManagement(); - return managed != null ? managed.getDependencies() - : Collections.emptyList(); - } - - @Override - public String getProperty(String propertyName) throws IOException { - if (getModel() == null) { - return null; - } - return getModel().getProperties().getProperty(propertyName); - } - - private Model getModel() throws IOException { - if (model == null) { - initModel(); - } - return model; - } -} diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/AddExtensions.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/AddExtensions.java similarity index 52% rename from independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/AddExtensions.java rename to independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/AddExtensions.java index b0b1462bae823..7a78655f23b3a 100644 --- a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/AddExtensions.java +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/AddExtensions.java @@ -1,6 +1,13 @@ -package io.quarkus.cli.commands; +package io.quarkus.devtools.commands; +import static com.google.common.base.Preconditions.checkNotNull; + +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.AddExtensionsCommandHandler; import io.quarkus.devtools.project.QuarkusProject; +import io.quarkus.devtools.project.extensions.ExtensionManager; import io.quarkus.platform.tools.ToolsConstants; import io.quarkus.platform.tools.ToolsUtils; import java.util.Set; @@ -13,19 +20,26 @@ public class AddExtensions { public static final String NAME = "add-extensions"; public static final String EXTENSIONS = ToolsUtils.dotJoin(ToolsConstants.QUARKUS, NAME, "extensions"); public static final String OUTCOME_UPDATED = ToolsUtils.dotJoin(ToolsConstants.QUARKUS, NAME, "outcome", "updated"); + public static final String EXTENSION_MANAGER = ToolsUtils.dotJoin(ToolsConstants.QUARKUS, NAME, "extension-manager"); private final QuarkusCommandInvocation invocation; + private final AddExtensionsCommandHandler handler = new AddExtensionsCommandHandler(); public AddExtensions(final QuarkusProject quarkusProject) { this.invocation = new QuarkusCommandInvocation(quarkusProject); } + public AddExtensions extensionManager(ExtensionManager extensionManager) { + invocation.setValue(EXTENSION_MANAGER, checkNotNull(extensionManager, "extensionManager is required")); + return this; + } + public AddExtensions extensions(Set extensions) { invocation.setValue(EXTENSIONS, extensions); return this; } public QuarkusCommandOutcome execute() throws QuarkusCommandException { - return new AddExtensionsCommandHandler().execute(invocation); + return handler.execute(invocation); } } diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/CreateProject.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/CreateProject.java similarity index 81% rename from independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/CreateProject.java rename to independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/CreateProject.java index 0f01b9bc452fc..2b5c21c508cf8 100644 --- a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/CreateProject.java +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/CreateProject.java @@ -1,17 +1,22 @@ -package io.quarkus.cli.commands; +package io.quarkus.devtools.commands; import static com.google.common.base.Preconditions.checkNotNull; -import static io.quarkus.generators.ProjectGenerator.CLASS_NAME; -import static io.quarkus.generators.ProjectGenerator.IS_SPRING; -import static io.quarkus.generators.ProjectGenerator.JAVA_TARGET; -import static io.quarkus.generators.ProjectGenerator.PROJECT_ARTIFACT_ID; -import static io.quarkus.generators.ProjectGenerator.PROJECT_GROUP_ID; -import static io.quarkus.generators.ProjectGenerator.PROJECT_VERSION; -import static io.quarkus.generators.ProjectGenerator.SOURCE_TYPE; - +import static io.quarkus.devtools.project.codegen.ProjectGenerator.CLASS_NAME; +import static io.quarkus.devtools.project.codegen.ProjectGenerator.EXTENSIONS; +import static io.quarkus.devtools.project.codegen.ProjectGenerator.IS_SPRING; +import static io.quarkus.devtools.project.codegen.ProjectGenerator.JAVA_TARGET; +import static io.quarkus.devtools.project.codegen.ProjectGenerator.PROJECT_ARTIFACT_ID; +import static io.quarkus.devtools.project.codegen.ProjectGenerator.PROJECT_GROUP_ID; +import static io.quarkus.devtools.project.codegen.ProjectGenerator.PROJECT_VERSION; +import static io.quarkus.devtools.project.codegen.ProjectGenerator.SOURCE_TYPE; + +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.CreateProjectCommandHandler; import io.quarkus.devtools.project.BuildTool; import io.quarkus.devtools.project.QuarkusProject; -import io.quarkus.generators.SourceType; +import io.quarkus.devtools.project.codegen.SourceType; import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; import java.io.IOException; import java.nio.file.Path; @@ -41,7 +46,7 @@ public class CreateProject { private Map values = new HashMap<>(); public CreateProject(final Path projectFolderPath, QuarkusPlatformDescriptor platformDescr) { - this.projectFolderPath = checkNotNull(projectFolderPath, "writer is required"); + this.projectFolderPath = checkNotNull(projectFolderPath, "projectFolderPath is required"); this.platformDescr = checkNotNull(platformDescr, "platformDescr is required"); } @@ -81,14 +86,11 @@ public CreateProject className(String className) { return this; } - /** - * @deprecated in 1.3.0.CR - */ - @Deprecated public CreateProject extensions(Set extensions) { if (isSpringStyle(extensions)) { setValue(IS_SPRING, true); } + setValue(EXTENSIONS, extensions); return this; } diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/ListExtensions.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/ListExtensions.java similarity index 65% rename from independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/ListExtensions.java rename to independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/ListExtensions.java index 6f9fd3adbc438..b476ce291c37a 100644 --- a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/ListExtensions.java +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/ListExtensions.java @@ -1,11 +1,15 @@ -package io.quarkus.cli.commands; +package io.quarkus.devtools.commands; +import static com.google.common.base.Preconditions.checkNotNull; + +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.ListExtensionsCommandHandler; import io.quarkus.devtools.project.QuarkusProject; +import io.quarkus.devtools.project.extensions.ExtensionManager; import io.quarkus.platform.tools.ToolsConstants; import io.quarkus.platform.tools.ToolsUtils; -import java.io.IOException; -import java.util.Map; -import org.apache.maven.model.Dependency; /** * Instances of this class are not thread-safe. They are created per single invocation. @@ -16,6 +20,7 @@ public class ListExtensions { public static final String ALL = ToolsUtils.dotJoin(PARAM_PREFIX, "all"); public static final String FORMAT = ToolsUtils.dotJoin(PARAM_PREFIX, "format"); public static final String SEARCH = ToolsUtils.dotJoin(PARAM_PREFIX, "search"); + public static final String EXTENSION_MANAGER = ToolsUtils.dotJoin(PARAM_PREFIX, "extension-manager"); private final QuarkusCommandInvocation invocation; private final ListExtensionsCommandHandler handler = new ListExtensionsCommandHandler(); @@ -34,32 +39,17 @@ public ListExtensions format(String format) { return this; } - public ListExtensions search(String search) { - invocation.setValue(SEARCH, search); + public ListExtensions extensionManager(ExtensionManager extensionManager) { + invocation.setValue(EXTENSION_MANAGER, checkNotNull(extensionManager, "extensionManager is required")); return this; } - /** - * @deprecated in 1.3.0.CR1 - * Please use {@link #all(boolean)}, {@link #format(String)} and {@link #search(String)} respectively. - */ - @Deprecated - public void listExtensions(boolean all, String format, String search) throws IOException { - all(all); - format(format); - search(search); - try { - execute(); - } catch (QuarkusCommandException e) { - throw new IOException("Failed to list extensions", e); - } + public ListExtensions search(String search) { + invocation.setValue(SEARCH, search); + return this; } public QuarkusCommandOutcome execute() throws QuarkusCommandException { return handler.execute(invocation); } - - public Map findInstalled() throws IOException { - return handler.findInstalled(invocation); - } } diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/RemoveExtensions.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/RemoveExtensions.java similarity index 58% rename from independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/RemoveExtensions.java rename to independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/RemoveExtensions.java index 5e6c2b6de8ed5..76345158e412f 100644 --- a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/RemoveExtensions.java +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/RemoveExtensions.java @@ -1,6 +1,13 @@ -package io.quarkus.cli.commands; +package io.quarkus.devtools.commands; +import static com.google.common.base.Preconditions.checkNotNull; + +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.RemoveExtensionsCommandHandler; import io.quarkus.devtools.project.QuarkusProject; +import io.quarkus.devtools.project.extensions.ExtensionManager; import io.quarkus.platform.tools.ToolsConstants; import io.quarkus.platform.tools.ToolsUtils; import java.util.Set; @@ -13,6 +20,7 @@ public class RemoveExtensions { public static final String NAME = "remove-extensions"; public static final String EXTENSIONS = ToolsUtils.dotJoin(ToolsConstants.QUARKUS, NAME, "extensions"); public static final String OUTCOME_UPDATED = ToolsUtils.dotJoin(ToolsConstants.QUARKUS, NAME, "outcome", "updated"); + public static final String EXTENSION_MANAGER = ToolsUtils.dotJoin(ToolsConstants.QUARKUS, NAME, "extension-manager"); private final QuarkusCommandInvocation invocation; @@ -25,6 +33,11 @@ public RemoveExtensions extensions(Set extensions) { return this; } + public RemoveExtensions extensionManager(ExtensionManager extensionManager) { + invocation.setValue(EXTENSION_MANAGER, checkNotNull(extensionManager, "extensionManager is required")); + return this; + } + public QuarkusCommandOutcome execute() throws QuarkusCommandException { return new RemoveExtensionsCommandHandler().execute(invocation); } diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/QuarkusCommandException.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/data/QuarkusCommandException.java similarity index 87% rename from independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/QuarkusCommandException.java rename to independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/data/QuarkusCommandException.java index e9545c28ae89f..eb13d45624e73 100644 --- a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/QuarkusCommandException.java +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/data/QuarkusCommandException.java @@ -1,4 +1,4 @@ -package io.quarkus.cli.commands; +package io.quarkus.devtools.commands.data; public class QuarkusCommandException extends Exception { diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/QuarkusCommandInvocation.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/data/QuarkusCommandInvocation.java similarity index 87% rename from independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/QuarkusCommandInvocation.java rename to independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/data/QuarkusCommandInvocation.java index 3f2de476c5a9e..c2ff4b506364f 100644 --- a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/QuarkusCommandInvocation.java +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/data/QuarkusCommandInvocation.java @@ -1,8 +1,7 @@ -package io.quarkus.cli.commands; +package io.quarkus.devtools.commands.data; import static com.google.common.base.Preconditions.checkNotNull; -import io.quarkus.devtools.buildfile.BuildFile; import io.quarkus.devtools.project.QuarkusProject; import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; import io.quarkus.platform.tools.DefaultMessageWriter; @@ -43,11 +42,7 @@ public MessageWriter log() { } public QuarkusPlatformDescriptor getPlatformDescriptor() { - return quarkusProject.getDescriptor(); - } - - public BuildFile getBuildFile() { - return quarkusProject.getBuildFile(); + return quarkusProject.getPlatformDescriptor(); } } diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/QuarkusCommandOutcome.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/data/QuarkusCommandOutcome.java similarity index 91% rename from independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/QuarkusCommandOutcome.java rename to independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/data/QuarkusCommandOutcome.java index be8747ea3d9df..e04e6e2f6a283 100644 --- a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/QuarkusCommandOutcome.java +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/data/QuarkusCommandOutcome.java @@ -1,4 +1,4 @@ -package io.quarkus.cli.commands; +package io.quarkus.devtools.commands.data; public class QuarkusCommandOutcome extends ValueMap { diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/SelectionResult.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/data/SelectionResult.java similarity index 94% rename from independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/SelectionResult.java rename to independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/data/SelectionResult.java index 3b51911a5794e..c97d6afcc0a31 100644 --- a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/SelectionResult.java +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/data/SelectionResult.java @@ -1,4 +1,4 @@ -package io.quarkus.cli.commands; +package io.quarkus.devtools.commands.data; import io.quarkus.dependencies.Extension; import java.util.Collections; diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/ValueMap.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/data/ValueMap.java similarity index 83% rename from independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/ValueMap.java rename to independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/data/ValueMap.java index 7636d9a88b5c6..8d99fc794f6e1 100644 --- a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/ValueMap.java +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/data/ValueMap.java @@ -1,4 +1,4 @@ -package io.quarkus.cli.commands; +package io.quarkus.devtools.commands.data; import java.util.HashMap; import java.util.Map; @@ -48,6 +48,18 @@ public String getStringValue(String name) { throw new IllegalStateException("value for '" + name + "' must be a String"); } + public boolean getBooleanValue(String name) { + final Object value = getValue(name, null); + if (value == null) { + throw new IllegalStateException("value for '" + name + "' must be defined"); + } + + if (Boolean.class.equals(value.getClass())) { + return ((Boolean) value).booleanValue(); + } + return Boolean.parseBoolean(value.toString()); + } + public boolean getValue(String name, boolean defaultValue) { final Object value = getValue(name, null); if (value == null) { diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/AddExtensionsCommandHandler.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/AddExtensionsCommandHandler.java new file mode 100644 index 0000000000000..97e585a7b68b8 --- /dev/null +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/AddExtensionsCommandHandler.java @@ -0,0 +1,47 @@ +package io.quarkus.devtools.commands.handlers; + +import static io.quarkus.devtools.commands.AddExtensions.EXTENSION_MANAGER; +import static io.quarkus.devtools.commands.handlers.QuarkusCommandHandlers.computeCoordsFromQuery; + +import io.quarkus.bootstrap.model.AppArtifactCoords; +import io.quarkus.devtools.commands.AddExtensions; +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.project.extensions.ExtensionManager; +import io.quarkus.devtools.project.extensions.ExtensionManager.InstallResult; +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +/** + * This class is thread-safe. It extracts extensions to be added to the project from an instance of + * {@link QuarkusCommandInvocation}. + */ +public class AddExtensionsCommandHandler implements QuarkusCommandHandler { + + @Override + public QuarkusCommandOutcome execute(QuarkusCommandInvocation invocation) throws QuarkusCommandException { + final Set extensionsQuery = invocation.getValue(AddExtensions.EXTENSIONS, Collections.emptySet()); + if (extensionsQuery.isEmpty()) { + return QuarkusCommandOutcome.success().setValue(AddExtensions.OUTCOME_UPDATED, false); + } + + final List extensionsToAdd = computeCoordsFromQuery(invocation, extensionsQuery); + final ExtensionManager extensionManager = invocation.getValue(EXTENSION_MANAGER, + invocation.getQuarkusProject().getExtensionManager()); + try { + if (extensionsToAdd != null) { + final InstallResult result = extensionManager.install(extensionsToAdd); + return new QuarkusCommandOutcome(true).setValue(AddExtensions.OUTCOME_UPDATED, result.isSourceUpdated()); + } + + } catch (IOException e) { + throw new QuarkusCommandException("Failed to add extensions", e); + } + + return new QuarkusCommandOutcome(false).setValue(AddExtensions.OUTCOME_UPDATED, false); + } + +} diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/CreateProjectCommandHandler.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/CreateProjectCommandHandler.java new file mode 100644 index 0000000000000..ea5d9ac82022f --- /dev/null +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/CreateProjectCommandHandler.java @@ -0,0 +1,81 @@ +package io.quarkus.devtools.commands.handlers; + +import static io.quarkus.devtools.commands.handlers.QuarkusCommandHandlers.computeCoordsFromQuery; +import static io.quarkus.devtools.project.codegen.ProjectGenerator.*; + +import io.quarkus.bootstrap.model.AppArtifactCoords; +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.project.BuildTool; +import io.quarkus.devtools.project.buildfile.GradleBuildFilesCreator; +import io.quarkus.devtools.project.codegen.ProjectGenerator; +import io.quarkus.devtools.project.codegen.ProjectGeneratorRegistry; +import io.quarkus.devtools.project.codegen.SourceType; +import io.quarkus.devtools.project.codegen.rest.BasicRestProjectGenerator; +import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; +import io.quarkus.platform.tools.ToolsUtils; +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.Properties; +import java.util.Set; + +/** + * Instances of this class are thread-safe. They create a new project extracting all the necessary properties from an instance + * of {@link QuarkusCommandInvocation}. + */ +public class CreateProjectCommandHandler implements QuarkusCommandHandler { + + @Override + public QuarkusCommandOutcome execute(QuarkusCommandInvocation invocation) throws QuarkusCommandException { + final QuarkusPlatformDescriptor platformDescr = invocation.getPlatformDescriptor(); + invocation.setValue(BOM_GROUP_ID, platformDescr.getBomGroupId()); + invocation.setValue(BOM_ARTIFACT_ID, platformDescr.getBomArtifactId()); + invocation.setValue(QUARKUS_VERSION, platformDescr.getQuarkusVersion()); + invocation.setValue(BOM_VERSION, platformDescr.getBomVersion()); + final Set extensionsQuery = invocation.getValue(ProjectGenerator.EXTENSIONS, Collections.emptySet()); + + final Properties quarkusProps = ToolsUtils.readQuarkusProperties(platformDescr); + quarkusProps.forEach((k, v) -> invocation.setValue(k.toString().replace("-", "_"), v.toString())); + + try { + String className = invocation.getStringValue(CLASS_NAME); + if (className != null) { + className = invocation.getValue(SOURCE_TYPE, SourceType.JAVA).stripExtensionFrom(className); + int idx = className.lastIndexOf('.'); + if (idx >= 0) { + String pkgName = invocation.getStringValue(PACKAGE_NAME); + if (pkgName == null) { + invocation.setValue(PACKAGE_NAME, className.substring(0, idx)); + } + className = className.substring(idx + 1); + } + invocation.setValue(CLASS_NAME, className); + } + + final List extensionsToAdd = computeCoordsFromQuery(invocation, extensionsQuery); + + // extensionsToAdd is null when an error occurred while matching extensions + if (extensionsToAdd != null) { + ProjectGeneratorRegistry.get(BasicRestProjectGenerator.NAME).generate(invocation); + + //TODO ia3andy extensions should be added directly during the project generation + if (invocation.getQuarkusProject().getBuildTool().equals(BuildTool.GRADLE)) { + final GradleBuildFilesCreator creator = new GradleBuildFilesCreator(invocation.getQuarkusProject()); + creator.create( + invocation.getStringValue(PROJECT_GROUP_ID), + invocation.getStringValue(PROJECT_ARTIFACT_ID), + invocation.getStringValue(PROJECT_VERSION), + quarkusProps, + extensionsToAdd); + } else { + invocation.getQuarkusProject().getExtensionManager().install(extensionsToAdd); + } + } + } catch (IOException e) { + throw new QuarkusCommandException("Failed to create project", e); + } + return QuarkusCommandOutcome.success(); + } +} diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/ListExtensionsCommandHandler.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/ListExtensionsCommandHandler.java new file mode 100644 index 0000000000000..e5fc7aa261285 --- /dev/null +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/ListExtensionsCommandHandler.java @@ -0,0 +1,161 @@ +package io.quarkus.devtools.commands.handlers; + +import static io.quarkus.devtools.project.extensions.Extensions.toKey; +import static java.util.stream.Collectors.toMap; + +import io.quarkus.bootstrap.model.AppArtifactCoords; +import io.quarkus.bootstrap.model.AppArtifactKey; +import io.quarkus.dependencies.Extension; +import io.quarkus.devtools.commands.ListExtensions; +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.project.BuildTool; +import io.quarkus.devtools.project.extensions.ExtensionManager; +import io.quarkus.platform.tools.MessageWriter; +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.BiConsumer; +import java.util.function.Function; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Instances of this class are thread-safe. It lists extensions according to the options passed in as properties of + * {@link QuarkusCommandInvocation} + */ +public class ListExtensionsCommandHandler implements QuarkusCommandHandler { + + private static final String FULL_FORMAT = "%-8s %-50s %-50s %-25s%n%s"; + private static final String CONCISE_FORMAT = "%-50s %-50s"; + private static final String NAME_FORMAT = "%-50s"; + + @Override + public QuarkusCommandOutcome execute(QuarkusCommandInvocation invocation) throws QuarkusCommandException { + + final boolean all = invocation.getValue(ListExtensions.ALL, true); + final String format = invocation.getValue(ListExtensions.FORMAT, "concise"); + final String search = invocation.getValue(ListExtensions.SEARCH, "*"); + final ExtensionManager extensionManager = invocation.getValue(ListExtensions.EXTENSION_MANAGER, + invocation.getQuarkusProject().getExtensionManager()); + + Map installedByKey; + try { + installedByKey = extensionManager.getInstalled().stream() + .collect(toMap(AppArtifactCoords::getKey, Function.identity())); + } catch (IOException e) { + throw new QuarkusCommandException("Failed to determine the list of installed extensions", e); + } + + Stream platformExtensionsStream = invocation.getPlatformDescriptor().getExtensions().stream(); + platformExtensionsStream = platformExtensionsStream.filter(this::filterUnlisted); + if (search != null && !"*".equalsIgnoreCase(search)) { + final Pattern searchPattern = Pattern.compile(".*" + search + ".*", Pattern.CASE_INSENSITIVE); + platformExtensionsStream = platformExtensionsStream.filter(e -> filterBySearch(searchPattern, e)); + } + List platformExtensions = platformExtensionsStream.collect(Collectors.toList()); + + if (platformExtensions.isEmpty()) { + invocation.log().info("No extension found with this pattern"); + } else { + String extensionStatus = all ? "available" : "installable"; + invocation.log().info(String.format("%nCurrent Quarkus extensions %s: ", extensionStatus)); + + BiConsumer currentFormatter; + switch (format.toLowerCase()) { + case "name": + currentFormatter = this::nameFormatter; + break; + case "full": + currentFormatter = this::fullFormatter; + currentFormatter.accept(invocation.log(), + new String[] { "Status", "Extension", "ArtifactId", "Updated Version", "Guide" }); + break; + case "concise": + default: + currentFormatter = this::conciseFormatter; + } + + platformExtensions.forEach(platformExtension -> display(invocation.log(), platformExtension, + installedByKey.get(toKey(platformExtension)), all, currentFormatter)); + final BuildTool buildTool = invocation.getQuarkusProject().getBuildTool(); + if ("concise".equalsIgnoreCase(format)) { + if (BuildTool.GRADLE.equals(buildTool)) { + invocation.log().info("\nTo get more information, append --format=full to your command line."); + } else { + invocation.log().info( + "\nTo get more information, append -Dquarkus.extension.format=full to your command line."); + } + } + + if (BuildTool.GRADLE.equals(buildTool)) { + invocation.log().info("\nAdd an extension to your project by adding the dependency to your " + + "build.gradle or use `./gradlew addExtension --extensions=\"artifactId\"`"); + } else { + invocation.log().info("\nAdd an extension to your project by adding the dependency to your " + + "pom.xml or use `./mvnw quarkus:add-extension -Dextensions=\"artifactId\"`"); + } + + } + + return QuarkusCommandOutcome.success(); + } + + private boolean filterUnlisted(Extension e) { + return !e.getMetadata().containsKey("unlisted"); + } + + private boolean filterBySearch(final Pattern searchPattern, Extension e) { + return searchPattern.matcher(e.getName()).matches(); + } + + private void conciseFormatter(MessageWriter writer, String[] cols) { + writer.info(String.format(CONCISE_FORMAT, cols[1], cols[2], cols[4])); + } + + private void fullFormatter(MessageWriter writer, String[] cols) { + writer.info(String.format(FULL_FORMAT, cols[0], cols[1], cols[2], cols[3], cols[4])); + } + + private void nameFormatter(MessageWriter writer, String[] cols) { + writer.info(String.format(NAME_FORMAT, cols[2])); + } + + private void display(MessageWriter messageWriter, final Extension platformExtension, final AppArtifactCoords installed, + boolean all, + BiConsumer formatter) { + if (!all && installed != null) { + return; + } + + String label = ""; + String version = ""; + + if (installed != null) { + final String installedVersion = installed.getVersion(); + if (installedVersion == null) { + label = "default"; + version = platformExtension.getVersion(); + } else if (installedVersion.equalsIgnoreCase(platformExtension.getVersion())) { + label = "custom"; + version = installedVersion; + } else { + label = "custom*"; + version = String.format("%s* <> %s", installedVersion, platformExtension.getVersion()); + } + } + + String[] result = new String[] { label, platformExtension.getName(), platformExtension.getArtifactId(), version, + platformExtension.getGuide() }; + + for (int i = 0; i < result.length; i++) { + result[i] = Objects.toString(result[i], ""); + } + + formatter.accept(messageWriter, result); + } + +} diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/QuarkusCommandHandler.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/QuarkusCommandHandler.java new file mode 100644 index 0000000000000..dfc372a8b3ce3 --- /dev/null +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/QuarkusCommandHandler.java @@ -0,0 +1,10 @@ +package io.quarkus.devtools.commands.handlers; + +import io.quarkus.devtools.commands.data.QuarkusCommandException; +import io.quarkus.devtools.commands.data.QuarkusCommandInvocation; +import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; + +public interface QuarkusCommandHandler { + + QuarkusCommandOutcome execute(QuarkusCommandInvocation invocation) throws QuarkusCommandException; +} diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/AddExtensionsCommandHandler.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/QuarkusCommandHandlers.java similarity index 56% rename from independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/AddExtensionsCommandHandler.java rename to independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/QuarkusCommandHandlers.java index 6541aa6992b7c..ec0110279d903 100644 --- a/independent-projects/tools/common/src/main/java/io/quarkus/cli/commands/AddExtensionsCommandHandler.java +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/QuarkusCommandHandlers.java @@ -1,8 +1,13 @@ -package io.quarkus.cli.commands; +package io.quarkus.devtools.commands.handlers; +import static io.quarkus.platform.tools.ConsoleMessageFormats.nok; + +import com.google.common.collect.ImmutableList; +import io.quarkus.bootstrap.model.AppArtifactCoords; import io.quarkus.dependencies.Extension; -import io.quarkus.devtools.buildfile.BuildFile; -import java.io.IOException; +import io.quarkus.devtools.commands.data.QuarkusCommandInvocation; +import io.quarkus.devtools.commands.data.SelectionResult; +import io.quarkus.devtools.project.extensions.Extensions; import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; @@ -11,101 +16,74 @@ import java.util.regex.PatternSyntaxException; import java.util.stream.Collectors; -/** - * This class is thread-safe. It extracts extensions to be added to the project from an instance of - * {@link QuarkusCommandInvocation}. - */ -public class AddExtensionsCommandHandler implements QuarkusCommand { - - final static Printer PRINTER = new Printer(); +final class QuarkusCommandHandlers { - @Override - public QuarkusCommandOutcome execute(QuarkusCommandInvocation invocation) throws QuarkusCommandException { - - final Set extensions = invocation.getValue(AddExtensions.EXTENSIONS, Collections.emptySet()); - if (extensions.isEmpty()) { - return QuarkusCommandOutcome.success().setValue(AddExtensions.OUTCOME_UPDATED, false); - } - - boolean updated = false; - boolean success = true; - - final List registry = invocation.getPlatformDescriptor().getExtensions(); + private QuarkusCommandHandlers() { + } - final BuildFile buildFile = invocation.getBuildFile(); - try { - for (String query : extensions) { - if (query.contains(":")) { - // GAV case. - updated = buildFile.addExtensionAsGAV(query) || updated; + static List computeCoordsFromQuery(final QuarkusCommandInvocation invocation, + final Set extensionsQuery) { + final ImmutableList.Builder builder = ImmutableList.builder(); + for (String query : extensionsQuery) { + if (query.contains(":")) { + builder.add(AppArtifactCoords.fromString(query)); + } else { + SelectionResult result = select(query, invocation.getPlatformDescriptor().getExtensions(), false); + if (result.matches()) { + final Set withStrippedVersion = result.getExtensions().stream().map(Extensions::toCoords) + .map(Extensions::stripVersion).collect(Collectors.toSet()); + // We strip the version because those extensions are managed + builder.addAll(withStrippedVersion); } else { - SelectionResult result = select(query, registry, false); - if (!result.matches()) { - StringBuilder sb = new StringBuilder(); - // We have 3 cases, we can still have a single candidate, but the match is on label - // or we have several candidates, or none - Set candidates = result.getExtensions(); - if (candidates.isEmpty()) { - // No matches at all. - PRINTER.nok(" Cannot find a dependency matching '" + query + "', maybe a typo?"); - success = false; - } else { - sb.append(Printer.NOK).append(" Multiple extensions matching '").append(query).append("'"); - result.getExtensions() - .forEach(extension -> sb.append(System.lineSeparator()).append(" * ") - .append(extension.managementKey())); - sb.append(System.lineSeparator()) - .append(" Be more specific e.g using the exact name or the full GAV."); - PRINTER.print(sb.toString()); - success = false; - } - } else { // Matches. - for (Extension extension : result) { - // Don't set success to false even if the dependency is not added; as it's should be idempotent. - updated = buildFile.addDependency(invocation.getPlatformDescriptor(), extension) || updated; - } + StringBuilder sb = new StringBuilder(); + // We have 3 cases, we can still have a single candidate, but the match is on label + // or we have several candidates, or none + Set candidates = result.getExtensions(); + if (candidates.isEmpty()) { + // No matches at all. + invocation.log().info(nok(" Cannot find a dependency matching '" + query + "', maybe a typo?")); + return null; + } else { + sb.append(nok(" Multiple extensions matching '")).append(query).append("'"); + result.getExtensions() + .forEach(extension -> sb.append(System.lineSeparator()).append(" * ") + .append(extension.managementKey())); + sb.append(System.lineSeparator()) + .append(" Be more specific e.g using the exact name or the full GAV."); + invocation.log().info(sb.toString()); + return null; } } } - } catch (IOException e) { - throw new QuarkusCommandException("Failed to add extensions", e); - } - - if (buildFile != null && updated) { - try { - buildFile.close(); - } catch (IOException e) { - throw new QuarkusCommandException("Failed to update the project", e); - } } - - return new QuarkusCommandOutcome(success).setValue(AddExtensions.OUTCOME_UPDATED, updated); + return builder.build(); } /** * Selection algorithm. * * @param query the query - * @param extensions the extension list + * @param allPlatformExtensions the list of all platform extensions * @param labelLookup whether or not the query must be tested against the labels of the extensions. Should * be {@code false} by default. * @return the list of matching candidates and whether or not a match has been found. */ - static SelectionResult select(String query, List extensions, boolean labelLookup) { + static SelectionResult select(final String query, final List allPlatformExtensions, final boolean labelLookup) { String q = query.trim().toLowerCase(); // Try exact matches - Set matchesNameOrArtifactId = extensions.stream() + Set matchesNameOrArtifactId = allPlatformExtensions.stream() .filter(extension -> extension.getName().equalsIgnoreCase(q) || matchesArtifactId(extension.getArtifactId(), q)) .collect(Collectors.toSet()); if (matchesNameOrArtifactId.size() == 1) { return new SelectionResult(matchesNameOrArtifactId, true); } - extensions = extensions.stream().filter(e -> !e.isUnlisted()).collect(Collectors.toList()); + final List listedPlatformExtensions = allPlatformExtensions.stream() + .filter(e -> !e.isUnlisted()).collect(Collectors.toList()); // Try short names - Set matchesShortName = extensions.stream().filter(extension -> matchesShortName(extension, q)) + Set matchesShortName = listedPlatformExtensions.stream().filter(extension -> matchesShortName(extension, q)) .collect(Collectors.toSet()); if (matchesShortName.size() == 1 && matchesNameOrArtifactId.isEmpty()) { @@ -113,9 +91,11 @@ static SelectionResult select(String query, List extensions, boolean } // Partial matches on name, artifactId and short names - Set partialMatches = extensions.stream().filter(extension -> extension.getName().toLowerCase().contains(q) - || extension.getArtifactId().toLowerCase().contains(q) - || extension.getShortName().toLowerCase().contains(q)).collect(Collectors.toSet()); + Set partialMatches = listedPlatformExtensions.stream() + .filter(extension -> extension.getName().toLowerCase().contains(q) + || extension.getArtifactId().toLowerCase().contains(q) + || extension.getShortName().toLowerCase().contains(q)) + .collect(Collectors.toSet()); // Even if we have a single partial match, if the name, artifactId and short names are ambiguous, so not // consider it as a match. if (partialMatches.size() == 1 && matchesNameOrArtifactId.isEmpty() && matchesShortName.isEmpty()) { @@ -125,7 +105,7 @@ static SelectionResult select(String query, List extensions, boolean // find by labels List matchesLabels; if (labelLookup) { - matchesLabels = extensions.stream() + matchesLabels = listedPlatformExtensions.stream() .filter(extension -> extension.labelsForMatching().contains(q)).collect(Collectors.toList()); } else { matchesLabels = Collections.emptyList(); @@ -135,7 +115,7 @@ static SelectionResult select(String query, List extensions, boolean Set matchesPatterns; Pattern pattern = toRegex(q); if (pattern != null) { - matchesPatterns = extensions.stream() + matchesPatterns = listedPlatformExtensions.stream() .filter(extension -> pattern.matcher(extension.getName().toLowerCase()).matches() || pattern.matcher(extension.getArtifactId().toLowerCase()).matches() || pattern.matcher(extension.getShortName().toLowerCase()).matches() @@ -229,4 +209,5 @@ private static boolean matchesArtifactId(String artifactId, String q) { return artifactId.equalsIgnoreCase(q) || artifactId.equalsIgnoreCase("quarkus-" + q); } + } diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/RemoveExtensionsCommandHandler.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/RemoveExtensionsCommandHandler.java new file mode 100644 index 0000000000000..4df546b59d851 --- /dev/null +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/commands/handlers/RemoveExtensionsCommandHandler.java @@ -0,0 +1,49 @@ +package io.quarkus.devtools.commands.handlers; + +import static io.quarkus.devtools.commands.RemoveExtensions.EXTENSION_MANAGER; +import static io.quarkus.devtools.commands.handlers.QuarkusCommandHandlers.computeCoordsFromQuery; + +import io.quarkus.bootstrap.model.AppArtifactCoords; +import io.quarkus.bootstrap.model.AppArtifactKey; +import io.quarkus.devtools.commands.RemoveExtensions; +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.project.extensions.ExtensionManager; +import io.quarkus.devtools.project.extensions.ExtensionManager.UninstallResult; +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * This class is thread-safe. It extracts extensions to be removed from the project from an instance of + * {@link QuarkusCommandInvocation}. + */ +public class RemoveExtensionsCommandHandler implements QuarkusCommandHandler { + + @Override + public QuarkusCommandOutcome execute(QuarkusCommandInvocation invocation) throws QuarkusCommandException { + final Set extensionsQuery = invocation.getValue(RemoveExtensions.EXTENSIONS, Collections.emptySet()); + if (extensionsQuery.isEmpty()) { + return QuarkusCommandOutcome.success().setValue(RemoveExtensions.OUTCOME_UPDATED, false); + } + + final List extensionsToRemove = computeCoordsFromQuery(invocation, extensionsQuery); + final ExtensionManager extensionManager = invocation.getValue(EXTENSION_MANAGER, + invocation.getQuarkusProject().getExtensionManager()); + try { + if (extensionsToRemove != null) { + final Set keys = extensionsToRemove.stream().map(AppArtifactCoords::getKey) + .collect(Collectors.toSet()); + final UninstallResult result = extensionManager.uninstall(keys); + return new QuarkusCommandOutcome(true).setValue(RemoveExtensions.OUTCOME_UPDATED, result.isSourceUpdated()); + } + } catch (IOException e) { + throw new QuarkusCommandException("Failed to add extensions", e); + } + + return new QuarkusCommandOutcome(true).setValue(RemoveExtensions.OUTCOME_UPDATED, false); + } +} diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/BuildTool.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/BuildTool.java index 0987465b3c1b3..85900d6aaf5e1 100644 --- a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/BuildTool.java +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/BuildTool.java @@ -1,11 +1,10 @@ package io.quarkus.devtools.project; -import static com.google.common.base.Preconditions.checkNotNull; - -import io.quarkus.devtools.buildfile.BuildFile; -import io.quarkus.devtools.buildfile.GradleBuildFile; -import io.quarkus.devtools.buildfile.MavenBuildFile; -import io.quarkus.devtools.writer.ProjectWriter; +import io.quarkus.devtools.project.buildfile.GenericGradleBuildFile; +import io.quarkus.devtools.project.buildfile.MavenBuildFile; +import io.quarkus.devtools.project.extensions.ExtensionManager; +import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; +import java.nio.file.Path; /** * An enum of build tools, such as Maven and Gradle. @@ -53,14 +52,14 @@ public String getBuildDirectory() { return buildDirectory; } - public BuildFile createBuildFile(final ProjectWriter writer) { - checkNotNull(writer, "writer is required"); + public ExtensionManager createExtensionManager(final Path projectFolderPath, + final QuarkusPlatformDescriptor platformDescriptor) { switch (this) { case GRADLE: - return new GradleBuildFile(writer); + return new GenericGradleBuildFile(); case MAVEN: default: - return new MavenBuildFile(writer); + return new MavenBuildFile(projectFolderPath, platformDescriptor); } } } diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/QuarkusProject.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/QuarkusProject.java index 8d6ef03e40831..7e391029cf935 100644 --- a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/QuarkusProject.java +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/QuarkusProject.java @@ -2,33 +2,38 @@ import static com.google.common.base.Preconditions.checkNotNull; -import io.quarkus.devtools.buildfile.BuildFile; -import io.quarkus.devtools.writer.FileProjectWriter; +import io.quarkus.devtools.project.buildfile.MavenBuildFile; +import io.quarkus.devtools.project.extensions.ExtensionManager; import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; import java.nio.file.Path; public final class QuarkusProject { private final Path projectFolderPath; - private final BuildFile buildFile; - private final QuarkusPlatformDescriptor descriptor; + private final QuarkusPlatformDescriptor platformDescriptor; + private final ExtensionManager extensionManager; - private QuarkusProject(final Path projectFolderPath, final QuarkusPlatformDescriptor descriptor, - final BuildFile buildFile) { + private QuarkusProject(final Path projectFolderPath, final QuarkusPlatformDescriptor platformDescriptor, + final ExtensionManager extensionManager) { this.projectFolderPath = checkNotNull(projectFolderPath, "projectFolderPath is required"); - this.descriptor = checkNotNull(descriptor, "descriptor is required"); - this.buildFile = checkNotNull(buildFile, "buildFile is required"); + this.platformDescriptor = checkNotNull(platformDescriptor, "platformDescriptor is required"); + this.extensionManager = checkNotNull(extensionManager, "extensionManager is required"); } - public static QuarkusProject of(final Path projectFolderPath, final QuarkusPlatformDescriptor descriptor, + public static QuarkusProject of(final Path projectFolderPath, final QuarkusPlatformDescriptor platformDescriptor, + final ExtensionManager extensionManager) { + return new QuarkusProject(projectFolderPath, platformDescriptor, extensionManager); + } + + public static QuarkusProject of(final Path projectFolderPath, final QuarkusPlatformDescriptor platformDescriptor, final BuildTool buildTool) { - final BuildFile buildFile = buildTool.createBuildFile(new FileProjectWriter(projectFolderPath.toFile())); - return of(projectFolderPath, descriptor, buildFile); + return new QuarkusProject(projectFolderPath, platformDescriptor, + buildTool.createExtensionManager(projectFolderPath, platformDescriptor)); } - public static QuarkusProject of(final Path projectFolderPath, final QuarkusPlatformDescriptor descriptor, - final BuildFile buildFile) { - return new QuarkusProject(projectFolderPath, descriptor, buildFile); + public static QuarkusProject maven(final Path projectFolderPath, final QuarkusPlatformDescriptor platformDescriptor) { + return new QuarkusProject(projectFolderPath, platformDescriptor, + new MavenBuildFile(projectFolderPath, platformDescriptor)); } public static QuarkusProject resolveExistingProject(final Path projectFolderPath, @@ -44,19 +49,19 @@ public Path getProjectFolderPath() { return projectFolderPath; } - public BuildFile getBuildFile() { - return buildFile; + public BuildTool getBuildTool() { + return extensionManager.getBuildTool(); } - public BuildTool getBuildTool() { - return buildFile.getBuildTool(); + public ExtensionManager getExtensionManager() { + return extensionManager; } - public QuarkusPlatformDescriptor getDescriptor() { - return descriptor; + public QuarkusPlatformDescriptor getPlatformDescriptor() { + return platformDescriptor; } - private static BuildTool resolveExistingProjectBuildTool(Path projectFolderPath) { + public static BuildTool resolveExistingProjectBuildTool(Path projectFolderPath) { if (projectFolderPath.resolve("pom.xml").toFile().exists()) { return BuildTool.MAVEN; } else if (projectFolderPath.resolve("build.gradle").toFile().exists() @@ -65,4 +70,5 @@ private static BuildTool resolveExistingProjectBuildTool(Path projectFolderPath) } return null; } + } diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/buildfile/AbstractGradleBuildFile.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/buildfile/AbstractGradleBuildFile.java new file mode 100644 index 0000000000000..7633ed2605662 --- /dev/null +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/buildfile/AbstractGradleBuildFile.java @@ -0,0 +1,245 @@ +package io.quarkus.devtools.project.buildfile; + +import io.quarkus.bootstrap.model.AppArtifactCoords; +import io.quarkus.bootstrap.model.AppArtifactKey; +import io.quarkus.devtools.project.BuildTool; +import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Optional; +import java.util.Properties; +import java.util.Scanner; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; + +// We keep it here to take advantage of the abstract tests +public abstract class AbstractGradleBuildFile extends BuildFile { + + private static final String BUILD_GRADLE_PATH = "build.gradle"; + private static final String SETTINGS_GRADLE_PATH = "settings.gradle"; + private static final String GRADLE_PROPERTIES_PATH = "gradle.properties"; + + private final Optional rootProjectPath; + + private AtomicReference modelReference = new AtomicReference<>(); + + public AbstractGradleBuildFile(final Path projectFolderPath, final QuarkusPlatformDescriptor platformDescriptor) { + super(projectFolderPath, platformDescriptor); + this.rootProjectPath = Optional.empty(); + } + + public AbstractGradleBuildFile(final Path projectFolderPath, final QuarkusPlatformDescriptor platformDescriptor, + Path rootProjectPath) { + super(projectFolderPath, platformDescriptor); + this.rootProjectPath = Optional.ofNullable(rootProjectPath); + } + + @Override + public void writeToDisk() throws IOException { + if (rootProjectPath.isPresent()) { + Files.write(rootProjectPath.get().resolve(SETTINGS_GRADLE_PATH), getModel().getRootSettingsContent().getBytes()); + try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { + getModel().getRootPropertiesContent().store(out, "Gradle properties"); + Files.write(rootProjectPath.get().resolve(GRADLE_PROPERTIES_PATH), + getModel().getRootSettingsContent().getBytes()); + } + } else { + writeToProjectFile(SETTINGS_GRADLE_PATH, getModel().getSettingsContent().getBytes()); + try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { + getModel().getPropertiesContent().store(out, "Gradle properties"); + writeToProjectFile(GRADLE_PROPERTIES_PATH, out.toByteArray()); + } + } + writeToProjectFile(BUILD_GRADLE_PATH, getModel().getBuildContent().getBytes()); + } + + @Override + protected void addDependencyInBuildFile(AppArtifactCoords coords) throws IOException { + addDependencyInModel(getModel(), coords); + } + + static void addDependencyInModel(Model model, AppArtifactCoords coords) throws IOException { + StringBuilder newBuildContent = new StringBuilder(); + readLineByLine(model.getBuildContent(), currentLine -> { + newBuildContent.append(currentLine).append(System.lineSeparator()); + if (currentLine.startsWith("dependencies {")) { + newBuildContent.append(" implementation '") + .append(coords.getGroupId()) + .append(":") + .append(coords.getArtifactId()); + if (coords.getVersion() != null && !coords.getVersion().isEmpty()) { + newBuildContent.append(":") + .append(coords.getVersion()); + } + newBuildContent.append("'") + .append(System.lineSeparator()); + } + }); + model.setBuildContent(newBuildContent.toString()); + } + + @Override + protected void removeDependencyFromBuildFile(AppArtifactKey key) throws IOException { + StringBuilder newBuildContent = new StringBuilder(); + Scanner scanner = new Scanner(getModel().getBuildContent()); + while (scanner.hasNextLine()) { + String line = scanner.nextLine(); + if (!line.contains(key.getGroupId() + ":" + key.getArtifactId())) { + newBuildContent.append(line).append(System.lineSeparator()); + } + } + scanner.close(); + getModel().setBuildContent(newBuildContent.toString()); + } + + @Override + protected boolean containsBOM(String groupId, String artifactId) throws IOException { + String buildContent = getModel().getBuildContent(); + return buildContent.contains("enforcedPlatform(\"${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:") + || buildContent.contains("enforcedPlatform(\"" + groupId + ":" + artifactId + ":"); + } + + @Override + public String getProperty(String propertyName) throws IOException { + final String property = getModel().getPropertiesContent().getProperty(propertyName); + if (property != null || getModel().getRootPropertiesContent() == null) { + return property; + } + return getModel().getRootPropertiesContent().getProperty(propertyName); + } + + @Override + public BuildTool getBuildTool() { + return BuildTool.GRADLE; + } + + private Model getModel() throws IOException { + return modelReference.updateAndGet(model -> { + if (model == null) { + try { + return readModel(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + return model; + }); + } + + @Override + protected void refreshData() { + this.modelReference.set(null); + } + + private boolean hasRootProjectFile(final String fileName) throws IOException { + if (!rootProjectPath.isPresent()) { + return false; + } + final Path filePath = rootProjectPath.get().resolve(fileName); + return Files.exists(filePath); + } + + private byte[] readRootProjectFile(final String fileName) throws IOException { + final Path filePath = rootProjectPath + .orElseThrow(() -> new IllegalStateException("There is no rootProject defined in this GradleBuildFile")) + .resolve(fileName); + return Files.readAllBytes(filePath); + } + + private Model readModel() throws IOException { + String settingsContent = ""; + String buildContent = ""; + Properties propertiesContent = new Properties(); + String rootSettingsContent = null; + Properties rootPropertiesContent = null; + if (hasProjectFile(SETTINGS_GRADLE_PATH)) { + final byte[] settings = readProjectFile(SETTINGS_GRADLE_PATH); + settingsContent = new String(settings, StandardCharsets.UTF_8); + } + if (hasRootProjectFile(SETTINGS_GRADLE_PATH)) { + final byte[] settings = readRootProjectFile(SETTINGS_GRADLE_PATH); + rootSettingsContent = new String(settings, StandardCharsets.UTF_8); + } + if (hasProjectFile(BUILD_GRADLE_PATH)) { + final byte[] build = readProjectFile(BUILD_GRADLE_PATH); + buildContent = new String(build, StandardCharsets.UTF_8); + } + if (hasProjectFile(GRADLE_PROPERTIES_PATH)) { + final byte[] properties = readProjectFile(GRADLE_PROPERTIES_PATH); + propertiesContent.load(new ByteArrayInputStream(properties)); + } + if (hasRootProjectFile(GRADLE_PROPERTIES_PATH)) { + final byte[] properties = readRootProjectFile(GRADLE_PROPERTIES_PATH); + rootPropertiesContent = new Properties(); + rootPropertiesContent.load(new ByteArrayInputStream(properties)); + } + return new Model(settingsContent, buildContent, propertiesContent, rootSettingsContent, rootPropertiesContent); + } + + protected String getBuildContent() throws IOException { + return getModel().getBuildContent(); + } + + private static void readLineByLine(String content, Consumer lineConsumer) { + try (Scanner scanner = new Scanner(new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)), + StandardCharsets.UTF_8.name())) { + while (scanner.hasNextLine()) { + String currentLine = scanner.nextLine(); + lineConsumer.accept(currentLine); + } + } + } + + static class Model { + private String settingsContent; + private String buildContent; + private Properties propertiesContent; + + private String rootSettingsContent; + private Properties rootPropertiesContent; + + public Model(String settingsContent, String buildContent, Properties propertiesContent, String rootSettingsContent, + Properties rootPropertiesContent) { + this.settingsContent = settingsContent; + this.buildContent = buildContent; + this.propertiesContent = propertiesContent; + this.rootSettingsContent = rootSettingsContent; + this.rootPropertiesContent = rootPropertiesContent; + } + + public String getSettingsContent() { + return settingsContent; + } + + public String getBuildContent() { + return buildContent; + } + + public Properties getPropertiesContent() { + return propertiesContent; + } + + public String getRootSettingsContent() { + return rootSettingsContent; + } + + public Properties getRootPropertiesContent() { + return rootPropertiesContent; + } + + public void setSettingsContent(String settingsContent) { + this.settingsContent = settingsContent; + } + + public void setBuildContent(String buildContent) { + this.buildContent = buildContent; + } + + } + +} diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/buildfile/BuildFile.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/buildfile/BuildFile.java new file mode 100644 index 0000000000000..ee313991a9942 --- /dev/null +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/buildfile/BuildFile.java @@ -0,0 +1,161 @@ +package io.quarkus.devtools.project.buildfile; + +import static com.google.common.base.Preconditions.checkNotNull; +import static io.quarkus.devtools.project.extensions.Extensions.findInList; +import static io.quarkus.devtools.project.extensions.Extensions.toCoords; +import static io.quarkus.devtools.project.extensions.Extensions.toKey; +import static java.util.stream.Collectors.toList; + +import io.quarkus.bootstrap.model.AppArtifactCoords; +import io.quarkus.bootstrap.model.AppArtifactKey; +import io.quarkus.dependencies.Extension; +import io.quarkus.devtools.project.extensions.ExtensionManager; +import io.quarkus.devtools.project.extensions.Extensions; +import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collection; +import java.util.List; +import java.util.Set; +import java.util.concurrent.atomic.LongAdder; +import java.util.stream.Collectors; +import org.apache.maven.model.Dependency; + +public abstract class BuildFile implements ExtensionManager { + + private final Path projectFolderPath; + private final QuarkusPlatformDescriptor platformDescriptor; + + public BuildFile(final Path projectFolderPath, final QuarkusPlatformDescriptor platformDescriptor) { + this.projectFolderPath = checkNotNull(projectFolderPath, "projectPath is required"); + this.platformDescriptor = checkNotNull(platformDescriptor, "platformDescriptor is required"); + } + + @Override + public final boolean hasQuarkusPlatformBom() throws IOException { + return containsBOM(platformDescriptor.getBomGroupId(), platformDescriptor.getBomArtifactId()); + } + + @Override + public final InstallResult install(Collection coords) throws IOException { + if (!hasQuarkusPlatformBom()) { + throw new IllegalStateException("The Quarkus BOM is required to add a Quarkus extension"); + } + this.refreshData(); + final Set existingKeys = getDependenciesKeys(); + final LongAdder counter = new LongAdder(); + coords.stream() + .distinct() + .filter(a -> !existingKeys.contains(a.getKey())) + .forEach(e -> { + try { + addDependencyInBuildFile(e); + counter.increment(); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + }); + this.writeToDisk(); + return new InstallResult(counter.intValue()); + } + + @Override + public final Collection getInstalled() throws IOException { + this.refreshData(); + return this.getDependencies().stream() + .filter(d -> this.isQuarkusExtension(toKey(d))) + .map(d -> toCoords(d, extractVersion(d))) + .collect(toList()); + } + + @Override + public final UninstallResult uninstall(Collection keys) throws IOException { + this.refreshData(); + final Set existingKeys = getDependenciesKeys(); + final LongAdder counter = new LongAdder(); + keys.stream() + .distinct() + .filter(existingKeys::contains) + .forEach(k -> { + try { + removeDependencyFromBuildFile(k); + counter.increment(); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + }); + this.writeToDisk(); + return new UninstallResult(counter.intValue()); + } + + protected abstract void addDependencyInBuildFile(AppArtifactCoords coords) throws IOException; + + protected abstract void removeDependencyFromBuildFile(AppArtifactKey key) throws IOException; + + protected abstract List getDependencies() throws IOException; + + protected abstract void writeToDisk() throws IOException; + + protected abstract String getProperty(String propertyName) throws IOException; + + protected abstract boolean containsBOM(String groupId, String artifactId) throws IOException; + + protected abstract void refreshData(); + + protected Path getProjectFolderPath() { + return projectFolderPath; + } + + protected boolean hasProjectFile(final String fileName) throws IOException { + final Path filePath = projectFolderPath.resolve(fileName); + return Files.exists(filePath); + } + + protected byte[] readProjectFile(final String fileName) throws IOException { + final Path filePath = projectFolderPath.resolve(fileName); + return Files.readAllBytes(filePath); + } + + protected void writeToProjectFile(final String fileName, final byte[] content) throws IOException { + Files.write(projectFolderPath.resolve(fileName), content); + } + + private boolean isQuarkusExtension(final AppArtifactKey key) { + // This will not always be true as the platform descriptor does not contain the list of all available extensions + return isDefinedInRegistry(platformDescriptor.getExtensions(), key); + } + + private Set getDependenciesKeys() throws IOException { + return getDependencies().stream().map(Extensions::toKey).collect(Collectors.toSet()); + } + + private String extractVersion(final Dependency d) { + String version = d != null ? d.getVersion() : null; + if (version != null && version.startsWith("$")) { + String value = null; + try { + value = (String) this.getProperty(propertyName(version)); + if (value != null) { + return value; + } + } catch (IOException e) { + // ignore this error. + } + } + if (version != null) { + return version; + } + return null; + } + + private String propertyName(final String variable) { + return variable.substring(2, variable.length() - 1); + } + + public static boolean isDefinedInRegistry(List registry, final AppArtifactKey key) { + return findInList(registry, key).isPresent(); + } + +} diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/buildfile/GenericGradleBuildFile.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/buildfile/GenericGradleBuildFile.java new file mode 100644 index 0000000000000..51501741f8dc0 --- /dev/null +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/buildfile/GenericGradleBuildFile.java @@ -0,0 +1,40 @@ +package io.quarkus.devtools.project.buildfile; + +import io.quarkus.bootstrap.model.AppArtifactCoords; +import io.quarkus.bootstrap.model.AppArtifactKey; +import io.quarkus.devtools.project.BuildTool; +import io.quarkus.devtools.project.extensions.ExtensionManager; +import java.io.IOException; +import java.util.Collection; + +/** + * TODO We need to find a way to use the gradle api outside of a gradle plugin + */ +public class GenericGradleBuildFile implements ExtensionManager { + + @Override + public BuildTool getBuildTool() { + return BuildTool.GRADLE; + } + + @Override + public Collection getInstalled() throws IOException { + throw new IllegalStateException("This feature is not yet implemented outside of the Gradle Plugin."); + } + + @Override + public boolean hasQuarkusPlatformBom() throws IOException { + throw new IllegalStateException("This feature is not yet implemented outside of the Gradle Plugin."); + } + + @Override + public InstallResult install(Collection coords) throws IOException { + throw new IllegalStateException("This feature is not yet implemented outside of the Gradle Plugin."); + } + + @Override + public UninstallResult uninstall(Collection keys) throws IOException { + throw new IllegalStateException("This feature is not yet implemented outside of the Gradle Plugin."); + } + +} diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/buildfile/GradleBuildFilesCreator.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/buildfile/GradleBuildFilesCreator.java new file mode 100644 index 0000000000000..735adff8eaeea --- /dev/null +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/buildfile/GradleBuildFilesCreator.java @@ -0,0 +1,215 @@ +package io.quarkus.devtools.project.buildfile; + +import static io.quarkus.devtools.project.buildfile.AbstractGradleBuildFile.addDependencyInModel; + +import io.quarkus.bootstrap.model.AppArtifactCoords; +import io.quarkus.devtools.project.QuarkusProject; +import io.quarkus.devtools.project.buildfile.AbstractGradleBuildFile.Model; +import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; +import io.quarkus.platform.tools.ToolsUtils; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Properties; +import java.util.Scanner; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; + +public final class GradleBuildFilesCreator { + + private static final String BUILD_GRADLE_PATH = "build.gradle"; + private static final String SETTINGS_GRADLE_PATH = "settings.gradle"; + private static final String GRADLE_PROPERTIES_PATH = "gradle.properties"; + private final QuarkusProject quarkusProject; + + private AtomicReference modelReference = new AtomicReference<>(); + + public GradleBuildFilesCreator(QuarkusProject quarkusProject) { + this.quarkusProject = quarkusProject; + } + + public void create(String groupId, String artifactId, String version, + Properties properties, List extensions) throws IOException { + createSettingsContent(artifactId); + createBuildContent(groupId, version); + createProperties(); + extensions.stream() + .forEach(e -> { + try { + addDependencyInBuildFile(e); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + }); + this.writeToDisk(); + } + + private void writeToDisk() throws IOException { + writeToProjectFile(SETTINGS_GRADLE_PATH, getModel().getSettingsContent().getBytes()); + writeToProjectFile(BUILD_GRADLE_PATH, getModel().getBuildContent().getBytes()); + try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { + getModel().getPropertiesContent().store(out, "Gradle properties"); + writeToProjectFile(GRADLE_PROPERTIES_PATH, out.toByteArray()); + } + } + + private void addDependencyInBuildFile(AppArtifactCoords coords) throws IOException { + addDependencyInModel(getModel(), coords); + } + + protected boolean containsBOM(String groupId, String artifactId) throws IOException { + String buildContent = getModel().getBuildContent(); + return buildContent.contains("enforcedPlatform(\"${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:") + || buildContent.contains("enforcedPlatform(\"" + groupId + ":" + artifactId + ":"); + } + + public String getProperty(String propertyName) throws IOException { + return getModel().getPropertiesContent().getProperty(propertyName); + } + + private void readLineByLine(String content, Consumer lineConsumer) { + try (Scanner scanner = new Scanner(new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)), + StandardCharsets.UTF_8.name())) { + while (scanner.hasNextLine()) { + String currentLine = scanner.nextLine(); + lineConsumer.accept(currentLine); + } + } + } + + private Model getModel() throws IOException { + return modelReference.updateAndGet(model -> { + if (model == null) { + try { + return readModel(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + return model; + }); + } + + private Model readModel() throws IOException { + String settingsContent = ""; + String buildContent = ""; + Properties propertiesContent = new Properties(); + if (hasProjectFile(SETTINGS_GRADLE_PATH)) { + final byte[] settings = readProjectFile(SETTINGS_GRADLE_PATH); + settingsContent = new String(settings, StandardCharsets.UTF_8); + } + if (hasProjectFile(BUILD_GRADLE_PATH)) { + final byte[] build = readProjectFile(BUILD_GRADLE_PATH); + buildContent = new String(build, StandardCharsets.UTF_8); + } + if (hasProjectFile(GRADLE_PROPERTIES_PATH)) { + final byte[] properties = readProjectFile(GRADLE_PROPERTIES_PATH); + propertiesContent.load(new ByteArrayInputStream(properties)); + } + return new Model(settingsContent, buildContent, propertiesContent, null, null); + } + + protected boolean hasProjectFile(final String fileName) throws IOException { + final Path filePath = quarkusProject.getProjectFolderPath().resolve(fileName); + return Files.exists(filePath); + } + + protected byte[] readProjectFile(final String fileName) throws IOException { + final Path filePath = quarkusProject.getProjectFolderPath().resolve(fileName); + return Files.readAllBytes(filePath); + } + + protected void writeToProjectFile(final String fileName, final byte[] content) throws IOException { + Files.write(quarkusProject.getProjectFolderPath().resolve(fileName), content); + } + + private void createBuildContent(String groupId, String version) + throws IOException { + final String buildContent = getModel().getBuildContent(); + StringBuilder res = new StringBuilder(buildContent); + if (!buildContent.contains("id 'io.quarkus'")) { + res.append("plugins {"); + res.append(System.lineSeparator()).append(" id 'java'").append(System.lineSeparator()); + res.append(System.lineSeparator()).append(" id 'io.quarkus'").append(System.lineSeparator()); + res.append("}"); + } + if (!containsBOM(quarkusProject.getPlatformDescriptor().getBomGroupId(), + quarkusProject.getPlatformDescriptor().getBomArtifactId())) { + res.append(System.lineSeparator()); + res.append("dependencies {").append(System.lineSeparator()); + res.append( + " implementation enforcedPlatform(\"${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}\")") + .append(System.lineSeparator()); + res.append(" implementation 'io.quarkus:quarkus-resteasy'").append(System.lineSeparator()); + res.append(" testImplementation 'io.quarkus:quarkus-junit5'").append(System.lineSeparator()); + res.append(" testImplementation 'io.rest-assured:rest-assured'").append(System.lineSeparator()); + res.append("}").append(System.lineSeparator()); + + } + String groupLine = "group '" + groupId + "'"; + if (!buildContent.contains(groupLine)) { + res.append(System.lineSeparator()).append(groupLine) + .append(System.lineSeparator()); + } + String versionLine = "version '" + version + "'"; + if (!buildContent.contains(versionLine)) { + res.append(System.lineSeparator()).append(versionLine) + .append(System.lineSeparator()); + } + + res.append(System.lineSeparator()) + .append("test {").append(System.lineSeparator()) + .append(" systemProperty \"java.util.logging.manager\", \"org.jboss.logmanager.LogManager\"") + .append(System.lineSeparator()) + .append("}"); + + getModel().setBuildContent(res.toString()); + } + + private void createSettingsContent(String artifactId) throws IOException { + final String settingsContent = getModel().getSettingsContent(); + final StringBuilder res = new StringBuilder(); + if (!settingsContent.contains("id 'io.quarkus'")) { + res.append(System.lineSeparator()); + res.append("pluginManagement {").append(System.lineSeparator()); + res.append(" repositories {").append(System.lineSeparator()); + res.append(" mavenLocal()").append(System.lineSeparator()); + res.append(" mavenCentral()").append(System.lineSeparator()); + res.append(" gradlePluginPortal()").append(System.lineSeparator()); + res.append(" }").append(System.lineSeparator()); + res.append(" plugins {").append(System.lineSeparator()); + res.append(" id 'io.quarkus' version \"${quarkusPluginVersion}\"").append(System.lineSeparator()); + res.append(" }").append(System.lineSeparator()); + res.append("}").append(System.lineSeparator()); + } + if (!settingsContent.contains("rootProject.name")) { + res.append(System.lineSeparator()).append("rootProject.name='").append(artifactId).append("'") + .append(System.lineSeparator()); + } + res.append(settingsContent); + getModel().setSettingsContent(res.toString()); + } + + private void createProperties() throws IOException { + final QuarkusPlatformDescriptor platform = quarkusProject.getPlatformDescriptor(); + Properties props = getModel().getPropertiesContent(); + if (props.getProperty("quarkusPluginVersion") == null) { + props.setProperty("quarkusPluginVersion", ToolsUtils.getPluginVersion(ToolsUtils.readQuarkusProperties(platform))); + } + if (props.getProperty("quarkusPlatformGroupId") == null) { + props.setProperty("quarkusPlatformGroupId", platform.getBomGroupId()); + } + if (props.getProperty("quarkusPlatformArtifactId") == null) { + props.setProperty("quarkusPlatformArtifactId", platform.getBomArtifactId()); + } + if (props.getProperty("quarkusPlatformVersion") == null) { + props.setProperty("quarkusPlatformVersion", platform.getBomVersion()); + } + } + +} diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/buildfile/MavenBuildFile.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/buildfile/MavenBuildFile.java new file mode 100644 index 0000000000000..cc2edb8740c02 --- /dev/null +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/buildfile/MavenBuildFile.java @@ -0,0 +1,121 @@ +package io.quarkus.devtools.project.buildfile; + +import static io.quarkus.devtools.project.extensions.Extensions.toKey; + +import io.quarkus.bootstrap.model.AppArtifactCoords; +import io.quarkus.bootstrap.model.AppArtifactKey; +import io.quarkus.devtools.project.BuildTool; +import io.quarkus.maven.utilities.MojoUtils; +import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Path; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicReference; +import org.apache.maven.model.Dependency; +import org.apache.maven.model.Model; + +public class MavenBuildFile extends BuildFile { + + private AtomicReference modelRef = new AtomicReference<>(); + + public MavenBuildFile(final Path projectFolderPath, final QuarkusPlatformDescriptor platformDescriptor) { + super(projectFolderPath, platformDescriptor); + } + + @Override + public void writeToDisk() throws IOException { + if (getModel() == null) { + return; + } + try (ByteArrayOutputStream pomOutputStream = new ByteArrayOutputStream()) { + MojoUtils.write(getModel(), pomOutputStream); + writeToProjectFile(BuildTool.MAVEN.getDependenciesFile(), pomOutputStream.toByteArray()); + } + } + + @Override + protected void addDependencyInBuildFile(AppArtifactCoords coords) throws IOException { + if (getModel() != null) { + final Dependency d = new Dependency(); + d.setGroupId(coords.getGroupId()); + d.setArtifactId(coords.getArtifactId()); + d.setVersion(coords.getVersion()); + // When classifier is empty, you get in the pom.xml + if (coords.getClassifier() != null && !coords.getClassifier().isEmpty()) { + d.setClassifier(coords.getClassifier()); + } + d.setType(coords.getType()); + getModel().addDependency(d); + } + } + + @Override + protected void removeDependencyFromBuildFile(AppArtifactKey key) throws IOException { + if (getModel() != null) { + getModel().getDependencies() + .removeIf(d -> Objects.equals(toKey(d), key)); + } + } + + @Override + public List getDependencies() throws IOException { + return getModel() == null ? Collections.emptyList() : getModel().getDependencies(); + } + + @Override + protected boolean containsBOM(String groupId, String artifactId) throws IOException { + if (getModel() == null || getModel().getDependencyManagement() == null) { + return false; + } + List dependencies = getModel().getDependencyManagement().getDependencies(); + return dependencies.stream() + // Find bom + .filter(dependency -> "import".equals(dependency.getScope())) + .filter(dependency -> "pom".equals(dependency.getType())) + // Does it matches the bom artifact name + .anyMatch(dependency -> dependency.getArtifactId() + .equals(MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLATFORM_ARTIFACT_ID_VALUE) + && dependency.getGroupId().equals(MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLATFORM_GROUP_ID_VALUE)); + } + + @Override + protected void refreshData() { + this.modelRef.set(null); + } + + @Override + public String getProperty(String propertyName) throws IOException { + if (getModel() == null) { + return null; + } + return getModel().getProperties().getProperty(propertyName); + } + + @Override + public BuildTool getBuildTool() { + return BuildTool.MAVEN; + } + + private Model getModel() throws IOException { + return modelRef.updateAndGet(model -> { + if (model == null) { + try { + return initModel(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + return model; + }); + } + + private Model initModel() throws IOException { + byte[] content = readProjectFile(BuildTool.MAVEN.getDependenciesFile()); + return MojoUtils.readPom(new ByteArrayInputStream(content)); + } +} diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/generators/ProjectGenerator.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/codegen/ProjectGenerator.java similarity index 77% rename from independent-projects/tools/common/src/main/java/io/quarkus/generators/ProjectGenerator.java rename to independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/codegen/ProjectGenerator.java index 518e86f0505c2..575ac806428bb 100644 --- a/independent-projects/tools/common/src/main/java/io/quarkus/generators/ProjectGenerator.java +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/codegen/ProjectGenerator.java @@ -1,7 +1,6 @@ -package io.quarkus.generators; +package io.quarkus.devtools.project.codegen; -import io.quarkus.cli.commands.QuarkusCommandInvocation; -import io.quarkus.devtools.writer.ProjectWriter; +import io.quarkus.devtools.commands.data.QuarkusCommandInvocation; import java.io.IOException; public interface ProjectGenerator { @@ -18,11 +17,12 @@ public interface ProjectGenerator { String BUILD_DIRECTORY = "build_dir"; String ADDITIONAL_GITIGNORE_ENTRIES = "additional_gitignore_entries"; String CLASS_NAME = "class_name"; + String EXTENSIONS = "extensions"; String IS_SPRING = "is_spring"; String RESOURCE_PATH = "path"; String JAVA_TARGET = "java_target"; String getName(); - void generate(ProjectWriter writer, QuarkusCommandInvocation invocation) throws IOException; + void generate(QuarkusCommandInvocation invocation) throws IOException; } diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/generators/ProjectGeneratorRegistry.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/codegen/ProjectGeneratorRegistry.java similarity index 96% rename from independent-projects/tools/common/src/main/java/io/quarkus/generators/ProjectGeneratorRegistry.java rename to independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/codegen/ProjectGeneratorRegistry.java index 14e50d3f9b847..4090c600ef7b5 100644 --- a/independent-projects/tools/common/src/main/java/io/quarkus/generators/ProjectGeneratorRegistry.java +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/codegen/ProjectGeneratorRegistry.java @@ -1,4 +1,4 @@ -package io.quarkus.generators; +package io.quarkus.devtools.project.codegen; import java.util.Map; import java.util.NoSuchElementException; diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/generators/SourceType.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/codegen/SourceType.java similarity index 98% rename from independent-projects/tools/common/src/main/java/io/quarkus/generators/SourceType.java rename to independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/codegen/SourceType.java index be92d2f2be6b2..3044f4992d372 100644 --- a/independent-projects/tools/common/src/main/java/io/quarkus/generators/SourceType.java +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/codegen/SourceType.java @@ -1,4 +1,4 @@ -package io.quarkus.generators; +package io.quarkus.devtools.project.codegen; import io.quarkus.maven.utilities.MojoUtils; import java.util.Arrays; diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/generators/rest/BasicRestProjectGenerator.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/codegen/rest/BasicRestProjectGenerator.java similarity index 92% rename from independent-projects/tools/common/src/main/java/io/quarkus/generators/rest/BasicRestProjectGenerator.java rename to independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/codegen/rest/BasicRestProjectGenerator.java index 9578e9be6f54f..06be3d4e82d46 100644 --- a/independent-projects/tools/common/src/main/java/io/quarkus/generators/rest/BasicRestProjectGenerator.java +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/codegen/rest/BasicRestProjectGenerator.java @@ -1,12 +1,13 @@ -package io.quarkus.generators.rest; +package io.quarkus.devtools.project.codegen.rest; import static java.lang.String.format; -import io.quarkus.cli.commands.QuarkusCommandInvocation; +import io.quarkus.devtools.commands.data.QuarkusCommandInvocation; import io.quarkus.devtools.project.BuildTool; -import io.quarkus.devtools.writer.ProjectWriter; -import io.quarkus.generators.ProjectGenerator; -import io.quarkus.generators.SourceType; +import io.quarkus.devtools.project.codegen.ProjectGenerator; +import io.quarkus.devtools.project.codegen.SourceType; +import io.quarkus.devtools.project.codegen.writer.FileProjectWriter; +import io.quarkus.devtools.project.codegen.writer.ProjectWriter; import io.quarkus.maven.utilities.MojoUtils; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -25,7 +26,13 @@ public String getName() { } @Override - public void generate(final ProjectWriter writer, QuarkusCommandInvocation invocation) throws IOException { + public void generate(QuarkusCommandInvocation invocation) throws IOException { + final FileProjectWriter writer = new FileProjectWriter(invocation.getQuarkusProject().getProjectFolderPath().toFile()); + writer.init(); + generate(writer, invocation); + } + + void generate(ProjectWriter writer, QuarkusCommandInvocation invocation) throws IOException { final BasicRestProject project = new BasicRestProject(writer, invocation); project.initProject(); @@ -122,7 +129,7 @@ private boolean initBuildTool() throws IOException { } private BuildTool getBuildTool() { - return invocation.getBuildFile().getBuildTool(); + return invocation.getQuarkusProject().getBuildTool(); } private void generate(final String templateName, QuarkusCommandInvocation invocation, final String outputFilePath, @@ -141,7 +148,7 @@ private void generate(final String templateName, QuarkusCommandInvocation invoca // do some nasty replacements for Java target if we want to generate Java 11 projects if ("11".equals(invocation.getValue(JAVA_TARGET))) { - if (BuildTool.GRADLE.equals(invocation.getBuildFile().getBuildTool())) { + if (BuildTool.GRADLE.equals(invocation.getQuarkusProject().getBuildTool())) { template = template.replace("JavaVersion.VERSION_1_8", "JavaVersion.VERSION_11"); } else { template = template.replace("1.8", diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/writer/FileProjectWriter.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/codegen/writer/FileProjectWriter.java similarity index 74% rename from independent-projects/tools/common/src/main/java/io/quarkus/devtools/writer/FileProjectWriter.java rename to independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/codegen/writer/FileProjectWriter.java index 35e4a2d4e1e52..c5c8fdb5dfcf0 100644 --- a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/writer/FileProjectWriter.java +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/codegen/writer/FileProjectWriter.java @@ -1,7 +1,7 @@ /** * */ -package io.quarkus.devtools.writer; +package io.quarkus.devtools.project.codegen.writer; import java.io.File; import java.io.IOException; @@ -19,21 +19,21 @@ public FileProjectWriter(final File file) { } @Override - public boolean init() { - if (root.exists() && !root.isDirectory()) { - System.out.println("Project root needs to either not exist or be a directory"); - return false; - } else if (!root.exists()) { + public void init() throws IOException { + if (!root.exists()) { boolean mkdirStatus = root.mkdirs(); if (!mkdirStatus) { - System.out.println("Failed to create root directory"); - return false; + throw new IOException("Failed to create root directory"); } + return; + } + if (!root.isDirectory()) { + throw new IOException("Project root needs to be a directory"); + } + final String[] files = root.list(); + if (files != null && files.length > 0) { + throw new IOException("You can't create a project when the folder is not empty."); } - - System.out.println("Creating a new project in " + root.getAbsolutePath()); - - return true; } @Override diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/writer/ProjectWriter.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/codegen/writer/ProjectWriter.java similarity index 84% rename from independent-projects/tools/common/src/main/java/io/quarkus/devtools/writer/ProjectWriter.java rename to independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/codegen/writer/ProjectWriter.java index 45e75607edac7..4b9ecdea8cf5d 100644 --- a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/writer/ProjectWriter.java +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/codegen/writer/ProjectWriter.java @@ -1,7 +1,7 @@ /** * */ -package io.quarkus.devtools.writer; +package io.quarkus.devtools.project.codegen.writer; import java.io.Closeable; import java.io.File; @@ -12,9 +12,7 @@ */ public interface ProjectWriter extends Closeable { - default boolean init() { - return true; - } + void init() throws IOException; void write(String path, String content) throws IOException; diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/extensions/ExtensionManager.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/extensions/ExtensionManager.java new file mode 100644 index 0000000000000..ad71e2ff709a5 --- /dev/null +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/extensions/ExtensionManager.java @@ -0,0 +1,108 @@ +package io.quarkus.devtools.project.extensions; + +import io.quarkus.bootstrap.model.AppArtifactCoords; +import io.quarkus.bootstrap.model.AppArtifactKey; +import io.quarkus.dependencies.Extension; +import io.quarkus.devtools.project.BuildTool; +import java.io.IOException; +import java.util.Collection; +import java.util.Objects; + +/** + * This interface defines a high level way of managing (read/write) extensions in any QuarkusProject + */ +public interface ExtensionManager { + + /** + * @return the {@link BuildTool} of this extension manager + */ + BuildTool getBuildTool(); + + /** + * Read the build file(s) to get the list of installed extensions in this Quarkus project. + * + * @return The list of {@link AppArtifactCoords} installed in the project build file(s). + * @throws IOException if a problem occurs while reading the project build file(s) + */ + Collection getInstalled() throws IOException; + + /** + * Read build file(s) to check if an extension is installed in this Quarkus project. + * + * @param key the {@link AppArtifactKey} of the extension to check + * @return true if it's installed + * @throws IOException if a problem occurs while reading the project build file(s) + */ + default boolean isInstalled(AppArtifactKey key) throws IOException { + return getInstalled().stream().anyMatch(i -> Objects.equals(i.getKey(), key)); + } + + /** + * Check that the Quarkus Platform bom is defined in the project. + * The Quarkus Platform Bom role is to define version to use for Quarkus extensions. + * + * @return true if it's defined, false else + * @throws IOException if a problem occurs while reading the project build file(s) + */ + boolean hasQuarkusPlatformBom() throws IOException; + + /** + * This is going to install/add all the specified extensions to the project build file(s). + * + *
+     *   - If the project Quarkus platform bom is not defined, an {@link IllegalStateException} will be thrown
+     *   - Extensions which are already installed will ALWAYS be skipped whatever the specified version
+     *   - The provided version will be used wasn't already installed
+     * 
+ * + * @param coords the list of {@link AppArtifactCoords} for the extensions to install + * @return the {@link InstallResult} + * @throws IOException if a problem occurs while reading/writing the project build file(s) + */ + InstallResult install(Collection coords) throws IOException; + + /** + * This is going to uninstall/remove all the specified extensions from the project build file(s). + * + * This is ignoring the {@link Extension} version + * + * @param keys the set of {@link AppArtifactKey} for the extensions to uninstall + * @return the {@link InstallResult} + * @throws IOException if a problem occurs while reading/writing the project build file(s) + */ + UninstallResult uninstall(Collection keys) throws IOException; + + class InstallResult { + private final int installed; + + public InstallResult(int installed) { + this.installed = installed; + } + + public int getInstalled() { + return installed; + } + + public boolean isSourceUpdated() { + return installed > 0; + } + } + + class UninstallResult { + private final int uninstalled; + + public UninstallResult(int uninstalled) { + this.uninstalled = uninstalled; + } + + public int getUninstalled() { + return uninstalled; + } + + public boolean isSourceUpdated() { + return uninstalled > 0; + } + + } + +} diff --git a/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/extensions/Extensions.java b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/extensions/Extensions.java new file mode 100644 index 0000000000000..e7da2430b173d --- /dev/null +++ b/independent-projects/tools/common/src/main/java/io/quarkus/devtools/project/extensions/Extensions.java @@ -0,0 +1,49 @@ +package io.quarkus.devtools.project.extensions; + +import io.quarkus.bootstrap.model.AppArtifactCoords; +import io.quarkus.bootstrap.model.AppArtifactKey; +import io.quarkus.dependencies.Extension; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import org.apache.maven.model.Dependency; + +public final class Extensions { + private Extensions() { + } + + public static AppArtifactKey toKey(final Extension extension) { + return toKey(extension.toDependency(false)); + } + + public static AppArtifactKey toKey(final Dependency dependency) { + return new AppArtifactKey(dependency.getGroupId(), dependency.getArtifactId(), dependency.getClassifier(), + dependency.getType()); + } + + public static Optional findInList(List list, final AppArtifactKey key) { + return list.stream().filter(e -> Objects.equals(toCoords(e).getKey(), key)).findFirst(); + } + + public static AppArtifactCoords toCoords(final Extension e) { + return toCoords(e.toDependency(false)); + } + + public static AppArtifactCoords toCoords(final Dependency d, final String overrideVersion) { + return overrideVersion(toCoords(d), overrideVersion); + } + + public static AppArtifactCoords stripVersion(final AppArtifactCoords coords) { + return overrideVersion(coords, null); + } + + public static AppArtifactCoords overrideVersion(final AppArtifactCoords coords, final String overrideVersion) { + return new AppArtifactCoords(coords.getGroupId(), coords.getArtifactId(), coords.getClassifier(), coords.getType(), + overrideVersion); + } + + public static AppArtifactCoords toCoords(final Dependency d) { + return new AppArtifactCoords(d.getGroupId(), d.getArtifactId(), d.getClassifier(), d.getType(), d.getVersion()); + } + +} diff --git a/independent-projects/tools/common/src/main/resources/META-INF/services/io.quarkus.devtools.project.codegen.ProjectGenerator b/independent-projects/tools/common/src/main/resources/META-INF/services/io.quarkus.devtools.project.codegen.ProjectGenerator new file mode 100644 index 0000000000000..41ba05fc1a3e4 --- /dev/null +++ b/independent-projects/tools/common/src/main/resources/META-INF/services/io.quarkus.devtools.project.codegen.ProjectGenerator @@ -0,0 +1 @@ +io.quarkus.devtools.project.codegen.rest.BasicRestProjectGenerator diff --git a/independent-projects/tools/common/src/main/resources/META-INF/services/io.quarkus.generators.ProjectGenerator b/independent-projects/tools/common/src/main/resources/META-INF/services/io.quarkus.generators.ProjectGenerator deleted file mode 100644 index 481f77c230b6b..0000000000000 --- a/independent-projects/tools/common/src/main/resources/META-INF/services/io.quarkus.generators.ProjectGenerator +++ /dev/null @@ -1 +0,0 @@ -io.quarkus.generators.rest.BasicRestProjectGenerator diff --git a/independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/CreateProjectTest.java b/independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/CreateProjectTest.java deleted file mode 100644 index 44cbd56da51ea..0000000000000 --- a/independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/CreateProjectTest.java +++ /dev/null @@ -1,314 +0,0 @@ -package io.quarkus.cli.commands; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.contentOf; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import io.quarkus.devtools.project.BuildTool; -import io.quarkus.devtools.writer.FileProjectWriter; -import io.quarkus.generators.ProjectGenerator; -import io.quarkus.maven.utilities.MojoUtils; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Comparator; -import java.util.List; -import java.util.Properties; -import java.util.concurrent.Callable; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.stream.Collectors; -import java.util.stream.IntStream; -import java.util.stream.Stream; -import org.apache.maven.model.Model; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; - -public class CreateProjectTest extends PlatformAwareTestBase { - @Test - public void create() throws Exception { - final File file = new File("target/basic-rest"); - delete(file); - createProject(file, "io.quarkus", "basic-rest", "1.0.0-SNAPSHOT"); - - final File gitignore = new File(file, ".gitignore"); - assertTrue(gitignore.exists()); - final String gitignoreContent = new String(Files.readAllBytes(gitignore.toPath()), StandardCharsets.UTF_8); - assertTrue(gitignoreContent.contains("\ntarget/\n")); - } - - @Test - public void createGradle() throws Exception { - final File file = new File("target/basic-rest-gradle"); - delete(file); - createProject(BuildTool.GRADLE, file, "io.quarkus", "basic-rest", "1.0.0-SNAPSHOT"); - - final File gitignore = new File(file, ".gitignore"); - assertTrue(gitignore.exists()); - final String gitignoreContent = new String(Files.readAllBytes(gitignore.toPath()), StandardCharsets.UTF_8); - Assertions.assertFalse(gitignoreContent.contains("\ntarget/\n")); - assertTrue(gitignoreContent.contains("\nbuild/")); - assertTrue(gitignoreContent.contains("\n.gradle/\n")); - - assertThat(new File(file, "README.md")).exists(); - assertThat(contentOf(new File(file, "README.md"), "UTF-8")).contains("./gradlew"); - } - - @Test - public void createGradleOnExisting() throws Exception { - final File testDir = new File("target/existing"); - delete(testDir); - testDir.mkdirs(); - - final File buildGradle = new File(testDir, "build.gradle"); - buildGradle.createNewFile(); - final File settingsGradle = new File(testDir, "settings.gradle"); - settingsGradle.createNewFile(); - - createProject(BuildTool.GRADLE, testDir, "io.quarkus", "basic-rest", "1.0.0-SNAPSHOT"); - - final File gitignore = new File(testDir, ".gitignore"); - assertTrue(gitignore.exists()); - final String gitignoreContent = new String(Files.readAllBytes(gitignore.toPath()), StandardCharsets.UTF_8); - Assertions.assertFalse(gitignoreContent.contains("\ntarget/\n")); - assertTrue(gitignoreContent.contains("\nbuild/")); - assertTrue(gitignoreContent.contains("\n.gradle/\n")); - - assertThat(contentOf(new File(testDir, "build.gradle"), "UTF-8")) - .contains("id 'io.quarkus'") - .contains("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}"); - - final Properties props = new Properties(); - try (InputStream is = Files.newInputStream(testDir.toPath().resolve("gradle.properties"))) { - props.load(is); - } - Assertions.assertEquals(getBomGroupId(), props.get("quarkusPlatformGroupId")); - Assertions.assertEquals(getBomArtifactId(), props.get("quarkusPlatformArtifactId")); - Assertions.assertEquals(getBomVersion(), props.get("quarkusPlatformVersion")); - - assertThat(new File(testDir, "README.md")).exists(); - assertThat(contentOf(new File(testDir, "README.md"), "UTF-8")).contains("./gradlew"); - } - - @Test - public void createOnTopPomWithoutResource() throws Exception { - final File testDir = new File("target/existing"); - delete(testDir); - testDir.mkdirs(); - - Model model = new Model(); - model.setModelVersion("4.0.0"); - model.setGroupId("org.acme"); - model.setArtifactId("foobar"); - model.setVersion("10.1.2"); - final File pom = new File(testDir, "pom.xml"); - MojoUtils.write(model, pom); - createProject(testDir, "something.is", "wrong", "1.0.0-SNAPSHOT"); - - assertThat(contentOf(pom, "UTF-8")) - .contains(getPluginArtifactId(), MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLUGIN_VERSION_VALUE, getPluginGroupId()); - - assertThat(new File(testDir, "src/main/java")).isDirectory(); - assertThat(new File(testDir, "src/test/java")).isDirectory(); - - assertThat(new File(testDir, "README.md")).exists(); - assertThat(contentOf(new File(testDir, "README.md"), "UTF-8")).contains("./mvnw"); - assertThat(new File(testDir, "src/main/resources/application.properties")).exists(); - assertThat(new File(testDir, "src/main/resources/META-INF/resources/index.html")).isFile(); - assertThat(new File(testDir, "src/main/java")).isDirectory().matches(f -> { - String[] list = f.list(); - return list != null && list.length == 0; - }); - assertThat(new File(testDir, "src/test/java")).isDirectory().matches(f -> { - String[] list = f.list(); - return list != null && list.length == 0; - }); - - assertThat(contentOf(new File(testDir, "pom.xml"), "UTF-8")) - .contains(MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLATFORM_ARTIFACT_ID_VALUE); - - } - - @Test - public void createOnTopPomWithResource() throws Exception { - final File testDir = new File("target/existing"); - delete(testDir); - testDir.mkdirs(); - - Model model = new Model(); - model.setModelVersion("4.0.0"); - model.setGroupId("org.acme"); - model.setArtifactId("foobar"); - model.setVersion("10.1.2"); - final File pom = new File(testDir, "pom.xml"); - MojoUtils.write(model, pom); - - final QuarkusCommandOutcome result = new CreateProject(testDir.toPath(), getPlatformDescriptor()) - .groupId("something.is") - .artifactId("wrong") - .version("1.0.0-SNAPSHOT") - .className("org.foo.MyResource") - .execute(); - assertTrue(result.isSuccess()); - - assertThat(contentOf(pom, "UTF-8")) - .contains(getPluginArtifactId(), MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLUGIN_VERSION_VALUE, getPluginGroupId()); - - assertThat(new File(testDir, "src/main/java")).isDirectory(); - assertThat(new File(testDir, "src/test/java")).isDirectory(); - - assertThat(new File(testDir, "README.md")).exists(); - assertThat(contentOf(new File(testDir, "README.md"), "UTF-8")).contains("./mvnw"); - assertThat(new File(testDir, "src/main/resources/application.properties")).exists(); - assertThat(new File(testDir, "src/main/resources/META-INF/resources/index.html")).exists(); - assertThat(new File(testDir, "src/main/java")).isDirectory(); - assertThat(new File(testDir, "src/main/java/org/foo/MyResource.java")).isFile(); - assertThat(contentOf(new File(testDir, "src/main/java/org/foo/MyResource.java"), "UTF-8")) - .contains("@Path", "@GET", "@Produces"); - assertThat(new File(testDir, "src/test/java")).isDirectory(); - assertThat(new File(testDir, "src/test/java/org/foo/MyResourceTest.java")).isFile(); - assertThat(new File(testDir, "src/test/java/org/foo/NativeMyResourceIT.java")).isFile(); - - assertThat(contentOf(new File(testDir, "pom.xml"))).contains(getBomArtifactId()); - - } - - @Test - public void createOnTopPomWithSpringController() throws Exception { - final File testDir = new File("target/existing"); - delete(testDir); - testDir.mkdirs(); - - Model model = new Model(); - model.setModelVersion("4.0.0"); - model.setGroupId("org.acme"); - model.setArtifactId("foobar"); - model.setVersion("10.1.2"); - final File pom = new File(testDir, "pom.xml"); - MojoUtils.write(model, pom); - - final QuarkusCommandOutcome result = new CreateProject(testDir.toPath(), getPlatformDescriptor()) - .groupId("something.is") - .artifactId("wrong") - .version("1.0.0-SNAPSHOT") - .className("org.foo.MyResource") - .setValue(ProjectGenerator.IS_SPRING, true) - .execute(); - assertTrue(result.isSuccess()); - - assertThat(contentOf(pom, "UTF-8")) - .contains(getPluginArtifactId(), MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLUGIN_VERSION_VALUE, getPluginGroupId()); - - assertThat(new File(testDir, "src/main/java")).isDirectory(); - assertThat(new File(testDir, "src/test/java")).isDirectory(); - - assertThat(new File(testDir, "README.md")).exists(); - assertThat(contentOf(new File(testDir, "README.md"), "UTF-8")).contains("./mvnw"); - assertThat(new File(testDir, "src/main/resources/application.properties")).exists(); - assertThat(new File(testDir, "src/main/resources/META-INF/resources/index.html")).exists(); - assertThat(new File(testDir, "src/main/java")).isDirectory(); - assertThat(new File(testDir, "src/main/java/org/foo/MyResource.java")).isFile(); - assertThat(contentOf(new File(testDir, "src/main/java/org/foo/MyResource.java"), "UTF-8")) - .contains("@RestController", "@RequestMapping", "@GetMapping"); - assertThat(new File(testDir, "src/test/java")).isDirectory(); - assertThat(new File(testDir, "src/test/java/org/foo/MyResourceTest.java")).isFile(); - assertThat(new File(testDir, "src/test/java/org/foo/NativeMyResourceIT.java")).isFile(); - - assertThat(contentOf(new File(testDir, "pom.xml"))).contains(getBomArtifactId()); - - } - - @Test - public void createNewWithCustomizations() throws Exception { - final File testDir = new File("target/existing"); - delete(testDir); - testDir.mkdirs(); - - final QuarkusCommandOutcome result = new CreateProject(testDir.toPath(), getPlatformDescriptor()) - .groupId("org.acme") - .artifactId("acme") - .version("1.0.0-SNAPSHOT") - .className("org.acme.MyResource") - .execute(); - assertTrue(result.isSuccess()); - - assertThat(new File(testDir, "pom.xml")).isFile(); - assertThat(new File(testDir, "src/main/java/org/acme/MyResource.java")).isFile(); - assertThat(new File(testDir, "src/main/java/org/acme/MyApplication.java")).doesNotExist(); - - assertThat(contentOf(new File(testDir, "pom.xml"), "UTF-8")) - .contains(getPluginArtifactId(), MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLUGIN_VERSION_VALUE, getPluginGroupId()); - - assertThat(new File(testDir, "src/main/java")).isDirectory(); - assertThat(new File(testDir, "src/test/java")).isDirectory(); - - assertThat(new File(testDir, "src/main/resources/application.properties")).exists(); - assertThat(new File(testDir, "src/main/resources/META-INF/resources/index.html")).exists(); - - assertThat(contentOf(new File(testDir, "pom.xml"), "UTF-8")) - .contains(MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLATFORM_VERSION_VALUE); - - } - - @Test - @Timeout(2) - @DisplayName("Should create correctly multiple times in parallel with multiple threads") - void createMultipleTimes() throws InterruptedException { - final ExecutorService executorService = Executors.newFixedThreadPool(4); - final CountDownLatch latch = new CountDownLatch(20); - - List> collect = IntStream.range(0, 20).boxed().map(i -> (Callable) () -> { - File tempDir = Files.createTempDirectory("test").toFile(); - FileProjectWriter write = new FileProjectWriter(tempDir); - final QuarkusCommandOutcome result = new CreateProject(tempDir.toPath(), getPlatformDescriptor()) - .groupId("org.acme") - .artifactId("acme") - .version("1.0.0-SNAPSHOT") - .className("org.acme.MyResource") - .execute(); - assertTrue(result.isSuccess()); - latch.countDown(); - write.close(); - tempDir.delete(); - return null; - }).collect(Collectors.toList()); - executorService.invokeAll(collect); - latch.await(); - } - - public static void delete(final File file) throws IOException { - - if (file.exists()) { - try (Stream stream = Files.walk(file.toPath())) { - stream.sorted(Comparator.reverseOrder()) - .map(Path::toFile) - .forEach(File::delete); - } - } - - Assertions.assertFalse( - Files.exists(file.toPath()), "Directory still exists"); - } - - private void createProject(final File file, String groupId, String artifactId, String version) - throws QuarkusCommandException { - createProject(BuildTool.MAVEN, file, groupId, artifactId, version); - } - - private void createProject(BuildTool buildTool, File file, String groupId, String artifactId, String version) - throws QuarkusCommandException { - final QuarkusCommandOutcome result = new CreateProject(file.toPath(), getPlatformDescriptor()) - .buildTool(buildTool) - .groupId(groupId) - .artifactId(artifactId) - .version(version) - .execute(); - assertTrue(result.isSuccess()); - } -} diff --git a/independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/AbstractAddExtensionsTest.java b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/AbstractAddExtensionsTest.java similarity index 93% rename from independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/AbstractAddExtensionsTest.java rename to independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/AbstractAddExtensionsTest.java index 2e38ffa5b54ca..a8aad92c648cd 100644 --- a/independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/AbstractAddExtensionsTest.java +++ b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/AbstractAddExtensionsTest.java @@ -1,7 +1,9 @@ -package io.quarkus.cli.commands; +package io.quarkus.devtools.commands; import static java.util.Arrays.asList; +import io.quarkus.devtools.commands.data.QuarkusCommandException; +import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; @@ -135,25 +137,25 @@ void testPartialMatchConflict() throws Exception { } @Test - void addExistingAndMissingExtensions() throws Exception { + void addExistingAndMissingExtensionsWillFailForBoth() throws Exception { createProject(); final QuarkusCommandOutcome result = addExtensions(asList("missing", "agroal")); final T project = readProject(); doesNotHaveDependency(project, "quarkus-missing"); - hasDependency(project, "quarkus-agroal"); + doesNotHaveDependency(project, "quarkus-agroal"); Assertions.assertFalse(result.isSuccess()); - Assertions.assertTrue(result.valueIs(AddExtensions.OUTCOME_UPDATED, true)); + Assertions.assertFalse(result.valueIs(AddExtensions.OUTCOME_UPDATED, true)); } @Test - void addDuplicatedExtension() throws Exception { + void doNotAddExtensionWhenMultipleMatch() throws Exception { createProject(); - addExtensions(asList("agroal", "jdbc", "non-exist-ent")); + addExtensions(asList("agroal", "jdbc")); final T project = readProject(); - hasDependency(project, "quarkus-agroal"); + doesNotHaveDependency(project, "quarkus-agroal"); doesNotHaveDependency(project, "quarkus-jdbc-postgresql"); doesNotHaveDependency(project, "quarkus-jdbc-h2"); } diff --git a/independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/AbstractRemoveExtensionsTest.java b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/AbstractRemoveExtensionsTest.java similarity index 93% rename from independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/AbstractRemoveExtensionsTest.java rename to independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/AbstractRemoveExtensionsTest.java index 1acfd547df718..9be4524fa0508 100644 --- a/independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/AbstractRemoveExtensionsTest.java +++ b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/AbstractRemoveExtensionsTest.java @@ -1,7 +1,9 @@ -package io.quarkus.cli.commands; +package io.quarkus.devtools.commands; import static java.util.Arrays.asList; +import io.quarkus.devtools.commands.data.QuarkusCommandException; +import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; @@ -64,12 +66,12 @@ void testRegexpMatches() throws Exception { final QuarkusCommandOutcome result = addExtensions(asList("Sm??lRye**")); Assertions.assertTrue(result.isSuccess()); - Assertions.assertTrue(result.valueIs(AddExtensions.OUTCOME_UPDATED, true)); + Assertions.assertTrue(result.getBooleanValue(AddExtensions.OUTCOME_UPDATED)); final QuarkusCommandOutcome result2 = removeExtensions(asList("Sm??lRye**")); Assertions.assertTrue(result2.isSuccess()); - Assertions.assertTrue(result2.valueIs(RemoveExtensions.OUTCOME_UPDATED, true)); + Assertions.assertTrue(result2.getBooleanValue(RemoveExtensions.OUTCOME_UPDATED)); final T project = readProject(); hasNoDependency(project, "quarkus-smallrye-reactive-messaging"); diff --git a/independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/AddGradleExtensionsTest.java b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/AddGradleExtensionsTest.java similarity index 53% rename from independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/AddGradleExtensionsTest.java rename to independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/AddGradleExtensionsTest.java index f080c5fffff4a..553fcb373760b 100644 --- a/independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/AddGradleExtensionsTest.java +++ b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/AddGradleExtensionsTest.java @@ -1,20 +1,23 @@ -package io.quarkus.cli.commands; +package io.quarkus.devtools.commands; +import com.google.common.collect.ImmutableList; +import io.quarkus.devtools.commands.data.QuarkusCommandException; +import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; import io.quarkus.devtools.project.BuildTool; import io.quarkus.devtools.project.QuarkusProject; +import io.quarkus.devtools.project.buildfile.AbstractGradleBuildFile; +import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; import java.io.IOException; import java.nio.file.Files; +import java.nio.file.Path; import java.util.HashSet; import java.util.List; -import org.junit.jupiter.api.Disabled; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.apache.maven.model.Dependency; class AddGradleExtensionsTest extends AbstractAddExtensionsTest> { - @Disabled - void addExtensionTwiceInTwoBatches() throws IOException { - //FIXME This is currently not working - } - @Override protected List createProject() throws IOException, QuarkusCommandException { CreateProjectTest.delete(getProjectPath().toFile()); @@ -53,6 +56,30 @@ private static String getBuildFileDependencyString(final String groupId, final S } private QuarkusProject getQuarkusProject() { - return QuarkusProject.of(getProjectPath(), getPlatformDescriptor(), BuildTool.GRADLE); + final Path projectPath = getProjectPath(); + final QuarkusPlatformDescriptor platformDescriptor = getPlatformDescriptor(); + return QuarkusProject.of(projectPath, platformDescriptor, new TestingGradleBuildFile(projectPath, platformDescriptor)); + } + + static class TestingGradleBuildFile extends AbstractGradleBuildFile { + + public TestingGradleBuildFile(Path projectFolderPath, QuarkusPlatformDescriptor platformDescriptor) { + super(projectFolderPath, platformDescriptor); + } + + @Override + protected List getDependencies() throws IOException { + final Matcher matcher = Pattern.compile("\\s*implementation\\s+'([^\\v:]+):([^\\v:]+)(:[^:\\v]+)?'") + .matcher(getBuildContent()); + final ImmutableList.Builder builder = ImmutableList.builder(); + while (matcher.find()) { + final Dependency dep = new Dependency(); + dep.setGroupId(matcher.group(1)); + dep.setArtifactId(matcher.group(2)); + dep.setVersion(matcher.group(3)); + builder.add(dep); + } + return builder.build(); + } } } diff --git a/independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/AddMavenExtensionsTest.java b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/AddMavenExtensionsTest.java similarity index 87% rename from independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/AddMavenExtensionsTest.java rename to independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/AddMavenExtensionsTest.java index 57ee06f705246..904777bf8917e 100644 --- a/independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/AddMavenExtensionsTest.java +++ b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/AddMavenExtensionsTest.java @@ -1,5 +1,7 @@ -package io.quarkus.cli.commands; +package io.quarkus.devtools.commands; +import io.quarkus.devtools.commands.data.QuarkusCommandException; +import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; import io.quarkus.devtools.project.BuildTool; import io.quarkus.devtools.project.QuarkusProject; import io.quarkus.maven.utilities.MojoUtils; @@ -46,6 +48,6 @@ protected long countDependencyOccurrences(final Model project, final String grou } private QuarkusProject getQuarkusProject() { - return QuarkusProject.of(getProjectPath(), getPlatformDescriptor(), BuildTool.MAVEN); + return QuarkusProject.maven(getProjectPath(), getPlatformDescriptor()); } } diff --git a/independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/CreateProjectTest.java b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/CreateProjectTest.java new file mode 100644 index 0000000000000..8bc7c11aea806 --- /dev/null +++ b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/CreateProjectTest.java @@ -0,0 +1,142 @@ +package io.quarkus.devtools.commands; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.contentOf; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import io.quarkus.devtools.commands.data.QuarkusCommandException; +import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; +import io.quarkus.devtools.project.BuildTool; +import io.quarkus.devtools.project.codegen.writer.FileProjectWriter; +import io.quarkus.maven.utilities.MojoUtils; +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Comparator; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; +import org.apache.maven.model.Model; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; + +public class CreateProjectTest extends PlatformAwareTestBase { + @Test + public void create() throws Exception { + final File file = new File("target/basic-rest"); + delete(file); + createProject(file, "io.quarkus", "basic-rest", "1.0.0-SNAPSHOT"); + + final File gitignore = new File(file, ".gitignore"); + assertTrue(gitignore.exists()); + final String gitignoreContent = new String(Files.readAllBytes(gitignore.toPath()), StandardCharsets.UTF_8); + assertTrue(gitignoreContent.contains("\ntarget/\n")); + } + + @Test + public void createGradle() throws Exception { + final File file = new File("target/basic-rest-gradle"); + delete(file); + createProject(BuildTool.GRADLE, file, "io.quarkus", "basic-rest", "1.0.0-SNAPSHOT"); + + final File gitignore = new File(file, ".gitignore"); + assertTrue(gitignore.exists()); + final String gitignoreContent = new String(Files.readAllBytes(gitignore.toPath()), StandardCharsets.UTF_8); + Assertions.assertFalse(gitignoreContent.contains("\ntarget/\n")); + assertTrue(gitignoreContent.contains("\nbuild/")); + assertTrue(gitignoreContent.contains("\n.gradle/\n")); + + assertThat(new File(file, "README.md")).exists(); + assertThat(contentOf(new File(file, "README.md"), "UTF-8")).contains("./gradlew"); + } + + @Test + public void createOnTopOfExisting() throws Exception { + final File testDir = new File("target/existing"); + delete(testDir); + testDir.mkdirs(); + + Model model = new Model(); + model.setModelVersion("4.0.0"); + model.setGroupId("org.acme"); + model.setArtifactId("foobar"); + model.setVersion("10.1.2"); + final File pom = new File(testDir, "pom.xml"); + MojoUtils.write(model, pom); + assertThatExceptionOfType(QuarkusCommandException.class).isThrownBy(() -> { + new CreateProject(testDir.toPath(), getPlatformDescriptor()) + .groupId("something.is") + .artifactId("wrong") + .version("1.0.0-SNAPSHOT") + .className("org.foo.MyResource") + .execute(); + }).withRootCauseInstanceOf(IOException.class); + } + + @Test + @Timeout(2) + @DisplayName("Should create correctly multiple times in parallel with multiple threads") + void createMultipleTimes() throws InterruptedException { + final ExecutorService executorService = Executors.newFixedThreadPool(4); + final CountDownLatch latch = new CountDownLatch(20); + + List> collect = IntStream.range(0, 20).boxed().map(i -> (Callable) () -> { + File tempDir = Files.createTempDirectory("test").toFile(); + FileProjectWriter write = new FileProjectWriter(tempDir); + final QuarkusCommandOutcome result = new CreateProject(tempDir.toPath(), getPlatformDescriptor()) + .groupId("org.acme") + .artifactId("acme") + .version("1.0.0-SNAPSHOT") + .className("org.acme.MyResource") + .execute(); + assertTrue(result.isSuccess()); + latch.countDown(); + write.close(); + tempDir.delete(); + return null; + }).collect(Collectors.toList()); + executorService.invokeAll(collect); + latch.await(); + } + + public static void delete(final File file) throws IOException { + + if (file.exists()) { + try (Stream stream = Files.walk(file.toPath())) { + stream.sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + } + } + + Assertions.assertFalse( + Files.exists(file.toPath()), "Directory still exists"); + } + + private void createProject(final File file, String groupId, String artifactId, String version) + throws QuarkusCommandException { + createProject(BuildTool.MAVEN, file, groupId, artifactId, version); + } + + private void createProject(BuildTool buildTool, File file, String groupId, String artifactId, String version) + throws QuarkusCommandException { + final QuarkusCommandOutcome result = new CreateProject(file.toPath(), getPlatformDescriptor()) + .buildTool(buildTool) + .groupId(groupId) + .artifactId(artifactId) + .version(version) + .execute(); + assertTrue(result.isSuccess()); + } +} diff --git a/independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/ListExtensionsTest.java b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/ListExtensionsTest.java similarity index 72% rename from independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/ListExtensionsTest.java rename to independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/ListExtensionsTest.java index cd039e61c49a6..f2d71ddb0c717 100644 --- a/independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/ListExtensionsTest.java +++ b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/ListExtensionsTest.java @@ -1,11 +1,14 @@ -package io.quarkus.cli.commands; +package io.quarkus.devtools.commands; import static io.quarkus.maven.utilities.MojoUtils.readPom; import static java.util.Arrays.asList; -import static org.assertj.core.api.Assertions.assertThat; +import static java.util.stream.Collectors.toMap; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.junit.jupiter.api.Assertions.assertTrue; -import io.quarkus.devtools.project.BuildTool; +import io.quarkus.bootstrap.model.AppArtifactCoords; +import io.quarkus.bootstrap.model.AppArtifactKey; +import io.quarkus.devtools.commands.data.QuarkusCommandException; import io.quarkus.devtools.project.QuarkusProject; import io.quarkus.maven.utilities.MojoUtils; import io.quarkus.maven.utilities.QuarkusDependencyPredicate; @@ -13,11 +16,13 @@ import java.io.File; import java.io.IOException; import java.io.PrintStream; +import java.io.UncheckedIOException; import java.nio.file.Files; +import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.util.HashSet; import java.util.Map; -import org.apache.maven.model.Dependency; +import java.util.function.Function; import org.apache.maven.model.Model; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -31,9 +36,9 @@ public void listWithBom() throws Exception { final ListExtensions listExtensions = new ListExtensions(project); - final Map installed = listExtensions.findInstalled(); + final Map installed = readByKey(project); - Assertions.assertNotNull(installed.get(getPluginGroupId() + ":quarkus-agroal")); + Assertions.assertNotNull(installed.get(AppArtifactKey.fromString(getPluginGroupId() + ":quarkus-agroal"))); } /** @@ -49,27 +54,26 @@ public void listWithBomExtensionWithSpaces() throws Exception { final ListExtensions listExtensions = new ListExtensions(quarkusProject); - final Map installed = listExtensions.findInstalled(); + final Map installed = readByKey(quarkusProject); - Assertions.assertNotNull(installed.get(getPluginGroupId() + ":quarkus-resteasy")); - Assertions.assertNotNull(installed.get(getPluginGroupId() + ":quarkus-hibernate-validator")); + Assertions.assertNotNull(installed.get(AppArtifactKey.fromString(getPluginGroupId() + ":quarkus-resteasy"))); + Assertions.assertNotNull(installed.get(AppArtifactKey.fromString(getPluginGroupId() + ":quarkus-hibernate-validator"))); } @Test public void listWithoutBom() throws Exception { final File pom = new File("target/list-extensions-test", "pom.xml"); final QuarkusProject quarkusProject = createNewProject(pom); - Model model = readPom(pom); - model.setDependencyManagement(null); model.getDependencies().stream() .filter(new QuarkusDependencyPredicate()) .forEach(d -> d.setVersion("0.0.1")); MojoUtils.write(model, pom); - addExtensions(quarkusProject, "commons-io:commons-io:2.5", "Agroal"); + addExtensions(quarkusProject, "commons-io:commons-io:2.5", "Agroal", + "io.quarkus:quarkus-hibernate-orm-panache:" + getPluginVersion()); model = readPom(pom); @@ -86,21 +90,28 @@ public void listWithoutBom() throws Exception { } boolean agroal = false; boolean resteasy = false; + boolean panache = false; boolean hibernateValidator = false; final String output = baos.toString("UTF-8"); boolean checkGuideInLineAfter = false; for (String line : output.split("\r?\n")) { if (line.contains(" Agroal ")) { - assertTrue(line.startsWith("current"), "Agroal should list as current: " + line); + assertTrue(line.startsWith("default"), "Agroal should list as being default: " + line); agroal = true; } else if (line.contains(" RESTEasy ")) { - assertTrue(line.startsWith("update"), "RESTEasy should list as having an update: " + line); + assertTrue(line.startsWith("custom*"), "RESTEasy should list as being custom*: " + line); assertTrue( line.endsWith( - String.format("%-16s", getPluginVersion())), - "RESTEasy should list as having an update: " + line); + String.format("%-15s", getPluginVersion())), + "RESTEasy should list as being custom*: " + line); resteasy = true; checkGuideInLineAfter = true; + } else if (line.contains("quarkus-hibernate-orm-panache")) { + assertTrue(line.startsWith("custom"), "Panache should list as being custom: " + line); + assertTrue( + line.endsWith(String.format("%-25s", getPluginVersion())), + "Panache should list as being custom*: " + line); + panache = true; } else if (line.contains(" Hibernate Validator ")) { assertTrue(line.startsWith(" "), "Hibernate Validator should not list as anything: " + line); hibernateValidator = true; @@ -113,7 +124,7 @@ public void listWithoutBom() throws Exception { } } - assertTrue(agroal && resteasy && hibernateValidator); + assertTrue(agroal && resteasy && hibernateValidator && panache); } @Test @@ -162,9 +173,9 @@ public void searchRest() throws Exception { @Test void testListExtensionsWithoutAPomFile() throws IOException { final Path tempDirectory = Files.createTempDirectory("proj"); - ListExtensions listExtensions = new ListExtensions( - QuarkusProject.of(tempDirectory, getPlatformDescriptor(), BuildTool.MAVEN)); - assertThat(listExtensions.findInstalled()).isEmpty(); + final QuarkusProject project = QuarkusProject.maven(tempDirectory, getPlatformDescriptor()); + assertThatExceptionOfType(UncheckedIOException.class).isThrownBy(() -> readByKey(project)) + .withRootCauseInstanceOf(NoSuchFileException.class); } private void addExtensions(QuarkusProject quarkusProject, String... extensions) throws Exception { @@ -181,6 +192,11 @@ private QuarkusProject createNewProject(final File pom) throws IOException, Quar .artifactId("add-extension-test") .version("0.0.1-SNAPSHOT") .execute(); - return QuarkusProject.of(projectFolderPath, getPlatformDescriptor(), BuildTool.MAVEN); + return QuarkusProject.maven(projectFolderPath, getPlatformDescriptor()); + } + + private static Map readByKey(QuarkusProject project) throws IOException { + return project.getExtensionManager().getInstalled().stream() + .collect(toMap(AppArtifactCoords::getKey, Function.identity())); } } diff --git a/independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/PlatformAwareTestBase.java b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/PlatformAwareTestBase.java similarity index 98% rename from independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/PlatformAwareTestBase.java rename to independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/PlatformAwareTestBase.java index d9a9541f65528..33b1278cd484d 100644 --- a/independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/PlatformAwareTestBase.java +++ b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/PlatformAwareTestBase.java @@ -1,4 +1,4 @@ -package io.quarkus.cli.commands; +package io.quarkus.devtools.commands; import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; import io.quarkus.platform.tools.config.QuarkusPlatformConfig; diff --git a/independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/RemoveGradleExtensionsTest.java b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/RemoveGradleExtensionsTest.java similarity index 80% rename from independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/RemoveGradleExtensionsTest.java rename to independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/RemoveGradleExtensionsTest.java index 2348a72ba3361..963af786163e6 100644 --- a/independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/RemoveGradleExtensionsTest.java +++ b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/RemoveGradleExtensionsTest.java @@ -1,9 +1,13 @@ -package io.quarkus.cli.commands; +package io.quarkus.devtools.commands; +import io.quarkus.devtools.commands.data.QuarkusCommandException; +import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; import io.quarkus.devtools.project.BuildTool; import io.quarkus.devtools.project.QuarkusProject; +import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; import java.io.IOException; import java.nio.file.Files; +import java.nio.file.Path; import java.util.HashSet; import java.util.List; import org.junit.jupiter.api.Disabled; @@ -56,7 +60,10 @@ protected long countDependencyOccurrences(final List buildFile, final St } private QuarkusProject getQuarkusProject() { - return QuarkusProject.of(getProjectPath(), getPlatformDescriptor(), BuildTool.GRADLE); + final Path projectPath = getProjectPath(); + final QuarkusPlatformDescriptor platformDescriptor = getPlatformDescriptor(); + return QuarkusProject.of(projectPath, platformDescriptor, + new AddGradleExtensionsTest.TestingGradleBuildFile(projectPath, platformDescriptor)); } private static String getBuildFileDependencyString(final String groupId, final String artifactId, final String version) { diff --git a/independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/RemoveMavenExtensionsTest.java b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/RemoveMavenExtensionsTest.java similarity index 88% rename from independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/RemoveMavenExtensionsTest.java rename to independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/RemoveMavenExtensionsTest.java index 57971f118a4ed..febaba8565c34 100644 --- a/independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/RemoveMavenExtensionsTest.java +++ b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/RemoveMavenExtensionsTest.java @@ -1,6 +1,7 @@ -package io.quarkus.cli.commands; +package io.quarkus.devtools.commands; -import io.quarkus.devtools.project.BuildTool; +import io.quarkus.devtools.commands.data.QuarkusCommandException; +import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; import io.quarkus.devtools.project.QuarkusProject; import io.quarkus.maven.utilities.MojoUtils; import java.io.File; @@ -52,6 +53,6 @@ protected long countDependencyOccurrences(final Model project, final String grou } private QuarkusProject getQuarkusProject() { - return QuarkusProject.of(getProjectPath(), getPlatformDescriptor(), BuildTool.MAVEN); + return QuarkusProject.maven(getProjectPath(), getPlatformDescriptor()); } } diff --git a/independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/AddExtensionsSelectTest.java b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/handlers/QuarkusCommandHandlersTest.java similarity index 86% rename from independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/AddExtensionsSelectTest.java rename to independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/handlers/QuarkusCommandHandlersTest.java index 633d796491b68..ab9135375aa1c 100644 --- a/independent-projects/tools/common/src/test/java/io/quarkus/cli/commands/AddExtensionsSelectTest.java +++ b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/commands/handlers/QuarkusCommandHandlersTest.java @@ -1,14 +1,16 @@ -package io.quarkus.cli.commands; +package io.quarkus.devtools.commands.handlers; +import static io.quarkus.devtools.commands.handlers.QuarkusCommandHandlers.select; import static java.util.Arrays.asList; import io.quarkus.dependencies.Extension; +import io.quarkus.devtools.commands.data.SelectionResult; import java.util.Collections; import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -class AddExtensionsSelectTest { +class QuarkusCommandHandlersTest { @Test void testMultiMatchByLabels() { @@ -24,11 +26,11 @@ void testMultiMatchByLabels() { List extensions = asList(e1, e2, e3); Collections.shuffle(extensions); - SelectionResult matches = AddExtensionsCommandHandler.select("foo", extensions, true); + SelectionResult matches = select("foo", extensions, true); Assertions.assertFalse(matches.matches()); Assertions.assertEquals(2, matches.getExtensions().size()); - matches = AddExtensionsCommandHandler.select("foo", extensions, false); + matches = select("foo", extensions, false); Assertions.assertFalse(matches.matches()); Assertions.assertEquals(0, matches.getExtensions().size()); } @@ -44,7 +46,7 @@ void testThatSingleLabelMatchIsNotAMatch() { List extensions = asList(e1, e2); Collections.shuffle(extensions); - SelectionResult matches = AddExtensionsCommandHandler.select("foo", extensions, true); + SelectionResult matches = select("foo", extensions, true); Assertions.assertFalse(matches.matches()); Assertions.assertEquals(1, matches.getExtensions().size()); } @@ -63,11 +65,11 @@ void testMultiMatchByArtifactIdsAndNames() { List extensions = asList(e1, e2, e3); Collections.shuffle(extensions); - SelectionResult matches = AddExtensionsCommandHandler.select("foo", extensions, false); + SelectionResult matches = select("foo", extensions, false); Assertions.assertFalse(matches.matches()); Assertions.assertEquals(2, matches.getExtensions().size()); - matches = AddExtensionsCommandHandler.select("foo", extensions, true); + matches = select("foo", extensions, true); Assertions.assertFalse(matches.matches()); Assertions.assertEquals(3, matches.getExtensions().size()); @@ -88,7 +90,7 @@ void testShortNameSelection() { List extensions = asList(e1, e2, e3); Collections.shuffle(extensions); - SelectionResult matches = AddExtensionsCommandHandler.select("foo", extensions, false); + SelectionResult matches = select("foo", extensions, false); Assertions.assertTrue(matches.matches()); Assertions.assertEquals(1, matches.getExtensions().size()); Assertions.assertTrue(matches.iterator().hasNext()); @@ -111,7 +113,7 @@ void testArtifactIdSelectionWithQuarkusPrefix() { List extensions = asList(e1, e2, e3); Collections.shuffle(extensions); - SelectionResult matches = AddExtensionsCommandHandler.select("foo", extensions, false); + SelectionResult matches = select("foo", extensions, false); Assertions.assertEquals(1, matches.getExtensions().size()); Assertions.assertTrue(matches.iterator().hasNext()); Assertions.assertTrue(matches.iterator().next().getArtifactId().equalsIgnoreCase("quarkus-foo")); @@ -133,12 +135,11 @@ void testListedVsUnlisted() { List extensions = asList(e1, e2, e3); Collections.shuffle(extensions); - SelectionResult matches = AddExtensionsCommandHandler.select("quarkus-foo", extensions, true); + SelectionResult matches = select("quarkus-foo", extensions, true); Assertions.assertEquals(2, matches.getExtensions().size()); - matches = AddExtensionsCommandHandler.select("quarkus-foo-unlisted", extensions, true); + matches = select("quarkus-foo-unlisted", extensions, true); Assertions.assertEquals(1, matches.getExtensions().size()); } - } diff --git a/independent-projects/tools/common/src/test/java/io/quarkus/generators/rest/BasicRestProjectGeneratorTest.java b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/project/codegen/rest/BasicRestProjectGeneratorTest.java similarity index 84% rename from independent-projects/tools/common/src/test/java/io/quarkus/generators/rest/BasicRestProjectGeneratorTest.java rename to independent-projects/tools/common/src/test/java/io/quarkus/devtools/project/codegen/rest/BasicRestProjectGeneratorTest.java index 1ca8d1441dc64..2e3255c76aff8 100644 --- a/independent-projects/tools/common/src/test/java/io/quarkus/generators/rest/BasicRestProjectGeneratorTest.java +++ b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/project/codegen/rest/BasicRestProjectGeneratorTest.java @@ -1,13 +1,13 @@ -package io.quarkus.generators.rest; - -import static io.quarkus.generators.ProjectGenerator.BOM_VERSION; -import static io.quarkus.generators.ProjectGenerator.CLASS_NAME; -import static io.quarkus.generators.ProjectGenerator.IS_SPRING; -import static io.quarkus.generators.ProjectGenerator.PACKAGE_NAME; -import static io.quarkus.generators.ProjectGenerator.PROJECT_ARTIFACT_ID; -import static io.quarkus.generators.ProjectGenerator.PROJECT_GROUP_ID; -import static io.quarkus.generators.ProjectGenerator.PROJECT_VERSION; -import static io.quarkus.generators.ProjectGenerator.SOURCE_TYPE; +package io.quarkus.devtools.project.codegen.rest; + +import static io.quarkus.devtools.project.codegen.ProjectGenerator.BOM_VERSION; +import static io.quarkus.devtools.project.codegen.ProjectGenerator.CLASS_NAME; +import static io.quarkus.devtools.project.codegen.ProjectGenerator.IS_SPRING; +import static io.quarkus.devtools.project.codegen.ProjectGenerator.PACKAGE_NAME; +import static io.quarkus.devtools.project.codegen.ProjectGenerator.PROJECT_ARTIFACT_ID; +import static io.quarkus.devtools.project.codegen.ProjectGenerator.PROJECT_GROUP_ID; +import static io.quarkus.devtools.project.codegen.ProjectGenerator.PROJECT_VERSION; +import static io.quarkus.devtools.project.codegen.ProjectGenerator.SOURCE_TYPE; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; @@ -19,13 +19,11 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import io.quarkus.bootstrap.util.IoUtils; -import io.quarkus.cli.commands.PlatformAwareTestBase; -import io.quarkus.cli.commands.QuarkusCommandInvocation; -import io.quarkus.devtools.project.BuildTool; +import io.quarkus.devtools.commands.PlatformAwareTestBase; +import io.quarkus.devtools.commands.data.QuarkusCommandInvocation; import io.quarkus.devtools.project.QuarkusProject; -import io.quarkus.devtools.writer.FileProjectWriter; -import io.quarkus.devtools.writer.ProjectWriter; -import io.quarkus.generators.SourceType; +import io.quarkus.devtools.project.codegen.SourceType; +import io.quarkus.devtools.project.codegen.writer.ProjectWriter; import io.quarkus.maven.utilities.MojoUtils; import java.nio.file.Files; import java.nio.file.Path; @@ -63,9 +61,8 @@ void generateMultipleTimes() throws InterruptedException { final BasicRestProjectGenerator basicRestProjectGenerator = new BasicRestProjectGenerator(); List> collect = IntStream.range(0, 20).boxed().map(i -> (Callable) () -> { final Path path = Files.createTempDirectory("test"); - try (FileProjectWriter writer = new FileProjectWriter(path.toFile())) { - basicRestProjectGenerator.generate(writer, - createQuarkusCommandInvocation(path)); + try { + basicRestProjectGenerator.generate(createQuarkusCommandInvocation(path)); } finally { IoUtils.recursiveDelete(path); } @@ -138,7 +135,7 @@ void generateFilesWithSpringControllerResource() throws Exception { } private QuarkusCommandInvocation createQuarkusCommandInvocation(Path projectPath) { - return new QuarkusCommandInvocation(QuarkusProject.of(projectPath, getPlatformDescriptor(), BuildTool.MAVEN), + return new QuarkusCommandInvocation(QuarkusProject.maven(projectPath, getPlatformDescriptor()), BASIC_PROJECT_CONTEXT); } diff --git a/independent-projects/tools/common/src/test/java/io/quarkus/devtools/project/compress/QuarkusProjectCompressTest.java b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/project/compress/QuarkusProjectCompressTest.java index 33b014420a00c..4b522f93f335f 100644 --- a/independent-projects/tools/common/src/test/java/io/quarkus/devtools/project/compress/QuarkusProjectCompressTest.java +++ b/independent-projects/tools/common/src/test/java/io/quarkus/devtools/project/compress/QuarkusProjectCompressTest.java @@ -6,11 +6,11 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import io.quarkus.cli.commands.CreateProject; -import io.quarkus.cli.commands.CreateProjectTest; -import io.quarkus.cli.commands.PlatformAwareTestBase; -import io.quarkus.cli.commands.QuarkusCommandException; -import io.quarkus.cli.commands.QuarkusCommandOutcome; +import io.quarkus.devtools.commands.CreateProject; +import io.quarkus.devtools.commands.CreateProjectTest; +import io.quarkus.devtools.commands.PlatformAwareTestBase; +import io.quarkus.devtools.commands.data.QuarkusCommandException; +import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; import io.quarkus.devtools.project.QuarkusProject; import java.io.File; import java.io.FileInputStream; diff --git a/independent-projects/tools/common/src/test/java/io/quarkus/test/platform/descriptor/CombinedQuarkusPlatformDescriptorTest.java b/independent-projects/tools/common/src/test/java/io/quarkus/test/platform/descriptor/CombinedQuarkusPlatformDescriptorTest.java index a0c449225fd1e..f9da268b93ccc 100644 --- a/independent-projects/tools/common/src/test/java/io/quarkus/test/platform/descriptor/CombinedQuarkusPlatformDescriptorTest.java +++ b/independent-projects/tools/common/src/test/java/io/quarkus/test/platform/descriptor/CombinedQuarkusPlatformDescriptorTest.java @@ -4,9 +4,9 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; -import io.quarkus.cli.commands.PlatformAwareTestBase; import io.quarkus.dependencies.Category; import io.quarkus.dependencies.Extension; +import io.quarkus.devtools.commands.PlatformAwareTestBase; import io.quarkus.platform.descriptor.CombinedQuarkusPlatformDescriptor; import io.quarkus.platform.descriptor.QuarkusPlatformDescriptor; import java.util.List; diff --git a/independent-projects/tools/platform-descriptor-api/src/main/java/io/quarkus/dependencies/Extension.java b/independent-projects/tools/platform-descriptor-api/src/main/java/io/quarkus/dependencies/Extension.java index 7ea3f369ac47c..1c3eda6389a5c 100644 --- a/independent-projects/tools/platform-descriptor-api/src/main/java/io/quarkus/dependencies/Extension.java +++ b/independent-projects/tools/platform-descriptor-api/src/main/java/io/quarkus/dependencies/Extension.java @@ -195,6 +195,9 @@ public Dependency toDependency(boolean stripVersion) { if (version != null && !version.isEmpty() && !stripVersion) { dependency.setVersion(version); } + if (type != null && !type.isEmpty()) { + dependency.setType(type); + } return dependency; } diff --git a/independent-projects/tools/platform-descriptor-api/src/main/java/io/quarkus/platform/tools/ConsoleMessageFormats.java b/independent-projects/tools/platform-descriptor-api/src/main/java/io/quarkus/platform/tools/ConsoleMessageFormats.java new file mode 100644 index 0000000000000..bbe5696f220cd --- /dev/null +++ b/independent-projects/tools/platform-descriptor-api/src/main/java/io/quarkus/platform/tools/ConsoleMessageFormats.java @@ -0,0 +1,23 @@ +package io.quarkus.platform.tools; + +public final class ConsoleMessageFormats { + + private static final String OK = "\u2705"; + private static final String NOK = "\u274c"; + private static final String NOOP = "\uD83D\uDC4D"; + + private ConsoleMessageFormats() { + } + + public static String nok(String content) { + return NOK + content; + } + + public static String ok(String content) { + return OK + content; + } + + public static String noop(String content) { + return NOOP + content; + } +} diff --git a/integration-tests/maven/src/test/java/io/quarkus/maven/it/CreateProjectMojoIT.java b/integration-tests/maven/src/test/java/io/quarkus/maven/it/CreateProjectMojoIT.java index 34a508677be72..08276e6099fbf 100644 --- a/integration-tests/maven/src/test/java/io/quarkus/maven/it/CreateProjectMojoIT.java +++ b/integration-tests/maven/src/test/java/io/quarkus/maven/it/CreateProjectMojoIT.java @@ -111,40 +111,13 @@ public void testProjectGenerationFromScratch() throws MavenInvocationException, } @Test - public void testProjectGenerationFromEmptyPom() throws Exception { + public void testProjectGenerationWithExistingPomShouldFail() throws Exception { testDir = initProject("projects/simple-pom-it", "projects/project-generation-from-empty-pom"); assertThat(testDir).isDirectory(); invoker = initInvoker(testDir); InvocationResult result = setup(new Properties()); - assertThat(result.getExitCode()).isZero(); - - assertThat(new File(testDir, "pom.xml")).isFile(); - assertThat(new File(testDir, "src/main/java")).isDirectory(); - - assertThat(new File(testDir, "src/main/resources/application.properties")).exists(); - assertThat(new File(testDir, "src/main/resources/META-INF/resources/index.html")).exists(); - - assertThat(FileUtils.readFileToString(new File(testDir, "pom.xml"), "UTF-8")) - .contains(getPluginArtifactId(), MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLUGIN_VERSION_VALUE, - getPluginGroupId()); - - final Model model = loadPom(testDir); - assertThat(model.getProperties().getProperty(MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLUGIN_VERSION_NAME)) - .isEqualTo(getPluginVersion()); - assertThat(model.getProperties().getProperty(MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLATFORM_ARTIFACT_ID_NAME)) - .isEqualTo(getBomArtifactId()); - - assertThat(model.getDependencyManagement().getDependencies().stream() - .anyMatch(d -> d.getArtifactId().equals(MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLATFORM_ARTIFACT_ID_VALUE) - && d.getVersion().equals(MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLATFORM_VERSION_VALUE) - && d.getScope().equals("import") - && d.getType().equals("pom"))).isTrue(); - - assertThat(model.getDependencies()).isEmpty(); - - assertThat(model.getProfiles()).hasSize(1); - assertThat(model.getProfiles().get(0).getId()).isEqualTo("native"); + assertThat(result.getExitCode()).isOne(); } @Test @@ -188,22 +161,19 @@ public void testProjectGenerationWithInvalidPackage() throws Exception { } @Test - public void testProjectGenerationFromMinimalPomWithResource() throws Exception { - testDir = initProject("projects/simple-pom-it", "projects/project-generation-from-empty-pom-with-resource"); + public void testProjectGenerationFromScratchWithMissingExtensionShouldFail() throws Exception { + testDir = initEmptyProject("projects/project-generation-with-missing-extension"); assertThat(testDir).isDirectory(); invoker = initInvoker(testDir); Properties properties = new Properties(); - properties.put("className", "org.acme.MyResource.java"); + properties.put("projectGroupId", "org.acme"); + properties.put("projectArtifactId", "acme"); + properties.put("className", "org.acme.MyResource"); + properties.put("extensions", "resteasy,smallrye-metrics,missing"); InvocationResult result = setup(properties); assertThat(result.getExitCode()).isZero(); - - check(new File(testDir, "pom.xml"), MojoUtils.TEMPLATE_PROPERTY_QUARKUS_PLATFORM_VERSION_NAME); - - assertThat(new File(testDir, "src/main/java")).isDirectory(); - - check(new File(testDir, "src/main/java/org/acme/MyResource.java"), "package org.acme;"); } @Test @@ -216,10 +186,10 @@ public void testProjectGenerationFromScratchWithExtensions() throws Exception { properties.put("projectGroupId", "org.acme"); properties.put("projectArtifactId", "acme"); properties.put("className", "org.acme.MyResource"); - properties.put("extensions", "resteasy,smallrye-metrics,missing"); + properties.put("extensions", "resteasy,smallrye-metrics"); InvocationResult result = setup(properties); - assertThat(result.getExitCode()).isNotZero(); // due to 'missing' extension + assertThat(result.getExitCode()).isZero(); // As the directory is not empty (log) navigate to the artifactID directory testDir = new File(testDir, "acme"); @@ -314,23 +284,6 @@ public void testProjectGenerationFromScratchWithCustomDependencies() throws Exce && d.getVersion().equalsIgnoreCase("2.5"))).isTrue(); } - @Test - public void testProjectGenerationFromMinimalPomWithDependencies() throws Exception { - testDir = initProject("projects/simple-pom-it", - "projects/project-generation-from-minimal-pom-with-extensions"); - assertThat(testDir).isDirectory(); - invoker = initInvoker(testDir); - - Properties properties = new Properties(); - properties.put("className", "org.acme.MyResource"); - properties.put("extensions", "commons-io:commons-io:2.5"); - InvocationResult result = setup(properties); - - assertThat(result.getExitCode()).isZero(); - check(new File(testDir, "src/main/java/org/acme/MyResource.java"), "package org.acme;"); - check(new File(testDir, "pom.xml"), "commons-io"); - } - /** * Reproducer for https://github.com/quarkusio/quarkus/issues/671 */ @@ -351,23 +304,6 @@ public void testThatDefaultPackageAreReplaced() throws Exception { "package org.acme.quarkus.sample;"); } - /** - * Reproducer for https://github.com/quarkusio/quarkus/issues/673 - */ - @Test - public void testThatGenerationFailedWhenTheUserPassGAVonExistingPom() throws Exception { - testDir = initProject("projects/simple-pom-it", "projects/fail-on-gav-and-existing-pom"); - assertThat(testDir).isDirectory(); - invoker = initInvoker(testDir); - - Properties properties = new Properties(); - properties.put("projectGroupId", "org.acme"); - properties.put("className", "MyResource"); - InvocationResult result = setup(properties); - assertThat(result.getExitCode()).isNotZero(); - assertThat(new File(testDir, "src/main/java/org/acme/MyResource.java")).doesNotExist(); - } - private void check(final File resource, final String contentsToFind) throws IOException { assertThat(resource).isFile(); assertThat(FileUtils.readFileToString(resource, "UTF-8")).contains(contentsToFind); diff --git a/integration-tests/maven/src/test/java/io/quarkus/maven/it/SetupIT.java b/integration-tests/maven/src/test/java/io/quarkus/maven/it/SetupIT.java deleted file mode 100644 index d832d3ab6f9c4..0000000000000 --- a/integration-tests/maven/src/test/java/io/quarkus/maven/it/SetupIT.java +++ /dev/null @@ -1,103 +0,0 @@ -package io.quarkus.maven.it; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.PrintStream; -import java.io.UnsupportedEncodingException; -import java.util.Collections; -import java.util.Properties; - -import org.apache.maven.shared.invoker.DefaultInvocationRequest; -import org.apache.maven.shared.invoker.InvocationRequest; -import org.apache.maven.shared.invoker.InvocationResult; -import org.apache.maven.shared.invoker.Invoker; -import org.apache.maven.shared.invoker.InvokerLogger; -import org.apache.maven.shared.invoker.MavenInvocationException; -import org.apache.maven.shared.invoker.PrintStreamLogger; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Test; - -import io.quarkus.maven.it.assertions.SetupVerifier; -import io.quarkus.maven.it.verifier.RunningInvoker; -import io.quarkus.platform.tools.ToolsConstants; - -@DisableForNative -public class SetupIT extends QuarkusPlatformAwareMojoTestBase { - - private Invoker invoker; - private RunningInvoker running; - private File testDir; - - @Test - public void testSetupOnExistingPom() throws Exception { - testDir = new File("target/test-classes", "projects/setup-on-existing-pom"); - assertThat(testDir).isDirectory(); - invoker = initInvoker(testDir); - - setup(new Properties()); - - File pomFile = new File(testDir, "pom.xml"); - assertThat(pomFile).isFile(); - SetupVerifier.verifySetup(pomFile); - } - - @Test - public void testSetupOnMinPom() throws Exception { - testDir = new File("target/test-classes", "projects/setup-on-min-pom"); - assertThat(testDir).isDirectory(); - invoker = initInvoker(testDir); - - setup(new Properties()); - - File pomFile = new File(testDir, "pom.xml"); - assertThat(pomFile).isFile(); - SetupVerifier.verifySetup(pomFile); - } - - @Test - public void testSetupWithCustomQuarkusVersion() throws Exception { - testDir = new File("target/test-classes", "projects/setup-with-custom-quarkus-version"); - assertThat(testDir).isDirectory(); - invoker = initInvoker(testDir); - - Properties params = new Properties(); - params.setProperty("quarkusVersion", "0.0.0"); - setup(params); - - File pomFile = new File(testDir, "pom.xml"); - assertThat(pomFile).isFile(); - SetupVerifier.verifySetupWithVersion(pomFile); - } - - @AfterEach - public void cleanup() { - if (running != null) { - running.stop(); - } - } - - private InvocationResult setup(Properties params) - throws MavenInvocationException, FileNotFoundException, UnsupportedEncodingException { - - params.setProperty("platformGroupId", ToolsConstants.IO_QUARKUS); - params.setProperty("platformArtifactId", "quarkus-bom"); - params.setProperty("platformVersion", getPluginVersion()); - - InvocationRequest request = new DefaultInvocationRequest(); - request.setBatchMode(true); - request.setGoals(Collections.singletonList( - getPluginGroupId() + ":" + getPluginArtifactId() + ":" + getPluginVersion() + ":create")); - request.setDebug(false); - request.setShowErrors(false); - request.setProperties(params); - getEnv().forEach(request::addShellEnvironment); - File log = new File(testDir, "build-create-" + testDir.getName() + ".log"); - PrintStreamLogger logger = new PrintStreamLogger(new PrintStream(new FileOutputStream(log), false, "UTF-8"), - InvokerLogger.DEBUG); - invoker.setLogger(logger); - return invoker.execute(request); - } -} diff --git a/integration-tests/maven/src/test/resources/projects/simple-pom-it/pom.xml b/integration-tests/maven/src/test/resources/projects/simple-pom-it/pom.xml index 1dc185b7a8940..5147ef2652777 100644 --- a/integration-tests/maven/src/test/resources/projects/simple-pom-it/pom.xml +++ b/integration-tests/maven/src/test/resources/projects/simple-pom-it/pom.xml @@ -5,4 +5,21 @@ io.acme.it acme-empty-pom 0.0.1.BUILD-SNAPSHOT + + io.quarkus + quarkus-bom + @project.version@ + + + + + + ${quarkus.platform.group-id} + ${quarkus.platform.artifact-id} + ${quarkus.platform.version} + pom + import + + +