diff --git a/devtools/cli/src/main/java/io/quarkus/cli/ProjectExtensionsList.java b/devtools/cli/src/main/java/io/quarkus/cli/ProjectExtensionsList.java index 3c3d7ef6fdb7d..65df4a92b4f28 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/ProjectExtensionsList.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/ProjectExtensionsList.java @@ -18,17 +18,24 @@ import picocli.CommandLine; import picocli.CommandLine.Mixin; -@CommandLine.Command(name = "list", aliases = "ls", sortOptions = false, showDefaultValues = true, mixinStandardHelpOptions = false, description = "List platforms and extensions for this project.") +@CommandLine.Command(name = "list", aliases = "ls", sortOptions = false, showDefaultValues = true, mixinStandardHelpOptions = false, description = "%n" + + "List platforms and extensions for this project. ", footer = { "%nList modes:%n", + "relative: Active when invoked within a project unless an explicit release is specified. " + + "The current project configuration will determine what extensions are listed, " + + "with installed (available) extensions listed by default.%n", + "absolute: Active when invoked outside of a project or when an explicit release is specified. " + + "All extensions for the specified release will be listed. " + + "The CLI release will be used if this command is invoked outside of a project and no other release is specified.%n" }) public class ProjectExtensionsList extends BaseBuildCommand implements Callable { @Mixin RunModeOption runMode; - @CommandLine.ArgGroup(order = 2, heading = "%nQuarkus version%n") + @CommandLine.ArgGroup(order = 2, heading = "%nQuarkus version (absolute)%n") TargetQuarkusVersionGroup targetQuarkusVersion = new TargetQuarkusVersionGroup(); @CommandLine.Option(names = { "-i", - "--installable" }, defaultValue = "false", order = 2, description = "Display installable extensions.") + "--installable" }, defaultValue = "false", order = 2, description = "List extensions that can be installed (relative)") boolean installable = false; @CommandLine.Option(names = { "-s", diff --git a/devtools/cli/src/main/java/io/quarkus/cli/common/ListFormatOptions.java b/devtools/cli/src/main/java/io/quarkus/cli/common/ListFormatOptions.java index a61235279ff31..2e8f5a034c578 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/common/ListFormatOptions.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/common/ListFormatOptions.java @@ -3,7 +3,7 @@ import picocli.CommandLine; public class ListFormatOptions { - @CommandLine.Option(names = { "--name" }, order = 4, description = "Display extension name only. (default)") + @CommandLine.Option(names = { "--name" }, order = 4, description = "Display extension name only.") boolean name = false; @CommandLine.Option(names = { "--concise" }, order = 5, description = "Display extension name and description.") diff --git a/devtools/cli/src/main/java/io/quarkus/cli/common/TargetQuarkusVersionGroup.java b/devtools/cli/src/main/java/io/quarkus/cli/common/TargetQuarkusVersionGroup.java index cfb99a5dc9764..8d2c1e48b125b 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/common/TargetQuarkusVersionGroup.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/common/TargetQuarkusVersionGroup.java @@ -1,7 +1,9 @@ package io.quarkus.cli.common; +import io.quarkus.cli.Version; import io.quarkus.maven.ArtifactCoords; import io.quarkus.maven.StreamCoords; +import io.quarkus.platform.tools.ToolsConstants; import picocli.CommandLine; import picocli.CommandLine.Model.CommandSpec; @@ -34,11 +36,37 @@ void setStream(String stream) { @CommandLine.Option(paramLabel = "groupId:artifactId:version", names = { "-P", "--platform-bom" }, description = "A specific Quarkus platform BOM, for example:%n io.quarkus:quarkus-bom:1.13.4.Final") void setPlatformBom(String bom) { - bom = bom.trim(); + bom = bom.replaceFirst("^::", "").trim(); if (!bom.isEmpty()) { try { - platformBom = ArtifactCoords.fromString(bom); - validPlatformBom = bom; // keep original (valid) string (dryrun) + int firstPos = bom.indexOf(":"); + int lastPos = bom.lastIndexOf(":"); + if (lastPos <= 0) { + // no : at all, use default group and artifact id + setBom(ToolsConstants.DEFAULT_PLATFORM_BOM_GROUP_ID, + ToolsConstants.DEFAULT_PLATFORM_BOM_ARTIFACT_ID, + bom); + } else if (lastPos == firstPos + 1) { // We have :: somewhere + if (lastPos == bom.length() - 1) { + // some.group::, use default artifact and client version + setBom(bom.substring(0, firstPos), + ToolsConstants.DEFAULT_PLATFORM_BOM_ARTIFACT_ID, + Version.clientVersion()); + } else { + // some.group::version, use default artifact id + setBom(bom.substring(0, firstPos), + ToolsConstants.DEFAULT_PLATFORM_BOM_ARTIFACT_ID, + bom.substring(lastPos + 1)); + } + } else if (firstPos == 0 && lastPos == bom.length() - 1) { + // :my-bom:, use default group and version + setBom(ToolsConstants.DEFAULT_PLATFORM_BOM_GROUP_ID, + bom.substring(1, lastPos), + Version.clientVersion()); + } else { + platformBom = ArtifactCoords.fromString(bom); + validPlatformBom = bom; // keep original (valid) string (dryrun) + } } catch (IllegalArgumentException iex) { throw new CommandLine.ParameterException(spec.commandLine(), String.format("Invalid value '%s' for option '--platform-bom'. " + @@ -80,4 +108,9 @@ public String toString() { + ", platformBom=" + platformBom + '}'; } + + private void setBom(String artifactId, String groupId, String version) { + platformBom = ArtifactCoords.pom(artifactId, groupId, version); + validPlatformBom = artifactId + ":" + groupId + ":" + version; + } } diff --git a/devtools/cli/src/test/java/io/quarkus/cli/common/TargetQuarkusVersionGroupTest.java b/devtools/cli/src/test/java/io/quarkus/cli/common/TargetQuarkusVersionGroupTest.java new file mode 100644 index 0000000000000..a0fc7306f9a01 --- /dev/null +++ b/devtools/cli/src/test/java/io/quarkus/cli/common/TargetQuarkusVersionGroupTest.java @@ -0,0 +1,90 @@ +package io.quarkus.cli.common; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import io.quarkus.cli.Version; +import io.quarkus.maven.ArtifactCoords; +import io.quarkus.maven.StreamCoords; +import io.quarkus.platform.tools.ToolsConstants; + +public class TargetQuarkusVersionGroupTest { + final static String clientVersion = Version.clientVersion(); + + TargetQuarkusVersionGroup qvg = new TargetQuarkusVersionGroup(); + + @Test + void testPlatformFullyQualified() { + qvg.setPlatformBom("io.something:custom-bom:1.3.0.Final"); + + ArtifactCoords coords = qvg.getPlatformBom(); + Assertions.assertEquals("io.something", coords.getGroupId()); + Assertions.assertEquals("custom-bom", coords.getArtifactId()); + Assertions.assertEquals("1.3.0.Final", coords.getVersion()); + Assertions.assertEquals("io.something:custom-bom:1.3.0.Final", qvg.validPlatformBom); + } + + @Test + void testPlatformUseDefaultArtifactVersion() { + qvg.setPlatformBom("just.group::"); + + ArtifactCoords coords = qvg.getPlatformBom(); + Assertions.assertEquals("just.group", coords.getGroupId()); + Assertions.assertEquals(ToolsConstants.DEFAULT_PLATFORM_BOM_ARTIFACT_ID, coords.getArtifactId()); + Assertions.assertEquals(clientVersion, coords.getVersion()); + Assertions.assertEquals("just.group:" + ToolsConstants.DEFAULT_PLATFORM_BOM_ARTIFACT_ID + ":" + clientVersion, + qvg.validPlatformBom); + } + + @Test + void testPlatformUseDefaultArtifact() { + qvg.setPlatformBom("group::version"); + + ArtifactCoords coords = qvg.getPlatformBom(); + Assertions.assertEquals("group", coords.getGroupId()); + Assertions.assertEquals(ToolsConstants.DEFAULT_PLATFORM_BOM_ARTIFACT_ID, coords.getArtifactId()); + Assertions.assertEquals("version", coords.getVersion()); + Assertions.assertEquals("group:" + ToolsConstants.DEFAULT_PLATFORM_BOM_ARTIFACT_ID + ":version", + qvg.validPlatformBom); + } + + @Test + void testPlatformUseDefaultGroupVersion() { + qvg.setPlatformBom(":artifact:"); + + ArtifactCoords coords = qvg.getPlatformBom(); + Assertions.assertEquals(ToolsConstants.DEFAULT_PLATFORM_BOM_GROUP_ID, coords.getGroupId()); + Assertions.assertEquals("artifact", coords.getArtifactId()); + Assertions.assertEquals(clientVersion, coords.getVersion()); + Assertions.assertEquals(ToolsConstants.DEFAULT_PLATFORM_BOM_GROUP_ID + ":artifact:" + clientVersion, + qvg.validPlatformBom); + } + + @Test + void testStreamUseDFullyQualified() { + qvg.setStream("stream-platform:stream-version"); + + StreamCoords coords = qvg.getStream(); + Assertions.assertEquals("stream-platform", coords.getPlatformKey()); + Assertions.assertEquals("stream-version", coords.getStreamId()); + } + + @Test + void testStreamUseDefaultGroup() { + qvg.setStream(":stream-version"); + + StreamCoords coords = qvg.getStream(); + Assertions.assertNull(coords.getPlatformKey()); + Assertions.assertEquals("stream-version", coords.getStreamId()); + } + + @Test + void testStreamUseDefaultVersion() { + qvg.setStream("stream-platform:"); + + StreamCoords coords = qvg.getStream(); + Assertions.assertEquals("stream-platform", coords.getPlatformKey()); + Assertions.assertNull(coords.getStreamId()); + } + +} diff --git a/docs/src/main/asciidoc/cli-tooling.adoc b/docs/src/main/asciidoc/cli-tooling.adoc index b6e43a946d470..94ed8528f650c 100644 --- a/docs/src/main/asciidoc/cli-tooling.adoc +++ b/docs/src/main/asciidoc/cli-tooling.adoc @@ -146,7 +146,32 @@ $ quarkus create app --help $ quarkus create cli --help ---- -Note: Use `--platform-bom=groupId:artifactId:version` to target a specific version of quarkus +[NOTE] +.Specifying the Quarkus version +==== +Both `quarkus create` and `quarkus extension list` allow you to explicitly specify a version of Quarkus in one of two ways: + +1. Specify a specific Platform Release BOM ++ +A https://quarkus.io/guides/platform#quarkus-platform-bom[Quarkus Platform release BOM] is identified by `groupId:artifactId:version` (GAV) coordinates. When specifying a platform release BOM, you may use empty segments to fallback to default values (groupId: `io.quarkus`, artifactId: `quarkus-bom`, version: cli version). If you specify only one segment (no `:`), it is assumed to be a version. ++ +For example: ++ +- Given the `2.0.0.Final` version of the CLI, specifying `-P :quarkus-universe-bom:` is equivalent to `-P io.quarkus:quarkus-universe-bom:2.0.0.Final`. +- Specifying `-P 999-SNAPSHOT` is equivalent to `-P io.quarkus:quarkus-bom:999-SNAPSHOT` ++ +Note: default values are subject to change. Using the `--dryRun` option will show you the computed value. + +2. Specify a Platform Stream ++ +Platform streams are new in Quarkus 2.0. ++ +A platform stream operates against the concept of a "registry". Each registry defines one or more platform streams, and each stream defines one or more platform release BOM files that define how projects using that stream should be configured. ++ +Streams are identified using `platformKey:streamId` syntax. A specific stream can be specified using `-S platformKey:streamId`. When specifying a stream, empty segments will be replaced with _discovered_ defaults, based on stream resource resolution rules. ++ +For `2.0.0.Final`, you must enable the registry client (`--registry-client`) explicitly to specify a stream. This will not be required in later releases. +==== == Dealing with extensions @@ -155,21 +180,43 @@ Note: Use `--platform-bom=groupId:artifactId:version` to target a specific versi $ quarkus ext --help ---- -The Quarkus CLI can obtain a list of the extensions in the project: +=== Listing extensions + +The Quarkus CLI can be used to list Quarkus extensions. [source,shell] ---- $ quarkus ext ls ---- -To get a list of available extensions to install use `--installable` or `-i`. +The format of the result can be controlled with one of four options: + +- `--name` Display the name (artifactId) only +- `--concise` Display the name (artifactId) and description +- `--full` Display concise format and version/status-related columns. +- `--origins` Display concise information along with the Quarkus platform release origin of the extension. + +The behavior of `quarkus ext ls` will vary depending on context. + +==== Listing Extensions for a Quarkus release + +If you invoke the Quarkus CLI from outside of a project, Quarkus will list all of the extensions available for the Quarkus release used by the CLI itself. -You can combine that with a search (`--search` or `-s`) and get a concise -list including description with `--concise`. +You can also list extensions for a specific release of Quarkus using `-P` or `-S`, as described in <>. + +This mode uses the `--origins` format by default. + +==== Listing Extensions for a Quarkus project + +When working with a Quarkus project, the CLI will list the extensions the current project has installed, using the `--name` format by default. + +Use the `--installable` or `-i` option to list extensions that can be installed from the Quarkus platform the project is using. + +You can narrow or filter the list using search (`--search` or `-s`). [source,shell] ---- -$ quarkus ext list -i --concise -s jdbc +$ quarkus ext list --concise -i -s jdbc JDBC Driver - DB2 quarkus-jdbc-db2 JDBC Driver - PostgreSQL quarkus-jdbc-postgresql JDBC Driver - H2 quarkus-jdbc-h2 @@ -182,7 +229,8 @@ Elytron Security JDBC quarkus-elytron-security-jdbc Agroal - Database connection pool quarkus-agroal ---- -== Adding extension(s) + +=== Adding extension(s) The Quarkus CLI can add Quarkus one or more extensions to your project with the 'add' command: @@ -194,9 +242,9 @@ $ quarkus ext add kubernetes health [SUCCESS] ✅ Extension io.quarkus:quarkus-smallrye-health has been installed ---- -== Removing extension(s) +=== Removing extension(s) -The Quarkus CLI can add Quarkus one or more extensions to your project with the 'add' +The Quarkus CLI can remove one or more extensions from your project with the 'remove' command: [source,shell]