From fe77423521ab6dd6432c9980bc0c052d8fbcbe12 Mon Sep 17 00:00:00 2001 From: Ioannis Canellos Date: Tue, 22 Nov 2022 18:07:36 +0200 Subject: [PATCH] feat: image cli forced deps/ext in gradle projects --- .../quarkus/cli/build/BaseBuildCommand.java | 19 +++ .../quarkus/cli/image/BaseImageCommand.java | 57 ++++++- .../main/java/io/quarkus/cli/image/Build.java | 43 ++---- .../java/io/quarkus/cli/image/Buildpack.java | 23 +-- .../java/io/quarkus/cli/image/Docker.java | 26 ++-- .../main/java/io/quarkus/cli/image/Jib.java | 23 +-- .../java/io/quarkus/cli/image/Openshift.java | 22 +-- .../main/java/io/quarkus/cli/image/Push.java | 35 ++--- .../quarkus/cli/utils/GradleInitScript.java | 65 ++++++++ .../quarkus/cli/image/CliImageGradleTest.java | 141 ++++++++++++++++++ 10 files changed, 347 insertions(+), 107 deletions(-) create mode 100644 devtools/cli/src/main/java/io/quarkus/cli/utils/GradleInitScript.java create mode 100644 devtools/cli/src/test/java/io/quarkus/cli/image/CliImageGradleTest.java diff --git a/devtools/cli/src/main/java/io/quarkus/cli/build/BaseBuildCommand.java b/devtools/cli/src/main/java/io/quarkus/cli/build/BaseBuildCommand.java index 62e4694bf354b..8150fe8b77b4c 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/build/BaseBuildCommand.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/build/BaseBuildCommand.java @@ -2,6 +2,8 @@ import java.nio.file.Path; import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; import io.quarkus.cli.common.HelpOption; import io.quarkus.cli.common.OutputOptionMixin; @@ -28,6 +30,7 @@ public class BaseBuildCommand { protected PropertiesOptions propertiesOptions = new PropertiesOptions(); Path projectRoot; + protected List forcedExtensions = new ArrayList<>(); public Path projectRoot() { if (projectRoot == null) { @@ -43,4 +46,20 @@ public BuildSystemRunner getRunner() { BuildTool buildTool = QuarkusProjectHelper.detectExistingBuildTool(projectRoot()); // nullable return BuildSystemRunner.getRunner(output, propertiesOptions, registryClient, projectRoot(), buildTool); } + + public List getForcedExtensions() { + return forcedExtensions; + } + + /** + * Commands using `@ParentCommand` need to set the ouput. + * This is needed for testing purposes. + * More specifically --cli-test-dir relies on this. + * + * @param output The command ouput + */ + public void setOutput(OutputOptionMixin output) { + this.output = output; + } + } diff --git a/devtools/cli/src/main/java/io/quarkus/cli/image/BaseImageCommand.java b/devtools/cli/src/main/java/io/quarkus/cli/image/BaseImageCommand.java index 6de819ba90a73..d36d246a48e3b 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/image/BaseImageCommand.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/image/BaseImageCommand.java @@ -7,16 +7,18 @@ import java.util.concurrent.Callable; import io.quarkus.cli.build.BaseBuildCommand; +import io.quarkus.cli.build.BuildSystemRunner; import io.quarkus.cli.common.BuildOptions; import io.quarkus.cli.common.PropertiesOptions; import io.quarkus.cli.common.RunModeOption; +import io.quarkus.cli.utils.GradleInitScript; import io.quarkus.devtools.project.BuildTool; import picocli.CommandLine; +import picocli.CommandLine.ExitCode; import picocli.CommandLine.Parameters; public class BaseImageCommand extends BaseBuildCommand implements Callable { - protected static final String QUARKUS_FORCED_EXTENSIONS = "quarkus.application.forced-extensions"; protected static final String QUARKUS_CONTAINER_IMAGE_EXTENSION_KEY_PREFIX = "io.quarkus:quarkus-container-image-"; protected static final String QUARKUS_CONTAINER_IMAGE_BUILDER = "quarkus.container-image.builder"; @@ -24,7 +26,6 @@ public class BaseImageCommand extends BaseBuildCommand implements Callable ACTION_MAPPING = Map.of(BuildTool.MAVEN, "quarkus:image-build", BuildTool.GRADLE, "imageBuild"); @@ -46,8 +47,54 @@ public void populateImageConfiguration(Map properties) { imageOptions.name.ifPresent(name -> properties.put(QUARKUS_CONTAINER_IMAGE_NAME, name)); imageOptions.tag.ifPresent(tag -> properties.put(QUARKUS_CONTAINER_IMAGE_TAG, tag)); imageOptions.registry.ifPresent(registry -> properties.put(QUARKUS_CONTAINER_IMAGE_REGISTRY, registry)); + } + + @Override + public Integer call() throws Exception { + try { + populateImageConfiguration(propertiesOptions.properties); + BuildSystemRunner runner = getRunner(); + + String action = getAction() + .orElseThrow(() -> new IllegalStateException("Unknown image action for " + runner.getBuildTool().name())); + + if (runner.getBuildTool() == BuildTool.GRADLE) { + prepareGradle(); + } + + if (runner.getBuildTool() == BuildTool.MAVEN) { + prepareMaven(); + } + BuildSystemRunner.BuildCommandArgs commandArgs = runner.prepareAction(action, buildOptions, runMode, params); + if (runMode.isDryRun()) { + System.out.println("Dry run option detected. Target command:"); + System.out.println(" " + commandArgs.showCommand()); + return ExitCode.OK; + } + return runner.run(commandArgs); + } catch (Exception e) { + return output.handleCommandException(e, "Unable to build image: " + e.getMessage()); + } + } + + public void prepareMaven() { if (runMode.isDryRun()) { - properties.put(QUARKUS_CONTAINER_IMAGE_DRY_RUN, "true"); + return; + } + BuildSystemRunner runner = getRunner(); + BuildSystemRunner.BuildCommandArgs compileArgs = runner.prepareAction("compiler:compile", buildOptions, runMode, + params); + int compileExitCode = runner.run(compileArgs); + if (compileExitCode != ExitCode.OK) { + throw new RuntimeException("Failed to compile. Compilation exited with exit code:" + compileExitCode); + } + } + + public void prepareGradle() { + if (!getForcedExtensions().isEmpty()) { + // Ensure that params is modifiable + params = new ArrayList<>(this.params); + GradleInitScript.populateForExtensions(getForcedExtensions(), params); } } @@ -59,8 +106,4 @@ public PropertiesOptions getPropertiesOptions() { return this.propertiesOptions; } - @Override - public Integer call() throws Exception { - return 0; - } } diff --git a/devtools/cli/src/main/java/io/quarkus/cli/image/Build.java b/devtools/cli/src/main/java/io/quarkus/cli/image/Build.java index ae528f585896f..493acc7a0a74f 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/image/Build.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/image/Build.java @@ -3,10 +3,8 @@ import java.util.Map; import java.util.Optional; -import io.quarkus.cli.build.BuildSystemRunner; import io.quarkus.devtools.project.BuildTool; import picocli.CommandLine; -import picocli.CommandLine.ExitCode; @CommandLine.Command(name = "build", sortOptions = false, showDefaultValues = true, mixinStandardHelpOptions = false, header = "Build a container image.", description = "%n" + "This command will build a container image for the project.", subcommands = { Docker.class, Buildpack.class, @@ -20,8 +18,17 @@ public class Build extends BaseImageCommand { BuildTool.GRADLE, "imageBuild"); @Override - public void populateImageConfiguration(Map properties) { - super.populateImageConfiguration(properties); + public void prepareGradle() { + //For gradle the builder options is meaningless so let's sepcicy the builder using `--builder` + //This is done only for `--builder` as this is the only option that is processed from the gradle task. + //The rest of the options are passed to the container image processors. + Optional optionalBuilder = Optional + .ofNullable(propertiesOptions.properties.remove(QUARKUS_CONTAINER_IMAGE_BUILDER)); + String builder = optionalBuilder.orElse("docker"); + params.add("--builder=" + builder); + forcedExtensions.add(QUARKUS_CONTAINER_IMAGE_EXTENSION_KEY_PREFIX + builder); + // Always call super.prepareGralde after adding forcedExtension or else forcedExtensions will be ignored. + super.prepareGradle(); } @Override @@ -29,34 +36,6 @@ public Optional getAction() { return Optional.ofNullable(ACTION_MAPPING.get(getRunner().getBuildTool())); } - @Override - public Integer call() throws Exception { - try { - populateImageConfiguration(propertiesOptions.properties); - BuildSystemRunner runner = getRunner(); - - String action = getAction().orElseThrow( - () -> new IllegalStateException("Unknown image build action for " + runner.getBuildTool().name())); - BuildSystemRunner.BuildCommandArgs commandArgs = runner.prepareAction(action, buildOptions, runMode, params); - if (runMode.isDryRun()) { - System.out.println("Dry run option detected. Target command:"); - System.out.println(" " + commandArgs.showCommand()); - return ExitCode.OK; - } - if (getRunner().getBuildTool() == BuildTool.MAVEN) { - BuildSystemRunner.BuildCommandArgs compileArgs = runner.prepareAction("compiler:compile", buildOptions, runMode, - params); - int compileExitCode = runner.run(compileArgs); - if (compileExitCode != ExitCode.OK) { - return compileExitCode; - } - } - return runner.run(commandArgs); - } catch (Exception e) { - return output.handleCommandException(e, "Unable to build image: " + e.getMessage()); - } - } - @Override public String toString() { return "Build {imageOptions='" + imageOptions + "'}"; diff --git a/devtools/cli/src/main/java/io/quarkus/cli/image/Buildpack.java b/devtools/cli/src/main/java/io/quarkus/cli/image/Buildpack.java index 26bd0f27439b5..a8c5bfe85d2b3 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/image/Buildpack.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/image/Buildpack.java @@ -1,6 +1,8 @@ package io.quarkus.cli.image; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Optional; @@ -51,10 +53,9 @@ public class Buildpack extends BaseImageCommand { BaseImageCommand parent; @Override - public void populateImageConfiguration(Map properties) { - super.populateImageConfiguration(properties); + public Integer call() throws Exception { + Map properties = parent.getPropertiesOptions().properties; properties.put(QUARKUS_CONTAINER_IMAGE_BUILDER, BUILDPACK); - properties.put(QUARKUS_FORCED_EXTENSIONS, QUARKUS_CONTAINER_IMAGE_EXTENSION_KEY_PREFIX + BUILDPACK); builderImage.ifPresent(i -> properties .put(BUILDPACK_CONFIG_PREFIX + (buildOptions.buildNative ? NATIVE_BUILDER_IMAGE : JVM_BUILDER_IMAGE), i)); @@ -64,16 +65,18 @@ public void populateImageConfiguration(Map properties) { dockerHost.ifPresent(h -> properties.put(BUILDPACK_CONFIG_PREFIX + DOCKER_HOST, h)); runImage.ifPresent(i -> properties.put(BUILDPACK_CONFIG_PREFIX + RUN_IMAGE, i)); buildEnv.forEach((k, v) -> properties.put(BUILDPACK_CONFIG_PREFIX + BUILDER_ENV + "." + k, v)); + + parent.getForcedExtensions().add(QUARKUS_CONTAINER_IMAGE_EXTENSION_KEY_PREFIX + BUILDPACK); + parent.runMode = runMode; + parent.buildOptions = buildOptions; + parent.imageOptions = imageOptions; + parent.setOutput(output); + return parent.call(); } @Override - public Integer call() throws Exception { - try { - populateImageConfiguration(parent.getPropertiesOptions().properties); - return parent.call(); - } catch (Exception e) { - return output.handleCommandException(e, "Unable to build image: " + e.getMessage()); - } + public List getForcedExtensions() { + return Arrays.asList(QUARKUS_CONTAINER_IMAGE_EXTENSION_KEY_PREFIX + BUILDPACK); } @Override diff --git a/devtools/cli/src/main/java/io/quarkus/cli/image/Docker.java b/devtools/cli/src/main/java/io/quarkus/cli/image/Docker.java index d8a7476b645c3..6323b4029d498 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/image/Docker.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/image/Docker.java @@ -22,23 +22,19 @@ public class Docker extends BaseImageCommand { @ParentCommand BaseImageCommand parent; - @Override - public void populateImageConfiguration(Map properties) { - super.populateImageConfiguration(properties); - properties.put(QUARKUS_CONTAINER_IMAGE_BUILDER, DOCKER); - properties.put(QUARKUS_FORCED_EXTENSIONS, QUARKUS_CONTAINER_IMAGE_EXTENSION_KEY_PREFIX + DOCKER); - dockerFile.ifPresent(d -> properties.put(DOCKER_CONFIG_PREFIX - + (buildOptions.buildNative ? DOCKERFILE_NATIVE_PATH : DOCKERFILE_JVM_PATH), d)); - } - @Override public Integer call() throws Exception { - try { - populateImageConfiguration(parent.getPropertiesOptions().properties); - return parent.call(); - } catch (Exception e) { - return output.handleCommandException(e, "Unable to build image: " + e.getMessage()); - } + Map properties = parent.getPropertiesOptions().properties; + properties.put(QUARKUS_CONTAINER_IMAGE_BUILDER, DOCKER); + dockerFile.ifPresent(d -> properties + .put(DOCKER_CONFIG_PREFIX + (buildOptions.buildNative ? DOCKERFILE_NATIVE_PATH : DOCKERFILE_JVM_PATH), d)); + + parent.getForcedExtensions().add(QUARKUS_CONTAINER_IMAGE_EXTENSION_KEY_PREFIX + DOCKER); + parent.runMode = runMode; + parent.buildOptions = buildOptions; + parent.imageOptions = imageOptions; + parent.setOutput(output); + return parent.call(); } @Override diff --git a/devtools/cli/src/main/java/io/quarkus/cli/image/Jib.java b/devtools/cli/src/main/java/io/quarkus/cli/image/Jib.java index 54bcb9868e5a5..2d9bd74fe8fc6 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/image/Jib.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/image/Jib.java @@ -1,6 +1,7 @@ package io.quarkus.cli.image; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -77,11 +78,9 @@ public class Jib extends BaseImageCommand { BaseImageCommand parent; @Override - public void populateImageConfiguration(Map properties) { - super.populateImageConfiguration(properties); + public Integer call() throws Exception { + Map properties = parent.getPropertiesOptions().properties; properties.put(QUARKUS_CONTAINER_IMAGE_BUILDER, JIB); - properties.put(QUARKUS_FORCED_EXTENSIONS, QUARKUS_CONTAINER_IMAGE_EXTENSION_KEY_PREFIX + JIB); - baseImage.ifPresent( d -> properties.put(JIB_CONFIG_PREFIX + (buildOptions.buildNative ? BASE_NATIVE_IMAGE : BASE_JVM_IMAGE), d)); @@ -133,16 +132,18 @@ public void populateImageConfiguration(Map properties) { if (buildOptions.offline) { properties.put(JIB_CONFIG_PREFIX + OFFLINE_MODE, "true"); } + + parent.getForcedExtensions().add(QUARKUS_CONTAINER_IMAGE_EXTENSION_KEY_PREFIX + JIB); + parent.runMode = runMode; + parent.buildOptions = buildOptions; + parent.imageOptions = imageOptions; + parent.setOutput(output); + return parent.call(); } @Override - public Integer call() throws Exception { - try { - populateImageConfiguration(parent.getPropertiesOptions().properties); - return parent.call(); - } catch (Exception e) { - return output.handleCommandException(e, "Unable to build image: " + e.getMessage()); - } + public List getForcedExtensions() { + return Arrays.asList(QUARKUS_CONTAINER_IMAGE_EXTENSION_KEY_PREFIX + JIB); } @Override diff --git a/devtools/cli/src/main/java/io/quarkus/cli/image/Openshift.java b/devtools/cli/src/main/java/io/quarkus/cli/image/Openshift.java index e403086d99c15..83a683afcdca2 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/image/Openshift.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/image/Openshift.java @@ -1,6 +1,7 @@ package io.quarkus.cli.image; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Optional; @@ -59,10 +60,9 @@ public class Openshift extends BaseImageCommand implements Callable { BaseImageCommand parent; @Override - public void populateImageConfiguration(Map properties) { - super.populateImageConfiguration(properties); + public Integer call() throws Exception { + Map properties = parent.getPropertiesOptions().properties; properties.put(QUARKUS_CONTAINER_IMAGE_BUILDER, OPENSHIFT); - properties.put(QUARKUS_FORCED_EXTENSIONS, QUARKUS_CONTAINER_IMAGE_EXTENSION_KEY_PREFIX + OPENSHIFT); properties.put(OPENSHIFT_CONFIG_PREFIX + BUILD_STRATEGY, buildStrategy.orElse("docker")); baseImage.ifPresent(d -> properties @@ -87,16 +87,18 @@ public void populateImageConfiguration(Map properties) { properties.put(OPENSHIFT_CONFIG_PREFIX + (buildOptions.buildNative ? NATIVE_FILE_NAME : JAR_FILE_NAME), artifactFilename); } + + parent.getForcedExtensions().add(QUARKUS_CONTAINER_IMAGE_EXTENSION_KEY_PREFIX + OPENSHIFT); + parent.runMode = runMode; + parent.buildOptions = buildOptions; + parent.imageOptions = imageOptions; + parent.setOutput(output); + return parent.call(); } @Override - public Integer call() throws Exception { - try { - populateImageConfiguration(parent.getPropertiesOptions().properties); - return parent.call(); - } catch (Exception e) { - return output.handleCommandException(e, "Unable to build image: " + e.getMessage()); - } + public List getForcedExtensions() { + return Arrays.asList(QUARKUS_CONTAINER_IMAGE_EXTENSION_KEY_PREFIX + OPENSHIFT); } @Override diff --git a/devtools/cli/src/main/java/io/quarkus/cli/image/Push.java b/devtools/cli/src/main/java/io/quarkus/cli/image/Push.java index 0b6fcd690f0ea..5b73ee263baf9 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/image/Push.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/image/Push.java @@ -1,12 +1,12 @@ package io.quarkus.cli.image; +import java.util.Arrays; +import java.util.List; import java.util.Map; import java.util.Optional; -import io.quarkus.cli.build.BuildSystemRunner; import io.quarkus.devtools.project.BuildTool; import picocli.CommandLine; -import picocli.CommandLine.ExitCode; @CommandLine.Command(name = "push", sortOptions = false, showDefaultValues = true, mixinStandardHelpOptions = false, header = "Push a container image.", description = "%n" + "This command will build and push a container image for the project.", footer = "%n", headerHeading = "%n", commandListHeading = "%nCommands:%n", synopsisHeading = "%nUsage: ", parameterListHeading = "%n", optionListHeading = "Options:%n") @@ -37,8 +37,8 @@ public class Push extends BaseImageCommand { public boolean alsoBuild; @Override - public void populateImageConfiguration(Map properties) { - super.populateImageConfiguration(properties); + public Integer call() throws Exception { + Map properties = getPropertiesOptions().properties; registryUsername.ifPresent(u -> properties.put(QUARKUS_CONTAINER_IMAGE_USERNAME, u)); if (registryPasswordStdin && !runMode.isDryRun()) { @@ -53,8 +53,7 @@ public void populateImageConfiguration(Map properties) { } else { properties.put(QUARKUS_CONTAINER_IMAGE_BUILD, "false"); } - - properties.put(QUARKUS_FORCED_EXTENSIONS, QUARKUS_CONTAINER_IMAGE_EXTENSION); + return super.call(); } @Override @@ -62,25 +61,17 @@ public Optional getAction() { return Optional.ofNullable(ACTION_MAPPING.get(getRunner().getBuildTool())); } - @Override - public Integer call() throws Exception { - try { - populateImageConfiguration(propertiesOptions.properties); - BuildSystemRunner runner = getRunner(); - String action = getAction().orElseThrow( - () -> new IllegalStateException("Unknown image push action for " + runner.getBuildTool().name())); - BuildSystemRunner.BuildCommandArgs commandArgs = runner.prepareAction(action, buildOptions, runMode, params); - if (runMode.isDryRun()) { - System.out.println("Dry run option detected. Target command:"); - System.out.println(commandArgs.showCommand()); - return ExitCode.OK; - } - return runner.run(commandArgs); - } catch (Exception e) { - return output.handleCommandException(e, "Unable to push image: " + e.getMessage()); + public void prepareMaven() { + if (alsoBuild) { + super.prepareMaven(); } } + @Override + public List getForcedExtensions() { + return Arrays.asList(QUARKUS_CONTAINER_IMAGE_EXTENSION); + } + @Override public String toString() { return "Push {imageOptions='" + imageOptions + "'}"; diff --git a/devtools/cli/src/main/java/io/quarkus/cli/utils/GradleInitScript.java b/devtools/cli/src/main/java/io/quarkus/cli/utils/GradleInitScript.java new file mode 100644 index 0000000000000..4391608675de3 --- /dev/null +++ b/devtools/cli/src/main/java/io/quarkus/cli/utils/GradleInitScript.java @@ -0,0 +1,65 @@ +package io.quarkus.cli.utils; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +public class GradleInitScript { + + public static final String ALL_PROJECTS = "allprojects {"; + public static final String APPLY_PLUGIN_JAVA = "apply plugin: 'java'"; + public static final String DEPENDENCIES = "dependencies {"; + public static final String DEPENDENCY = "implementation '%s'"; + public static final String CLOSE = "}"; + public static final String TAB = " "; + public static final String NEWLINE = "\n"; + + /** + * Create an init script that adds the specidied extensions and populate the arguments + * that should be passed to the gradle command, so that it loads the generated init script. + * The command will append to the specified argument list: `--init-script=` + * + * @param forcedExtensions The forcced extension to add to the init script + * @param args The argument list + */ + public static void populateForExtensions(Collection forcedExtensions, Collection args) { + List gavs = forcedExtensions.stream() + .map(String::trim) + .map(e -> e + ":${quarkusPlatformVersion}") + .collect(Collectors.toList()); + Path initScriptPath = GradleInitScript.createInitScript(gavs); + args.add("--init-script=" + initScriptPath.toAbsolutePath().toString()); + } + + public static Path createInitScript(List gavs) { + try { + Path path = Files.createTempFile("quarkus-gradle-init", ""); + createInitScript(path, gavs); + return path; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static void createInitScript(Path path, List gavs) { + StringBuilder sb = new StringBuilder(); + sb.append(ALL_PROJECTS).append(NEWLINE); + sb.append(TAB).append(APPLY_PLUGIN_JAVA).append(NEWLINE); + sb.append(TAB).append(DEPENDENCIES).append(NEWLINE); + + for (String gav : gavs) { + sb.append(TAB).append(TAB).append(String.format(DEPENDENCY, gav)).append(NEWLINE); + } + + sb.append(TAB).append(CLOSE).append(NEWLINE); + sb.append(CLOSE).append(NEWLINE); + try { + Files.writeString(path, sb.toString()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/devtools/cli/src/test/java/io/quarkus/cli/image/CliImageGradleTest.java b/devtools/cli/src/test/java/io/quarkus/cli/image/CliImageGradleTest.java new file mode 100644 index 0000000000000..5a9a5bed26c41 --- /dev/null +++ b/devtools/cli/src/test/java/io/quarkus/cli/image/CliImageGradleTest.java @@ -0,0 +1,141 @@ +package io.quarkus.cli.image; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import io.quarkus.cli.CliDriver; +import io.quarkus.cli.build.ExecuteUtil; +import io.quarkus.cli.build.GradleRunner; +import io.quarkus.devtools.testing.RegistryClientTestHelper; +import picocli.CommandLine; + +/** + * Similar to CliProjecGradleTest .. + */ +public class CliImageGradleTest { + + static final Path testProjectRoot = Paths.get(System.getProperty("user.dir")).toAbsolutePath() + .resolve("target/test-project/"); + static final Path workspaceRoot = testProjectRoot.resolve("CliImageGradleTest"); + static final Path wrapperRoot = testProjectRoot.resolve("gradle-wrapper"); + + Path project; + static File gradle; + + @BeforeAll + public static void setupTestRegistry() { + RegistryClientTestHelper.enableRegistryClientTestConfig(); + } + + @AfterAll + public static void cleanupTestRegistry() { + RegistryClientTestHelper.disableRegistryClientTestConfig(); + } + + @BeforeEach + public void setupTestDirectories() throws Exception { + CliDriver.deleteDir(workspaceRoot); + project = workspaceRoot.resolve("code-with-quarkus"); + } + + @BeforeAll + static void startGradleDaemon() throws Exception { + CliDriver.deleteDir(wrapperRoot); + + CliDriver.Result result = CliDriver.execute(workspaceRoot, "create", "app", "--gradle", "--verbose", "-e", "-B", + "--no-code", + "-o", testProjectRoot.toString(), + "gradle-wrapper"); + Assertions.assertEquals(CommandLine.ExitCode.OK, result.getExitCode(), "Expected OK return code." + result); + Assertions.assertTrue(result.getStdout().contains("SUCCESS"), + "Expected confirmation that the project has been created." + result); + + gradle = ExecuteUtil.findWrapper(wrapperRoot, GradleRunner.windowsWrapper, GradleRunner.otherWrapper); + + List args = new ArrayList<>(); + args.add(gradle.getAbsolutePath()); + args.add("--daemon"); + CliDriver.preserveLocalRepoSettings(args); + + result = CliDriver.executeArbitraryCommand(wrapperRoot, args.toArray(new String[0])); + Assertions.assertEquals(0, result.getExitCode(), "Gradle daemon should start properly"); + } + + @AfterAll + static void stopGradleDaemon() throws Exception { + if (gradle != null) { + List args = new ArrayList<>(); + args.add(gradle.getAbsolutePath()); + args.add("--stop"); + CliDriver.preserveLocalRepoSettings(args); + + CliDriver.Result result = CliDriver.executeArbitraryCommand(wrapperRoot, args.toArray(new String[0])); + Assertions.assertEquals(0, result.getExitCode(), "Gradle daemon should stop properly"); + } + } + + @Test + public void testUsage() throws Exception { + CliDriver.Result result = CliDriver.execute(workspaceRoot, "create", "app", "--gradle", "--verbose", "-e", "-B"); + assertEquals(CommandLine.ExitCode.OK, result.getExitCode(), "Expected OK return code." + result); + + // 1 image --dry-run + result = CliDriver.execute(project, "image", "--dry-run"); + assertEquals(CommandLine.ExitCode.OK, result.getExitCode(), "Expected OK return code." + result); + + // 2 image build --dry-run + result = CliDriver.execute(project, "image", "build", "--dry-run"); + assertEquals(CommandLine.ExitCode.OK, result.getExitCode(), "Expected OK return code." + result); + assertTrue(result.getStdout().contains("--builder=docker")); // Should fallback to docker + assertTrue(result.getStdout().contains("--init-script=")); + + result = CliDriver.execute(project, "image", "build", "docker", "--dry-run"); + assertEquals(CommandLine.ExitCode.OK, result.getExitCode(), "Expected OK return code." + result); + assertTrue(result.getStdout().contains("--builder=docker")); + assertTrue(result.getStdout().contains("--init-script=")); + + result = CliDriver.execute(project, "image", "build", "jib", "--dry-run"); + assertEquals(CommandLine.ExitCode.OK, result.getExitCode(), "Expected OK return code." + result); + assertTrue(result.getStdout().contains("--builder=jib")); + assertTrue(result.getStdout().contains("--init-script=")); + + result = CliDriver.execute(project, "image", "build", "buildpack", "--dry-run"); + assertEquals(CommandLine.ExitCode.OK, result.getExitCode(), "Expected OK return code." + result); + assertTrue(result.getStdout().contains("--builder=buildpack")); + assertTrue(result.getStdout().contains("--init-script=")); + + result = CliDriver.execute(project, "image", "build", "openshift", "--dry-run"); + assertEquals(CommandLine.ExitCode.OK, result.getExitCode(), "Expected OK return code." + result); + assertTrue(result.getStdout().contains("--builder=openshift")); + assertTrue(result.getStdout().contains("--init-script=")); + + result = CliDriver.execute(project, "image", "build", "--group=mygroup", "--name=myname", "--tag=1.0", "--dry-run"); + assertEquals(CommandLine.ExitCode.OK, result.getExitCode(), "Expected OK return code." + result); + assertTrue(result.getStdout().contains("-Dquarkus.container-image.group=mygroup")); + assertTrue(result.getStdout().contains("-Dquarkus.container-image.name=myname")); + assertTrue(result.getStdout().contains("-Dquarkus.container-image.tag=1.0")); + + // 3 image push --dry-run + result = CliDriver.execute(project, "image", "push", "--dry-run"); + assertEquals(CommandLine.ExitCode.OK, result.getExitCode(), "Expected OK return code." + result); + assertTrue(result.getStdout().contains("-Dquarkus.container-image.build=false")); + assertTrue(result.getStdout().contains("--init-script=")); + + // 4 image push --also-build --dry-run + result = CliDriver.execute(project, "image", "push", "--also-build", "--dry-run"); + assertEquals(CommandLine.ExitCode.OK, result.getExitCode(), "Expected OK return code." + result); + assertTrue(result.getStdout().contains("-Dquarkus.container-image.build=true")); + } +}