From 64e26b8d5e7d7d8886914598ad4b432d75a17dd8 Mon Sep 17 00:00:00 2001 From: Parul Sharma Date: Thu, 23 May 2024 15:01:30 +0530 Subject: [PATCH 1/2] Introduce command that would list available profiles#204 --- prospero-cli/pom.xml | 4 + .../org/wildfly/prospero/cli/CliMessages.java | 39 +++++++++- .../prospero/cli/StringCaptureConsole.java | 33 ++++++++ .../prospero/cli/commands/CliConstants.java | 1 + .../prospero/cli/commands/InstallCommand.java | 75 ++++++++++++++++++- .../main/resources/UsageMessages.properties | 10 +++ .../cli/commands/InstallCommandTest.java | 46 ++++++++++-- 7 files changed, 196 insertions(+), 12 deletions(-) create mode 100644 prospero-cli/src/main/java/org/wildfly/prospero/cli/StringCaptureConsole.java diff --git a/prospero-cli/pom.xml b/prospero-cli/pom.xml index 058f73e52..8785e26dc 100644 --- a/prospero-cli/pom.xml +++ b/prospero-cli/pom.xml @@ -100,6 +100,10 @@ system-rules test + + info.picocli + picocli + diff --git a/prospero-cli/src/main/java/org/wildfly/prospero/cli/CliMessages.java b/prospero-cli/src/main/java/org/wildfly/prospero/cli/CliMessages.java index 9b5ce364a..49e11095b 100644 --- a/prospero-cli/src/main/java/org/wildfly/prospero/cli/CliMessages.java +++ b/prospero-cli/src/main/java/org/wildfly/prospero/cli/CliMessages.java @@ -22,6 +22,7 @@ import org.wildfly.prospero.actions.ApplyCandidateAction; import org.wildfly.prospero.cli.commands.CliConstants; import org.wildfly.prospero.metadata.ProsperoMetadataUtils; +import picocli.CommandLine; import java.io.File; import java.nio.file.Path; @@ -115,15 +116,15 @@ default String changesFound() { } default String continueWithUpdate() { - return bundle.getString("prospero.updates.prompt") + " "; + return bundle.getString("prospero.updates.prompt") + " "; } default String continueWithRevert() { - return bundle.getString("prospero.revert.prompt") + " "; + return bundle.getString("prospero.revert.prompt") + " "; } default String continueWithBuildUpdate() { - return bundle.getString("prospero.updates.build.prompt") + " "; + return bundle.getString("prospero.updates.build.prompt") + " "; } default String updateCancelled() { @@ -368,9 +369,11 @@ default String attemptedRepositories() { default String missing() { return bundle.getString("prospero.general.error.resolve.missing"); } + default String checksumFailed() { return bundle.getString("prospero.general.error.resolve.checksum_failed"); } + default String offline() { return bundle.getString("prospero.general.error.resolve.offline"); } @@ -611,12 +614,15 @@ default String diffFeaturesChanges() { default String productAndVersionNotNull() { return bundle.getString("prospero.update.subscribe.product.version.required"); } + default String unknownProduct(String product) { return format(bundle.getString("prospero.update.subscribe.unknown.product"), product); } + default String writeManifest(Path manifestPath) { return format(bundle.getString("prospero.update.subscribe.write.manifest"), manifestPath); } + default String writeChannelsConfiguration(Path channelsPath) { return format(bundle.getString("prospero.update.subscribe.write.channels"), channelsPath); } @@ -624,12 +630,15 @@ default String writeChannelsConfiguration(Path channelsPath) { default String conflictsWhenGenerating(String diff) { return format(bundle.getString("prospero.update.subscribe.conflict.prompt"), diff); } + default String continueGenerating() { return bundle.getString("prospero.update.subscribe.conflict.prompt.continue"); } + default String quitGenerating() { return bundle.getString("prospero.update.subscribe.conflict.prompt.cancel"); } + default String metadataExistsAlready(Path path, String distName) { return format(bundle.getString("prospero.update.subscribe.meta.exists"), path, distName); } @@ -638,18 +647,40 @@ default String serverVersionsHeader() { return bundle.getString("prospero.channels.versions.header"); } - default IllegalArgumentException unknownStabilityLevel(String stabilityLevel, List supportedStabilityLevels) { + default IllegalArgumentException unknownStabilityLevel(String stabilityLevel, List supportedStabilityLevels) { return new IllegalArgumentException(format(bundle.getString("prospero.install.unknown_stability_level"), stabilityLevel, String.join(",", supportedStabilityLevels))); } + default String featurePackRequiresLayers(String featurePackName) { return format(bundle.getString("prospero.features.add.validation.layers_required"), featurePackName, CliConstants.LAYERS); } + default String featurePackDoesNotSupportCustomization(String featurePackName) { return format(bundle.getString("prospero.features.add.validation.customization_not_supported"), featurePackName, String.join(",", List.of(CliConstants.LAYERS, CliConstants.TARGET_CONFIG))); } + default String featurePackRequiresLicense(String featurePackName) { return format(bundle.getString("prospero.features.add.required_licences"), featurePackName); } + + default CommandLine.ParameterException missingRequiredParameter(CommandLine spec, String param) { + return new CommandLine.ParameterException(spec, format(bundle.getString("prospero.general.error.missing.parameter"), param)); + } + + default String availableProfiles() { + return bundle.getString("prospero.install.list.profile.header"); + } + + default String subscribedChannels() { + return bundle.getString("prospero.install.list.profile.subscribe.channels"); + } + default String includedFeaturePacks() { + return bundle.getString("prospero.install.list.profile.featurePacks"); + } + + default String getProfile() { + return bundle.getString("prospero.install.list.profile"); + } } diff --git a/prospero-cli/src/main/java/org/wildfly/prospero/cli/StringCaptureConsole.java b/prospero-cli/src/main/java/org/wildfly/prospero/cli/StringCaptureConsole.java new file mode 100644 index 000000000..19308bccb --- /dev/null +++ b/prospero-cli/src/main/java/org/wildfly/prospero/cli/StringCaptureConsole.java @@ -0,0 +1,33 @@ +package org.wildfly.prospero.cli; + +import org.wildfly.prospero.api.Console; +import org.wildfly.prospero.api.ProvisioningProgressEvent; + +import java.util.ArrayList; +import java.util.List; + +public class StringCaptureConsole implements Console { + private List lines = new ArrayList<>(); + + @Override + public void progressUpdate(ProvisioningProgressEvent update) { + //no operations + } + + @Override + public void println(String text) { + lines.add(text); + } + + public List getLines() { + return lines; + } + + public List getLines(int indent) { + final List indentedLines = new ArrayList<>(); + for (String line : lines) { + indentedLines.add(" ".repeat(indent) + line); + } + return indentedLines; + } +} diff --git a/prospero-cli/src/main/java/org/wildfly/prospero/cli/commands/CliConstants.java b/prospero-cli/src/main/java/org/wildfly/prospero/cli/commands/CliConstants.java index c7b087d55..6fa3b30e4 100644 --- a/prospero-cli/src/main/java/org/wildfly/prospero/cli/commands/CliConstants.java +++ b/prospero-cli/src/main/java/org/wildfly/prospero/cli/commands/CliConstants.java @@ -78,6 +78,7 @@ private Commands() { public static final String H = "-h"; public static final String HELP = "--help"; public static final String LAYERS = "--layers"; + public static final String LIST_PROFILES = "--list-profiles"; public static final String LOCAL_CACHE = "--local-cache"; public static final String OFFLINE = "--offline"; public static final String PACKAGE_STABILITY_LEVEL = "--package-stability-level"; diff --git a/prospero-cli/src/main/java/org/wildfly/prospero/cli/commands/InstallCommand.java b/prospero-cli/src/main/java/org/wildfly/prospero/cli/commands/InstallCommand.java index f79975c6b..6e4e29d5d 100644 --- a/prospero-cli/src/main/java/org/wildfly/prospero/cli/commands/InstallCommand.java +++ b/prospero-cli/src/main/java/org/wildfly/prospero/cli/commands/InstallCommand.java @@ -25,11 +25,16 @@ import java.util.List; import java.util.Locale; import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.jboss.galleon.Constants; +import org.jboss.galleon.ProvisioningException; +import org.jboss.galleon.api.config.GalleonFeaturePackConfig; import org.jboss.galleon.api.config.GalleonProvisioningConfig; +import org.jboss.galleon.universe.FeaturePackLocation; import org.wildfly.channel.Channel; import org.wildfly.channel.ChannelManifestCoordinate; import org.wildfly.channel.Repository; @@ -48,21 +53,28 @@ import org.wildfly.prospero.cli.LicensePrinter; import org.wildfly.prospero.cli.RepositoryDefinition; import org.wildfly.prospero.cli.ReturnCodes; +import org.wildfly.prospero.cli.StringCaptureConsole; import org.wildfly.prospero.api.TemporaryFilesManager; import org.wildfly.prospero.cli.commands.options.FeaturePackCandidates; import org.wildfly.prospero.cli.printers.ChannelPrinter; +import org.wildfly.prospero.galleon.GalleonUtils; import org.wildfly.prospero.licenses.License; +import org.wildfly.prospero.model.KnownFeaturePack; import picocli.CommandLine; + @CommandLine.Command( name = CliConstants.Commands.INSTALL, sortOptions = false ) public class InstallCommand extends AbstractInstallCommand { + @CommandLine.Spec + CommandLine.Model.CommandSpec spec; + @CommandLine.Option( names = CliConstants.DIR, - required = true, + required = false, order = 2 ) Path directory; @@ -117,6 +129,13 @@ static class FeaturePackOrDefinition { order = 3 ) Optional definition; + + @CommandLine.Option( + names = CliConstants.LIST_PROFILES, + order = 4 + ) + boolean listProfiles; + } public InstallCommand(CliConsole console, ActionFactory actionFactory) { @@ -127,6 +146,14 @@ public InstallCommand(CliConsole console, ActionFactory actionFactory) { public Integer call() throws Exception { final long startTime = System.currentTimeMillis(); + if (featurePackOrDefinition.listProfiles) { + getListOfProfiles(); + return ReturnCodes.SUCCESS; + } + if (directory == null) { + throw CliMessages.MESSAGES.missingRequiredParameter(spec.commandLine(),CliConstants.DIR); + } + // following is checked by picocli, adding this to avoid IDE warnings assert featurePackOrDefinition.definition.isPresent() || featurePackOrDefinition.fpl.isPresent() || featurePackOrDefinition.profile.isPresent(); @@ -162,7 +189,6 @@ public Integer call() throws Exception { } verifyTargetDirectoryIsEmpty(directory); - final ProvisioningDefinition provisioningDefinition = buildDefinition() .setStabilityLevel(stabilityLevels.stabilityLevel==null?null:stabilityLevels.stabilityLevel.toLowerCase(Locale.ROOT)) .setPackageStabilityLevel(stabilityLevels.packageStabilityLevel==null?null:stabilityLevels.packageStabilityLevel.toLowerCase(Locale.ROOT)) @@ -285,4 +311,49 @@ public void verify() { } } } + + private void getListOfProfiles() throws Exception { + console.println(CliMessages.MESSAGES.availableProfiles() + "\n"); + Set featurePackNames = KnownFeaturePacks.getNames(); + StringCaptureConsole captureConsole = new StringCaptureConsole(); + final ChannelPrinter channelPrinter = new ChannelPrinter(captureConsole); + for (String profile : featurePackNames){ + console.println("----------"); + console.println(CliMessages.MESSAGES.getProfile() +profile+ "\n"+" "+CliMessages.MESSAGES.subscribedChannels()); + for(Channel channel: getChannels(featurePackNames)){ + channelPrinter.print(channel); + for (String line: captureConsole.getLines(6)) { + console.println(line); //reprint with the normal console, adding some spaces before each line + } + } + console.println(" "+CliMessages.MESSAGES.includedFeaturePacks()); + for(String featurePacks: featurePackNames){ + List loc = getFpl(KnownFeaturePacks.getByName(featurePacks)); + for (FeaturePackLocation featurePackLocation: loc) { + console.println(" " + featurePackLocation.toString()); + } + } + + } + } + + private List getChannels(Set featurePackNames) throws Exception { + final List channels = new ArrayList<>(); + for (String channelNames : featurePackNames) { + KnownFeaturePack knownFeaturePack = KnownFeaturePacks.getByName(channelNames); + channels.addAll(knownFeaturePack.getChannels()); + } + return channels; + } + + private List getFpl(KnownFeaturePack knownFeaturePack) throws Exception { + GalleonProvisioningConfig config = GalleonUtils.loadProvisioningConfig(knownFeaturePack.getGalleonConfiguration()); + if (config.getFeaturePackDeps().isEmpty()) { + throw new ProvisioningException("At least one feature pack location must be specified in the provisioning configuration"); + } + return config.getFeaturePackDeps().stream() + .map(GalleonFeaturePackConfig::getLocation) + .collect(Collectors.toList()); + } + } diff --git a/prospero-cli/src/main/resources/UsageMessages.properties b/prospero-cli/src/main/resources/UsageMessages.properties index 11d9c6166..e9a0e7301 100644 --- a/prospero-cli/src/main/resources/UsageMessages.properties +++ b/prospero-cli/src/main/resources/UsageMessages.properties @@ -31,6 +31,9 @@ ${prospero.dist.name}.install.usage.customSynopsis.3 = \u0020 or: @|bold ${pros ${prospero.dist.name}.install.usage.customSynopsis.4 = \u0020 (to install a feature pack) ${prospero.dist.name}.install.usage.customSynopsis.5 = \u0020 or: @|bold ${prospero.dist.name} install|@ @|fg(yellow) --definition|@=@|italic |@ @|fg(yellow) --dir|@=@|italic |@ [@|fg(yellow) OPTION|@]... ${prospero.dist.name}.install.usage.customSynopsis.6 = \u0020 (to install from a Galleon `@|bold provisioning.xml|@` file) +${prospero.dist.name}.install.usage.customSynopsis.7 = \u0020 or: @|bold ${prospero.dist.name} install|@ @|fg(yellow) --list-profiles|@ +${prospero.dist.name}.install.usage.customSynopsis.8 = \u0020 (to list available installation profiles) + ${prospero.dist.name}.update.usage.header = Updates a server instance with the latest patches. ${prospero.dist.name}.update.usage.description.0 = Update operation can be run either as a one-step (@|bold perform|@) or two-step (@|bold prepare|@+@|bold apply|@) operation. @@ -139,6 +142,7 @@ channel-name = Name of the new channel. Channel names should be unique for the s ${prospero.dist.name}.channel.remove.channel-name = Name of the channel. customization-repository = URL to repository containing custom artifacts. definition = Path to the Galleon `@|bold provisioning.xml|@` file. +list-profiles = Display the available installation profiles. Includes feature packs included in the profile and subscribed channels. # General description for the --dir option, applies to all commands unless a command specifies different description: dir = Location of the existing application server. If not specified, current working directory is used. @@ -390,3 +394,9 @@ prospero.changes.diff.channel=channel prospero.changes.diff.features_changes=Installed features changes prospero.changes.conflict.header=Conflicting changes detected in the update: +prospero.general.error.missing.parameter=Error: missing argument: [%s] +prospero.install.list.profile.header=Available installation profiles +prospero.install.list.profile=Profile:\u0020 +prospero.install.list.profile.subscribe.channels=Subscribed channels:\u0020 +prospero.install.list.profile.featurePacks=Installed feature packs:\u0020 + diff --git a/prospero-cli/src/test/java/org/wildfly/prospero/cli/commands/InstallCommandTest.java b/prospero-cli/src/test/java/org/wildfly/prospero/cli/commands/InstallCommandTest.java index c10c5e6d0..ef36c7359 100644 --- a/prospero-cli/src/test/java/org/wildfly/prospero/cli/commands/InstallCommandTest.java +++ b/prospero-cli/src/test/java/org/wildfly/prospero/cli/commands/InstallCommandTest.java @@ -51,7 +51,6 @@ import org.wildfly.prospero.cli.CliMessages; import org.wildfly.prospero.cli.ReturnCodes; import org.wildfly.prospero.test.MetadataTestUtils; - import org.jboss.galleon.api.GalleonBuilder; import org.jboss.galleon.api.Provisioning; import org.jboss.galleon.api.config.GalleonProvisioningConfig; @@ -99,10 +98,32 @@ public void setUp() throws Exception { @Test public void errorIfTargetPathIsNotPresent() { - int exitCode = commandLine.execute(CliConstants.Commands.INSTALL); + int exitCode = commandLine.execute(CliConstants.Commands.INSTALL, + CliConstants.FPL, "foo:bar"); + Assert.assertEquals(ReturnCodes.INVALID_ARGUMENTS, exitCode); + assertTrue(getErrorOutput().contains(String.format( + "Error: missing argument: ", "[", + CliConstants.DIR, "]") )); + } + + @Test + public void errorIfOperationIsNotPresent(){ + int exitCode = commandLine.execute(CliConstants.Commands.INSTALL, + CliConstants.DIR, "test"); + assertEquals(ReturnCodes.INVALID_ARGUMENTS, exitCode); + assertTrue(getErrorOutput().contains(String.format( + "Missing required argument (specify one of these): (%s=%s | %s=%s | %s=%s | %s)", + CliConstants.PROFILE, CliConstants.PROFILE_REFERENCE, CliConstants.FPL, CliConstants.FEATURE_PACK_REFERENCE, CliConstants.DEFINITION, CliConstants.PATH, CliConstants.LIST_PROFILES))); + } + + @Test + public void listProfilesExcludesOtherOptions() { + int exitCode = commandLine.execute(CliConstants.Commands.INSTALL, + CliConstants.FPL, "foo:bar", + CliConstants.LIST_PROFILES); Assert.assertEquals(ReturnCodes.INVALID_ARGUMENTS, exitCode); - assertTrue(getErrorOutput().contains(String.format("Missing required option: '%s='", - CliConstants.DIR))); + assertTrue(getErrorOutput().contains(String.format("Error: %s=%s, %s are mutually exclusive (specify only one)", + CliConstants.FPL, CliConstants.FEATURE_PACK_REFERENCE, CliConstants.LIST_PROFILES))); } @Test @@ -110,8 +131,8 @@ public void errorIfFplIsNotPresent() { int exitCode = commandLine.execute(CliConstants.Commands.INSTALL, CliConstants.DIR, "test"); assertEquals(ReturnCodes.INVALID_ARGUMENTS, exitCode); assertTrue(getErrorOutput().contains(String.format( - "Missing required argument (specify one of these): (%s=%s | %s=%s | %s=%s)", - CliConstants.PROFILE, CliConstants.PROFILE_REFERENCE, CliConstants.FPL, CliConstants.FEATURE_PACK_REFERENCE, CliConstants.DEFINITION, CliConstants.PATH))); + "Missing required argument (specify one of these): (%s=%s | %s=%s | %s=%s | %s)", + CliConstants.PROFILE, CliConstants.PROFILE_REFERENCE, CliConstants.FPL, CliConstants.FEATURE_PACK_REFERENCE, CliConstants.DEFINITION, CliConstants.PATH, CliConstants.LIST_PROFILES))); } @Test @@ -382,6 +403,19 @@ public void invalidStabilityLevelCausesAnError() throws Exception { .contains(CliMessages.MESSAGES.unknownStabilityLevel("idontexist", InstallCommand.STABILITY_LEVELS).getMessage()); } + @Test + public void testListProfiles() throws Exception { + int exitCode = commandLine.execute(CliConstants.Commands.INSTALL, CliConstants.LIST_PROFILES); + assertEquals(ReturnCodes.SUCCESS, exitCode); + assertThat(getStandardOutput()).contains("known-fpl","org.wildfly.channels:wildfly-core:26.1.0","id: central", + "url: https://repo1.maven.org/maven2/", + "url: https://repo1.maven.org/maven2/", + "https://repository.jboss.org/nexus/content/groups/public/", + "org.wildfly.core:wildfly-core-galleon-pack:zip"); + + + } + @Override protected MavenOptions getCapturedMavenOptions() throws Exception { Mockito.verify(actionFactory).install(any(), mavenOptions.capture(), any()); From 7b08308fa2a6780da1f7a881f64d8c7f7dca9f4a Mon Sep 17 00:00:00 2001 From: Bartosz Spyrko-Smietanko Date: Thu, 6 Jun 2024 10:51:30 +0100 Subject: [PATCH 2/2] [#204] UX adjustments and refactoring --- prospero-cli/pom.xml | 4 - .../org/wildfly/prospero/cli/CliMessages.java | 4 + .../prospero/cli/StringCaptureConsole.java | 33 -------- .../prospero/cli/commands/InstallCommand.java | 76 ++++++++++--------- .../prospero/cli/printers/ChannelPrinter.java | 20 +++-- .../main/resources/UsageMessages.properties | 6 +- .../cli/commands/InstallCommandTest.java | 17 +++-- 7 files changed, 71 insertions(+), 89 deletions(-) delete mode 100644 prospero-cli/src/main/java/org/wildfly/prospero/cli/StringCaptureConsole.java diff --git a/prospero-cli/pom.xml b/prospero-cli/pom.xml index 8785e26dc..058f73e52 100644 --- a/prospero-cli/pom.xml +++ b/prospero-cli/pom.xml @@ -100,10 +100,6 @@ system-rules test - - info.picocli - picocli - diff --git a/prospero-cli/src/main/java/org/wildfly/prospero/cli/CliMessages.java b/prospero-cli/src/main/java/org/wildfly/prospero/cli/CliMessages.java index 49e11095b..e532bc6c8 100644 --- a/prospero-cli/src/main/java/org/wildfly/prospero/cli/CliMessages.java +++ b/prospero-cli/src/main/java/org/wildfly/prospero/cli/CliMessages.java @@ -672,6 +672,9 @@ default CommandLine.ParameterException missingRequiredParameter(CommandLine spec default String availableProfiles() { return bundle.getString("prospero.install.list.profile.header"); } + default String noAvailableProfiles() { + return bundle.getString("prospero.install.list.profile.no_profiles.header"); + } default String subscribedChannels() { return bundle.getString("prospero.install.list.profile.subscribe.channels"); @@ -683,4 +686,5 @@ default String includedFeaturePacks() { default String getProfile() { return bundle.getString("prospero.install.list.profile"); } + } diff --git a/prospero-cli/src/main/java/org/wildfly/prospero/cli/StringCaptureConsole.java b/prospero-cli/src/main/java/org/wildfly/prospero/cli/StringCaptureConsole.java deleted file mode 100644 index 19308bccb..000000000 --- a/prospero-cli/src/main/java/org/wildfly/prospero/cli/StringCaptureConsole.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.wildfly.prospero.cli; - -import org.wildfly.prospero.api.Console; -import org.wildfly.prospero.api.ProvisioningProgressEvent; - -import java.util.ArrayList; -import java.util.List; - -public class StringCaptureConsole implements Console { - private List lines = new ArrayList<>(); - - @Override - public void progressUpdate(ProvisioningProgressEvent update) { - //no operations - } - - @Override - public void println(String text) { - lines.add(text); - } - - public List getLines() { - return lines; - } - - public List getLines(int indent) { - final List indentedLines = new ArrayList<>(); - for (String line : lines) { - indentedLines.add(" ".repeat(indent) + line); - } - return indentedLines; - } -} diff --git a/prospero-cli/src/main/java/org/wildfly/prospero/cli/commands/InstallCommand.java b/prospero-cli/src/main/java/org/wildfly/prospero/cli/commands/InstallCommand.java index 6e4e29d5d..1a45771c5 100644 --- a/prospero-cli/src/main/java/org/wildfly/prospero/cli/commands/InstallCommand.java +++ b/prospero-cli/src/main/java/org/wildfly/prospero/cli/commands/InstallCommand.java @@ -53,7 +53,6 @@ import org.wildfly.prospero.cli.LicensePrinter; import org.wildfly.prospero.cli.RepositoryDefinition; import org.wildfly.prospero.cli.ReturnCodes; -import org.wildfly.prospero.cli.StringCaptureConsole; import org.wildfly.prospero.api.TemporaryFilesManager; import org.wildfly.prospero.cli.commands.options.FeaturePackCandidates; import org.wildfly.prospero.cli.printers.ChannelPrinter; @@ -62,6 +61,8 @@ import org.wildfly.prospero.model.KnownFeaturePack; import picocli.CommandLine; +import javax.xml.stream.XMLStreamException; + @CommandLine.Command( name = CliConstants.Commands.INSTALL, @@ -69,6 +70,8 @@ ) public class InstallCommand extends AbstractInstallCommand { + protected static final int PROFILES_INDENT = 4; + protected static final String PROFILE_SUBHEADERS_INDENT = " "; @CommandLine.Spec CommandLine.Model.CommandSpec spec; @@ -147,11 +150,10 @@ public Integer call() throws Exception { final long startTime = System.currentTimeMillis(); if (featurePackOrDefinition.listProfiles) { - getListOfProfiles(); - return ReturnCodes.SUCCESS; + return displayListOfProfiles(); } if (directory == null) { - throw CliMessages.MESSAGES.missingRequiredParameter(spec.commandLine(),CliConstants.DIR); + throw CliMessages.MESSAGES.missingRequiredParameter(spec.commandLine(), CliConstants.DIR); } // following is checked by picocli, adding this to avoid IDE warnings @@ -312,48 +314,48 @@ public void verify() { } } - private void getListOfProfiles() throws Exception { - console.println(CliMessages.MESSAGES.availableProfiles() + "\n"); - Set featurePackNames = KnownFeaturePacks.getNames(); - StringCaptureConsole captureConsole = new StringCaptureConsole(); - final ChannelPrinter channelPrinter = new ChannelPrinter(captureConsole); - for (String profile : featurePackNames){ + private int displayListOfProfiles() throws ProvisioningException { + final Set profiles = KnownFeaturePacks.getNames(); + if (profiles.isEmpty()) { + console.println(CliMessages.MESSAGES.noAvailableProfiles() + "\n"); + } else { + console.println(CliMessages.MESSAGES.availableProfiles() + "\n"); + } + + final ChannelPrinter channelPrinter = new ChannelPrinter(console, PROFILES_INDENT); + for (String profileName : profiles){ console.println("----------"); - console.println(CliMessages.MESSAGES.getProfile() +profile+ "\n"+" "+CliMessages.MESSAGES.subscribedChannels()); - for(Channel channel: getChannels(featurePackNames)){ + console.println(CliMessages.MESSAGES.getProfile() + profileName); + + final KnownFeaturePack profile = KnownFeaturePacks.getByName(profileName); + + console.println(PROFILE_SUBHEADERS_INDENT + CliMessages.MESSAGES.subscribedChannels()); + for(Channel channel: profile.getChannels()){ channelPrinter.print(channel); - for (String line: captureConsole.getLines(6)) { - console.println(line); //reprint with the normal console, adding some spaces before each line - } - } - console.println(" "+CliMessages.MESSAGES.includedFeaturePacks()); - for(String featurePacks: featurePackNames){ - List loc = getFpl(KnownFeaturePacks.getByName(featurePacks)); - for (FeaturePackLocation featurePackLocation: loc) { - console.println(" " + featurePackLocation.toString()); - } } + console.println(PROFILE_SUBHEADERS_INDENT + CliMessages.MESSAGES.includedFeaturePacks()); + for (FeaturePackLocation featurePackLocation: getFeaturePacks(profile)) { + console.println(" ".repeat(PROFILES_INDENT) + featurePackLocation.toString()); + } } - } - private List getChannels(Set featurePackNames) throws Exception { - final List channels = new ArrayList<>(); - for (String channelNames : featurePackNames) { - KnownFeaturePack knownFeaturePack = KnownFeaturePacks.getByName(channelNames); - channels.addAll(knownFeaturePack.getChannels()); - } - return channels; + return ReturnCodes.SUCCESS; } - private List getFpl(KnownFeaturePack knownFeaturePack) throws Exception { - GalleonProvisioningConfig config = GalleonUtils.loadProvisioningConfig(knownFeaturePack.getGalleonConfiguration()); - if (config.getFeaturePackDeps().isEmpty()) { - throw new ProvisioningException("At least one feature pack location must be specified in the provisioning configuration"); + private List getFeaturePacks(KnownFeaturePack profile) throws ProvisioningException { + try { + final GalleonProvisioningConfig config = GalleonUtils.loadProvisioningConfig(profile.getGalleonConfiguration()); + if (config.getFeaturePackDeps().isEmpty()) { + throw new ProvisioningException("At least one feature pack location must be specified in the provisioning configuration"); + } + + return config.getFeaturePackDeps().stream() + .map(GalleonFeaturePackConfig::getLocation) + .collect(Collectors.toList()); + } catch (XMLStreamException e) { + throw new ProvisioningException("Unable to parse provisioning configuration", e); } - return config.getFeaturePackDeps().stream() - .map(GalleonFeaturePackConfig::getLocation) - .collect(Collectors.toList()); } } diff --git a/prospero-cli/src/main/java/org/wildfly/prospero/cli/printers/ChannelPrinter.java b/prospero-cli/src/main/java/org/wildfly/prospero/cli/printers/ChannelPrinter.java index 3414cba73..41cd654d6 100644 --- a/prospero-cli/src/main/java/org/wildfly/prospero/cli/printers/ChannelPrinter.java +++ b/prospero-cli/src/main/java/org/wildfly/prospero/cli/printers/ChannelPrinter.java @@ -25,27 +25,37 @@ public class ChannelPrinter { private final Console console; + private final int indentSize; public ChannelPrinter(Console console) { + this(console, 0); + } + + public ChannelPrinter(Console console, int indentSize) { this.console = console; + this.indentSize = indentSize; } public void print(Channel channel) { String startMarker = "# "; if (channel.getName() != null && !channel.getName().isEmpty()) { - console.println(startMarker + channel.getName()); + printLine(startMarker + channel.getName()); startMarker = " "; } final String manifest = channel.getManifestCoordinate().getMaven() == null ?channel.getManifestCoordinate().getUrl().toExternalForm():toGav(channel.getManifestCoordinate().getMaven()); - console.println(startMarker + "manifest: " + manifest); - console.println(" " + "repositories:"); + printLine(startMarker + "manifest: " + manifest); + printLine(" " + "repositories:"); for (Repository repository : channel.getRepositories()) { - console.println(" " + " " + "id: " + repository.getId()); - console.println(" " + " " + "url: " + repository.getUrl()); + printLine(" " + " " + "id: " + repository.getId()); + printLine(" " + " " + "url: " + repository.getUrl()); } } + private void printLine(String text) { + console.println(" ".repeat(indentSize) + text); + } + private static String toGav(MavenCoordinate coord) { final String ga = coord.getGroupId() + ":" + coord.getArtifactId(); if (coord.getVersion() != null && !coord.getVersion().isEmpty()) { diff --git a/prospero-cli/src/main/resources/UsageMessages.properties b/prospero-cli/src/main/resources/UsageMessages.properties index e9a0e7301..bbce2cccf 100644 --- a/prospero-cli/src/main/resources/UsageMessages.properties +++ b/prospero-cli/src/main/resources/UsageMessages.properties @@ -142,7 +142,7 @@ channel-name = Name of the new channel. Channel names should be unique for the s ${prospero.dist.name}.channel.remove.channel-name = Name of the channel. customization-repository = URL to repository containing custom artifacts. definition = Path to the Galleon `@|bold provisioning.xml|@` file. -list-profiles = Display the available installation profiles. Includes feature packs included in the profile and subscribed channels. +list-profiles = Displays the available installation profiles. The profiles contain complete provisioning configurations required to install a server. # General description for the --dir option, applies to all commands unless a command specifies different description: dir = Location of the existing application server. If not specified, current working directory is used. @@ -394,8 +394,10 @@ prospero.changes.diff.channel=channel prospero.changes.diff.features_changes=Installed features changes prospero.changes.conflict.header=Conflicting changes detected in the update: -prospero.general.error.missing.parameter=Error: missing argument: [%s] +prospero.general.error.missing.parameter=Error: Missing required argument: (%s) prospero.install.list.profile.header=Available installation profiles +prospero.install.list.profile.no_profiles.header=No installation profiles available. \n \ + You can use the feature pack location (--fpl) argument to install the server. prospero.install.list.profile=Profile:\u0020 prospero.install.list.profile.subscribe.channels=Subscribed channels:\u0020 prospero.install.list.profile.featurePacks=Installed feature packs:\u0020 diff --git a/prospero-cli/src/test/java/org/wildfly/prospero/cli/commands/InstallCommandTest.java b/prospero-cli/src/test/java/org/wildfly/prospero/cli/commands/InstallCommandTest.java index ef36c7359..9d36093cb 100644 --- a/prospero-cli/src/test/java/org/wildfly/prospero/cli/commands/InstallCommandTest.java +++ b/prospero-cli/src/test/java/org/wildfly/prospero/cli/commands/InstallCommandTest.java @@ -101,9 +101,8 @@ public void errorIfTargetPathIsNotPresent() { int exitCode = commandLine.execute(CliConstants.Commands.INSTALL, CliConstants.FPL, "foo:bar"); Assert.assertEquals(ReturnCodes.INVALID_ARGUMENTS, exitCode); - assertTrue(getErrorOutput().contains(String.format( - "Error: missing argument: ", "[", - CliConstants.DIR, "]") )); + assertThat(getErrorOutput()) + .contains(CliMessages.MESSAGES.missingRequiredParameter(commandLine, CliConstants.DIR).getMessage()); } @Test @@ -111,9 +110,10 @@ public void errorIfOperationIsNotPresent(){ int exitCode = commandLine.execute(CliConstants.Commands.INSTALL, CliConstants.DIR, "test"); assertEquals(ReturnCodes.INVALID_ARGUMENTS, exitCode); - assertTrue(getErrorOutput().contains(String.format( - "Missing required argument (specify one of these): (%s=%s | %s=%s | %s=%s | %s)", - CliConstants.PROFILE, CliConstants.PROFILE_REFERENCE, CliConstants.FPL, CliConstants.FEATURE_PACK_REFERENCE, CliConstants.DEFINITION, CliConstants.PATH, CliConstants.LIST_PROFILES))); + assertThat(getErrorOutput()) + .contains(String.format("Missing required argument (specify one of these): (%s=%s | %s=%s | %s=%s | %s)", + CliConstants.PROFILE, CliConstants.PROFILE_REFERENCE, CliConstants.FPL, CliConstants.FEATURE_PACK_REFERENCE, + CliConstants.DEFINITION, CliConstants.PATH, CliConstants.LIST_PROFILES)); } @Test @@ -122,8 +122,9 @@ public void listProfilesExcludesOtherOptions() { CliConstants.FPL, "foo:bar", CliConstants.LIST_PROFILES); Assert.assertEquals(ReturnCodes.INVALID_ARGUMENTS, exitCode); - assertTrue(getErrorOutput().contains(String.format("Error: %s=%s, %s are mutually exclusive (specify only one)", - CliConstants.FPL, CliConstants.FEATURE_PACK_REFERENCE, CliConstants.LIST_PROFILES))); + assertThat(getErrorOutput()) + .contains(String.format("Error: %s=%s, %s are mutually exclusive (specify only one)", + CliConstants.FPL, CliConstants.FEATURE_PACK_REFERENCE, CliConstants.LIST_PROFILES)); } @Test