diff --git a/devtools/cli/src/main/java/io/quarkus/cli/Registry.java b/devtools/cli/src/main/java/io/quarkus/cli/Registry.java index 210a7debad06f..f88d0e04c8da3 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/Registry.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/Registry.java @@ -8,7 +8,9 @@ import picocli.CommandLine.Unmatched; @CommandLine.Command(name = "registry", sortOptions = false, mixinStandardHelpOptions = false, header = "Manage extension registries.", subcommands = { - RegistryListCommand.class }, headerHeading = "%n", commandListHeading = "%nCommands:%n", synopsisHeading = "%nUsage: ", optionListHeading = "%nOptions:%n") + RegistryAddCommand.class, + RegistryListCommand.class, + RegistryRemoveCommand.class }, headerHeading = "%n", commandListHeading = "%nCommands:%n", synopsisHeading = "%nUsage: ", optionListHeading = "%nOptions:%n") public class Registry extends BaseRegistryCommand { @Unmatched // avoids throwing errors for unmatched arguments diff --git a/devtools/cli/src/main/java/io/quarkus/cli/RegistryAddCommand.java b/devtools/cli/src/main/java/io/quarkus/cli/RegistryAddCommand.java new file mode 100644 index 0000000000000..f1e6b6c31a31f --- /dev/null +++ b/devtools/cli/src/main/java/io/quarkus/cli/RegistryAddCommand.java @@ -0,0 +1,74 @@ +package io.quarkus.cli; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Set; +import java.util.stream.Collectors; + +import io.quarkus.cli.registry.BaseRegistryCommand; +import io.quarkus.cli.registry.RegistryClientMixin; +import io.quarkus.registry.config.RegistriesConfig; +import io.quarkus.registry.config.RegistriesConfigLocator; +import io.quarkus.registry.config.RegistryConfig; +import io.quarkus.registry.config.json.JsonRegistriesConfig; +import io.quarkus.registry.config.json.JsonRegistryConfig; +import io.quarkus.registry.config.json.RegistriesConfigMapperHelper; +import picocli.CommandLine; + +@CommandLine.Command(name = "add", sortOptions = false, showDefaultValues = true, mixinStandardHelpOptions = false, header = "Add a Quarkus extension registry to the registry client configuration", description = "%n" + + "This command will add a Quarkus extension registry to the registry client configuration unless it's already present", headerHeading = "%n", commandListHeading = "%nCommands:%n", synopsisHeading = "%nUsage: ", parameterListHeading = "%n", optionListHeading = "Options:%n") +public class RegistryAddCommand extends BaseRegistryCommand { + + @CommandLine.Mixin + protected RegistryClientMixin registryClient; + + @CommandLine.Parameters(arity = "0..1", paramLabel = "REGISTRY-ID[,REGISTRY-ID]", description = "Registry ID to add to the registry client configuration%n" + + " Example:%n" + + " registry.quarkus.io%n" + + " registry.quarkus.acme.com,registry.quarkus.io%n") + String registryIds; + + @Override + public Integer call() throws Exception { + + registryClient.refreshRegistryCache(output); + + Path configYaml; + if (registryClient.getConfigArg() == null) { + configYaml = RegistriesConfigLocator.locateConfigYaml(); + if (configYaml == null) { + configYaml = RegistriesConfigLocator.getDefaultConfigYamlLocation(); + } + } else { + configYaml = Paths.get(registryClient.getConfigArg()); + } + + final RegistriesConfig config; + if (Files.exists(configYaml)) { + config = RegistriesConfigMapperHelper.deserialize(configYaml, JsonRegistriesConfig.class); + } else { + config = new JsonRegistriesConfig(); + } + + final Set existingIds = config.getRegistries().stream().map(RegistryConfig::getId).collect(Collectors.toSet()); + boolean persist = false; + for (String registryId : registryIds.split(",")) { + if (existingIds.add(registryId)) { + persist = true; + final JsonRegistryConfig registry = new JsonRegistryConfig(); + registry.setId(registryId); + config.getRegistries().add(registry); + output.info("Registry " + registryId + " was added"); + } else { + output.info("Registry " + registryId + " was skipped since it is already present"); + } + } + + if (persist) { + RegistriesConfigMapperHelper.serialize(config, configYaml); + } + + return CommandLine.ExitCode.OK; + } +} diff --git a/devtools/cli/src/main/java/io/quarkus/cli/RegistryListCommand.java b/devtools/cli/src/main/java/io/quarkus/cli/RegistryListCommand.java index 52327d73034a7..96872de2fba79 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/RegistryListCommand.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/RegistryListCommand.java @@ -28,7 +28,7 @@ public class RegistryListCommand extends BaseRegistryCommand { public Integer call() throws Exception { registryClient.refreshRegistryCache(output); - final RegistriesConfig config = RegistriesConfigLocator.resolveConfig(); + final RegistriesConfig config = registryClient.resolveConfig(); final ExtensionCatalogResolver catalogResolver = streams ? registryClient.getExtensionCatalogResolver(output) : null; diff --git a/devtools/cli/src/main/java/io/quarkus/cli/RegistryRemoveCommand.java b/devtools/cli/src/main/java/io/quarkus/cli/RegistryRemoveCommand.java new file mode 100644 index 0000000000000..a3a3a649f19eb --- /dev/null +++ b/devtools/cli/src/main/java/io/quarkus/cli/RegistryRemoveCommand.java @@ -0,0 +1,69 @@ +package io.quarkus.cli; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.Map; + +import io.quarkus.cli.registry.BaseRegistryCommand; +import io.quarkus.cli.registry.RegistryClientMixin; +import io.quarkus.registry.config.RegistriesConfig; +import io.quarkus.registry.config.RegistriesConfigLocator; +import io.quarkus.registry.config.RegistryConfig; +import io.quarkus.registry.config.json.JsonRegistriesConfig; +import io.quarkus.registry.config.json.RegistriesConfigMapperHelper; +import picocli.CommandLine; + +@CommandLine.Command(name = "remove", sortOptions = false, showDefaultValues = true, mixinStandardHelpOptions = false, header = "Remove a Quarkus extension registry from the registry client configuration", description = "%n" + + "This command will remove a Quarkus extension registry from the registry client configuration", headerHeading = "%n", commandListHeading = "%nCommands:%n", synopsisHeading = "%nUsage: ", parameterListHeading = "%n", optionListHeading = "Options:%n") +public class RegistryRemoveCommand extends BaseRegistryCommand { + + @CommandLine.Mixin + protected RegistryClientMixin registryClient; + + @CommandLine.Parameters(arity = "0..1", paramLabel = "REGISTRY-ID[,REGISTRY-ID]", description = "Registry ID to remove from the registry client configuration%n" + + " Example:%n" + + " registry.quarkus.io%n" + + " registry.quarkus.acme.com,registry.quarkus.io%n") + String registryIds; + + @Override + public Integer call() throws Exception { + + registryClient.refreshRegistryCache(output); + + Path configYaml; + if (registryClient.getConfigArg() == null) { + configYaml = RegistriesConfigLocator.locateConfigYaml(); + if (configYaml == null) { + output.error("Failed to locate the registry client configuration file"); + return CommandLine.ExitCode.SOFTWARE; + } + } else { + configYaml = Paths.get(registryClient.getConfigArg()); + } + + final RegistriesConfig config = RegistriesConfigMapperHelper.deserialize(configYaml, JsonRegistriesConfig.class); + + final Map registries = new LinkedHashMap<>(config.getRegistries().size()); + config.getRegistries().forEach(r -> registries.put(r.getId(), r)); + boolean persist = false; + for (String registryId : registryIds.split(",")) { + if (registries.remove(registryId) == null) { + output.info("Registry " + registryId + " was not previously configured"); + } else { + output.info("Registry " + registryId + " was removed"); + persist = true; + } + } + + if (persist) { + final JsonRegistriesConfig jsonConfig = new JsonRegistriesConfig(); + jsonConfig.setRegistries(new ArrayList<>(registries.values())); + RegistriesConfigMapperHelper.serialize(jsonConfig, configYaml); + } + + return CommandLine.ExitCode.OK; + } +} diff --git a/devtools/cli/src/main/java/io/quarkus/cli/build/GradleRunner.java b/devtools/cli/src/main/java/io/quarkus/cli/build/GradleRunner.java index d95bfecfcca25..4ddad8aed716d 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/build/GradleRunner.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/build/GradleRunner.java @@ -225,7 +225,9 @@ void setGradleProperties(ArrayDeque args, boolean batchMode) { } args.add(registryClient.getRegistryClientProperty()); - final String configFile = System.getProperty(RegistriesConfigLocator.CONFIG_FILE_PATH_PROPERTY); + final String configFile = registryClient.getConfigArg() == null + ? System.getProperty(RegistriesConfigLocator.CONFIG_FILE_PATH_PROPERTY) + : registryClient.getConfigArg(); if (configFile != null) { args.add("-D" + RegistriesConfigLocator.CONFIG_FILE_PATH_PROPERTY + "=" + configFile); } diff --git a/devtools/cli/src/main/java/io/quarkus/cli/build/MavenRunner.java b/devtools/cli/src/main/java/io/quarkus/cli/build/MavenRunner.java index b1e1cdc55ec38..1381a83916b15 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/build/MavenRunner.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/build/MavenRunner.java @@ -26,6 +26,7 @@ import io.quarkus.devtools.commands.data.QuarkusCommandOutcome; import io.quarkus.devtools.project.BuildTool; import io.quarkus.devtools.project.QuarkusProject; +import io.quarkus.devtools.project.QuarkusProjectHelper; import io.quarkus.devtools.project.buildfile.MavenProjectBuildFile; import picocli.CommandLine; @@ -75,6 +76,7 @@ public BuildTool getBuildTool() { } QuarkusProject quarkusProject() throws Exception { + QuarkusProjectHelper.setToolsConfig(registryClient.resolveConfig()); return MavenProjectBuildFile.getProject(projectRoot, output, Version::clientVersion); } diff --git a/devtools/cli/src/main/java/io/quarkus/cli/registry/RegistryClientMixin.java b/devtools/cli/src/main/java/io/quarkus/cli/registry/RegistryClientMixin.java index cdf44d7eb7233..ff1af01cbc692 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/registry/RegistryClientMixin.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/registry/RegistryClientMixin.java @@ -1,6 +1,7 @@ package io.quarkus.cli.registry; import java.nio.file.Path; +import java.nio.file.Paths; import io.quarkus.cli.Version; import io.quarkus.cli.common.OutputOptionMixin; @@ -14,6 +15,8 @@ import io.quarkus.registry.ExtensionCatalogResolver; import io.quarkus.registry.RegistryResolutionException; import io.quarkus.registry.catalog.ExtensionCatalog; +import io.quarkus.registry.config.RegistriesConfig; +import io.quarkus.registry.config.RegistriesConfigLocator; import picocli.CommandLine; public class RegistryClientMixin { @@ -28,10 +31,21 @@ public final String getRegistryClientProperty() { "--refresh" }, description = "Refresh the local Quarkus extension registry cache", defaultValue = "false") boolean refresh = false; + @CommandLine.Option(paramLabel = "CONFIG", names = { "--config" }, description = "Configuration file") + String config; + public boolean enabled() { return true; } + public String getConfigArg() { + return config; + } + + public RegistriesConfig resolveConfig() { + return config == null ? RegistriesConfigLocator.resolveConfig() : RegistriesConfigLocator.load(Paths.get(config)); + } + public QuarkusProject createQuarkusProject(Path projectRoot, TargetQuarkusVersionGroup targetVersion, BuildTool buildTool, OutputOptionMixin log) throws RegistryResolutionException { ExtensionCatalog catalog = getExtensionCatalog(targetVersion, log); @@ -46,6 +60,9 @@ ExtensionCatalog getExtensionCatalog(TargetQuarkusVersionGroup targetVersion, Ou throws RegistryResolutionException { log.debug("Resolving Quarkus extension catalog for " + targetVersion); QuarkusProjectHelper.setMessageWriter(log); + if (enabled()) { + QuarkusProjectHelper.setToolsConfig(resolveConfig()); + } if (VALIDATE && targetVersion.isStreamSpecified() && !enabled()) { throw new UnsupportedOperationException( diff --git a/devtools/cli/src/test/java/io/quarkus/cli/CliNonProjectTest.java b/devtools/cli/src/test/java/io/quarkus/cli/CliNonProjectTest.java index 2277ca74cbf45..ffc8addf886f5 100644 --- a/devtools/cli/src/test/java/io/quarkus/cli/CliNonProjectTest.java +++ b/devtools/cli/src/test/java/io/quarkus/cli/CliNonProjectTest.java @@ -19,6 +19,10 @@ import org.junit.jupiter.api.Test; import io.quarkus.devtools.testing.RegistryClientTestHelper; +import io.quarkus.registry.config.RegistriesConfig; +import io.quarkus.registry.config.RegistriesConfigLocator; +import io.quarkus.registry.config.json.JsonRegistriesConfig; +import io.quarkus.registry.config.json.RegistriesConfigMapperHelper; import picocli.CommandLine; public class CliNonProjectTest { @@ -200,6 +204,67 @@ public void testRegistryRefresh() throws Exception { "Should contain '- registry.quarkus.io', found: " + result.stdout); } + @Test + public void testRegistryAddRemove() throws Exception { + + CliDriver.Result result; + + final Path testConfigYaml = workspaceRoot.resolve("test-registry-add-remove.yaml").toAbsolutePath(); + Files.deleteIfExists(testConfigYaml); + + assertThat(testConfigYaml).doesNotExist(); + result = CliDriver.execute(workspaceRoot, "registry", "add", "one,two", "--config", testConfigYaml.toString()); + Assertions.assertEquals(CommandLine.ExitCode.OK, result.exitCode, + "Expected OK return code." + result); + + assertThat(testConfigYaml).exists(); + RegistriesConfig testConfig = RegistriesConfigLocator.load(testConfigYaml); + assertThat(testConfig.getRegistries()).hasSize(2); + assertThat(testConfig.getRegistries().get(0).getId()).isEqualTo("one"); + assertThat(testConfig.getRegistries().get(1).getId()).isEqualTo("two"); + + result = CliDriver.execute(workspaceRoot, "registry", "add", "two,three", "--config", testConfigYaml.toString()); + Assertions.assertEquals(CommandLine.ExitCode.OK, result.exitCode, + "Expected OK return code." + result); + + testConfig = RegistriesConfigLocator.load(testConfigYaml); + assertThat(testConfig.getRegistries()).hasSize(3); + assertThat(testConfig.getRegistries().get(0).getId()).isEqualTo("one"); + assertThat(testConfig.getRegistries().get(1).getId()).isEqualTo("two"); + assertThat(testConfig.getRegistries().get(2).getId()).isEqualTo("three"); + + result = CliDriver.execute(workspaceRoot, "registry", "remove", "one,two", "--config", testConfigYaml.toString()); + Assertions.assertEquals(CommandLine.ExitCode.OK, result.exitCode, + "Expected OK return code." + result); + + testConfig = RegistriesConfigLocator.load(testConfigYaml); + assertThat(testConfig.getRegistries()).hasSize(1); + assertThat(testConfig.getRegistries().get(0).getId()).isEqualTo("three"); + + result = CliDriver.execute(workspaceRoot, "registry", "add", "four", "--config", testConfigYaml.toString()); + Assertions.assertEquals(CommandLine.ExitCode.OK, result.exitCode, + "Expected OK return code." + result); + + testConfig = RegistriesConfigLocator.load(testConfigYaml); + assertThat(testConfig.getRegistries()).hasSize(2); + assertThat(testConfig.getRegistries().get(0).getId()).isEqualTo("three"); + assertThat(testConfig.getRegistries().get(1).getId()).isEqualTo("four"); + + result = CliDriver.execute(workspaceRoot, "registry", "remove", "three,four,five", "--config", + testConfigYaml.toString()); + Assertions.assertEquals(CommandLine.ExitCode.OK, result.exitCode, + "Expected OK return code." + result); + + testConfig = RegistriesConfigMapperHelper.deserialize(testConfigYaml, JsonRegistriesConfig.class); + assertThat(testConfig.getRegistries()).isEmpty(); + + testConfig = RegistriesConfigLocator.load(testConfigYaml); + assertThat(testConfig.getRegistries()).hasSize(1); + assertThat(testConfig.getRegistries().get(0).getId()).isEqualTo("registry.quarkus.io"); + + Files.delete(testConfigYaml); + } + private static String getRequiredProperty(String name) { return Objects.requireNonNull(System.getProperty(name)); diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/QuarkusProjectHelper.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/QuarkusProjectHelper.java index b14bd5b13e098..7d01a781d8294 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/QuarkusProjectHelper.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/QuarkusProjectHelper.java @@ -176,6 +176,10 @@ public static RegistriesConfig toolsConfig() { return toolsConfig == null ? toolsConfig = RegistriesConfigLocator.resolveConfig() : toolsConfig; } + public static void setToolsConfig(RegistriesConfig config) { + toolsConfig = config; + } + public static void reset() { initRegistryClientEnabled(); toolsConfig = null; diff --git a/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/config/RegistriesConfigLocator.java b/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/config/RegistriesConfigLocator.java index 156166f6693a4..6e24e44c8e302 100644 --- a/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/config/RegistriesConfigLocator.java +++ b/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/config/RegistriesConfigLocator.java @@ -105,6 +105,11 @@ public static RegistriesConfig load(Reader configYaml) { } } + /** + * Returns the registry client configuration file or null, if the file could not be found. + * + * @return the registry client configuration file or null, if the file could not be found + */ public static Path locateConfigYaml() { final String prop = PropertiesUtil.getProperty(CONFIG_FILE_PATH_PROPERTY); Path configYaml; @@ -120,10 +125,19 @@ public static Path locateConfigYaml() { if (Files.exists(configYaml)) { return configYaml; } - configYaml = Paths.get(PropertiesUtil.getProperty("user.home")).resolve(CONFIG_RELATIVE_PATH); + configYaml = getDefaultConfigYamlLocation(); return Files.exists(configYaml) ? configYaml : null; } + /** + * Returns the default location of the registry client configuration file. + * + * @return the default location of the registry client configuration file + */ + public static Path getDefaultConfigYamlLocation() { + return Paths.get(PropertiesUtil.getProperty("user.home")).resolve(CONFIG_RELATIVE_PATH); + } + static RegistriesConfig completeRequiredConfig(RegistriesConfig original) { final JsonRegistriesConfig config = new JsonRegistriesConfig(); config.setDebug(original.isDebug()); diff --git a/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/config/json/JsonRegistriesConfig.java b/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/config/json/JsonRegistriesConfig.java index bffce7073d8e9..2f88b7b183445 100644 --- a/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/config/json/JsonRegistriesConfig.java +++ b/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/config/json/JsonRegistriesConfig.java @@ -7,7 +7,6 @@ import io.quarkus.registry.config.RegistriesConfig; import io.quarkus.registry.config.RegistryConfig; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Objects; @@ -15,7 +14,7 @@ public class JsonRegistriesConfig implements RegistriesConfig { private boolean debug; - private List registries = Collections.emptyList(); + private List registries = new ArrayList<>(); @Override @JsonDeserialize(contentUsing = JsonRegistryConfigDeserializer.class) @@ -25,13 +24,14 @@ public List getRegistries() { } public void setRegistries(List registries) { - this.registries = registries == null ? Collections.emptyList() : registries; + if (registries == null) { + this.registries.clear(); + } else { + this.registries = registries; + } } public void addRegistry(RegistryConfig registry) { - if (registries.isEmpty()) { - registries = new ArrayList<>(); - } registries.add(registry); } diff --git a/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/config/json/JsonRegistryConfigSerializer.java b/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/config/json/JsonRegistryConfigSerializer.java index 42f51409cd25f..0f8c5ddc39447 100644 --- a/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/config/json/JsonRegistryConfigSerializer.java +++ b/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/config/json/JsonRegistryConfigSerializer.java @@ -19,7 +19,8 @@ public class JsonRegistryConfigSerializer extends JsonSerializer