-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #30309 from iocanel/deploy-cli
Introduce deploy CLI commands
- Loading branch information
Showing
47 changed files
with
1,482 additions
and
273 deletions.
There are no files selected for viewing
68 changes: 68 additions & 0 deletions
68
core/deployment/src/main/java/io/quarkus/deployment/util/DeploymentUtil.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
package io.quarkus.deployment.util; | ||
|
||
import java.util.Arrays; | ||
import java.util.List; | ||
import java.util.Optional; | ||
import java.util.function.Predicate; | ||
import java.util.regex.Matcher; | ||
import java.util.regex.Pattern; | ||
import java.util.stream.Collectors; | ||
import java.util.stream.StreamSupport; | ||
|
||
import org.eclipse.microprofile.config.Config; | ||
import org.eclipse.microprofile.config.ConfigProvider; | ||
|
||
public class DeploymentUtil { | ||
|
||
public static final String DEPLOY = "quarkus.%s.deploy"; | ||
private static final Pattern QUARKUS_DEPLOY_PATTERN = Pattern.compile("quarkus\\.([^\\.]+)\\.deploy"); | ||
|
||
/** | ||
* Get the available deployers. | ||
* The list is obtained by checking for properties `quarkus.xxx.deploy`. | ||
* These properties have a default value and thus they will be found regardless of the | ||
* actual user configuration, so we check for property names instead. | ||
* | ||
* @return a {@link List} with all available deployers. | ||
*/ | ||
public static List<String> getDeployers() { | ||
Config config = ConfigProvider.getConfig(); | ||
return StreamSupport.stream(config.getPropertyNames().spliterator(), false) | ||
.map(p -> QUARKUS_DEPLOY_PATTERN.matcher(p)) | ||
.filter(Matcher::matches) | ||
.map(m -> m.group(1)) | ||
.collect(Collectors.toList()); | ||
} | ||
|
||
/** | ||
* @return a {@link Predicate} that tests if deploye is enabled. | ||
*/ | ||
public static Predicate<String> isDeployExplicitlyEnabled() { | ||
return deployer -> ConfigProvider.getConfig().getOptionalValue(String.format(DEPLOY, deployer), Boolean.class) | ||
.orElse(false); | ||
} | ||
|
||
/** | ||
* @return the name of the first deployer that is explicitly enabled | ||
*/ | ||
public static Optional<String> getEnabledDeployer() { | ||
return getDeployers().stream().filter(isDeployExplicitlyEnabled()).findFirst(); | ||
} | ||
|
||
/** | ||
* Check if any of the specified deployers is enabled. | ||
* | ||
* @param the name of the speicifed deployers. | ||
* @return true if the specified deploy is explicitly enabled, fasle otherwise. | ||
*/ | ||
public static boolean isDeploymentEnabled(String... deployer) { | ||
return getDeployers().stream().filter(isDeployExplicitlyEnabled()).anyMatch(d -> Arrays.asList(deployer).contains(d)); | ||
} | ||
|
||
/** | ||
* @return true if deployment is explicitly enabled using: `quarkus.<deployment target>.deploy=true`. | ||
*/ | ||
public static boolean isDeploymentEnabled() { | ||
return getEnabledDeployer().isPresent(); | ||
} | ||
} |
63 changes: 63 additions & 0 deletions
63
devtools/cli/src/main/java/io/quarkus/cli/BuildToolContext.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package io.quarkus.cli; | ||
|
||
import java.nio.file.Path; | ||
import java.util.List; | ||
|
||
import io.quarkus.cli.common.BuildOptions; | ||
import io.quarkus.cli.common.PropertiesOptions; | ||
import io.quarkus.cli.common.RunModeOption; | ||
import io.quarkus.devtools.project.BuildTool; | ||
import io.quarkus.devtools.project.QuarkusProjectHelper; | ||
|
||
/** | ||
* Context class that holds all the required info that is passed to {@link BuildToolDelegatingCommand}. | ||
* In many cases a subcommand delegates to the parent. It's cleaner to keep all the state in a single | ||
* class to pass along. | ||
*/ | ||
public class BuildToolContext { | ||
|
||
private final Path projectRoot; | ||
private final RunModeOption runModeOption; | ||
private final BuildOptions buildOptions; | ||
private final PropertiesOptions propertiesOptions; | ||
private final List<String> forcedExtensions; | ||
private final List<String> params; | ||
|
||
public BuildToolContext(Path projectRoot, RunModeOption runModeOption, BuildOptions buildOptions, | ||
PropertiesOptions propertiesOptions, List<String> forcedExtensions, List<String> params) { | ||
this.projectRoot = projectRoot; | ||
this.runModeOption = runModeOption; | ||
this.buildOptions = buildOptions; | ||
this.propertiesOptions = propertiesOptions; | ||
this.forcedExtensions = forcedExtensions; | ||
this.params = params; | ||
} | ||
|
||
public Path getProjectRoot() { | ||
return projectRoot; | ||
} | ||
|
||
public RunModeOption getRunModeOption() { | ||
return runModeOption; | ||
} | ||
|
||
public BuildOptions getBuildOptions() { | ||
return buildOptions; | ||
} | ||
|
||
public PropertiesOptions getPropertiesOptions() { | ||
return propertiesOptions; | ||
} | ||
|
||
public List<String> getForcedExtensions() { | ||
return forcedExtensions; | ||
} | ||
|
||
public List<String> getParams() { | ||
return params; | ||
} | ||
|
||
public BuildTool getBuildTool() { | ||
return QuarkusProjectHelper.detectExistingBuildTool(projectRoot); // nullable | ||
} | ||
} |
165 changes: 165 additions & 0 deletions
165
devtools/cli/src/main/java/io/quarkus/cli/BuildToolDelegatingCommand.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
package io.quarkus.cli; | ||
|
||
import java.nio.file.Path; | ||
import java.nio.file.Paths; | ||
import java.util.ArrayList; | ||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Optional; | ||
import java.util.concurrent.Callable; | ||
|
||
import io.quarkus.cli.build.BuildSystemRunner; | ||
import io.quarkus.cli.common.BuildOptions; | ||
import io.quarkus.cli.common.HelpOption; | ||
import io.quarkus.cli.common.OutputOptionMixin; | ||
import io.quarkus.cli.common.PropertiesOptions; | ||
import io.quarkus.cli.common.RunModeOption; | ||
import io.quarkus.cli.registry.ToggleRegistryClientMixin; | ||
import io.quarkus.cli.utils.GradleInitScript; | ||
import io.quarkus.devtools.project.BuildTool; | ||
import picocli.CommandLine; | ||
import picocli.CommandLine.ExitCode; | ||
import picocli.CommandLine.Parameters; | ||
|
||
/** | ||
* A cli command that delegates to the quarkus build system. | ||
*/ | ||
public class BuildToolDelegatingCommand implements Callable<Integer> { | ||
|
||
private static final String GRADLE_NO_BUILD_CACHE = "--no-build-cache"; | ||
private static final String GRADLE_NO_DAEMON = "--no-daemon"; | ||
|
||
@CommandLine.Spec | ||
protected CommandLine.Model.CommandSpec spec; | ||
|
||
@CommandLine.Mixin(name = "output") | ||
protected OutputOptionMixin output; | ||
|
||
@CommandLine.Mixin | ||
protected ToggleRegistryClientMixin registryClient; | ||
|
||
@CommandLine.Mixin | ||
protected HelpOption helpOption; | ||
|
||
@CommandLine.ArgGroup(exclusive = false, validate = false) | ||
protected PropertiesOptions propertiesOptions = new PropertiesOptions(); | ||
|
||
@CommandLine.Mixin | ||
private RunModeOption runMode; | ||
|
||
@CommandLine.ArgGroup(order = 1, exclusive = false, validate = false, heading = "%nBuild options:%n") | ||
private BuildOptions buildOptions = new BuildOptions(); | ||
|
||
@Parameters(description = "Additional parameters passed to the build system") | ||
private List<String> params = new ArrayList<>(); | ||
|
||
private Path projectRoot; | ||
|
||
private Path projectRoot() { | ||
if (projectRoot == null) { | ||
projectRoot = output.getTestDirectory(); | ||
if (projectRoot == null) { | ||
projectRoot = Paths.get(System.getProperty("user.dir")).toAbsolutePath(); | ||
} | ||
} | ||
return projectRoot; | ||
} | ||
|
||
public BuildSystemRunner getRunner(BuildToolContext context) { | ||
return BuildSystemRunner.getRunner(output, context.getPropertiesOptions(), registryClient, context.getProjectRoot(), | ||
context.getBuildTool()); | ||
} | ||
|
||
public void populateContext(BuildToolContext context) { | ||
} | ||
|
||
public Optional<BuildToolDelegatingCommand> getParentCommand() { | ||
return Optional.empty(); | ||
} | ||
|
||
public final Integer call(BuildToolContext context) throws Exception { | ||
try { | ||
populateContext(context); | ||
prepare(context); | ||
BuildSystemRunner runner = getRunner(context); | ||
if (getParentCommand().isPresent()) { | ||
return getParentCommand().get().call(context); | ||
} | ||
|
||
if (context.getRunModeOption().isDryRun()) { | ||
output.info("Dry run option detected. Target command(s):"); | ||
} | ||
String action = getAction(context) | ||
.orElseThrow(() -> new IllegalStateException("Unknown action for " + runner.getBuildTool().name())); | ||
BuildSystemRunner.BuildCommandArgs commandArgs = runner.prepareAction(action, context.getBuildOptions(), | ||
context.getRunModeOption(), context.getParams()); | ||
if (context.getRunModeOption().isDryRun()) { | ||
output.info(" " + commandArgs.showCommand()); | ||
return ExitCode.OK; | ||
} | ||
return runner.run(commandArgs); | ||
} catch (Exception e) { | ||
return output.handleCommandException(e, "Unable to build: " + e.getMessage()); | ||
} | ||
} | ||
|
||
@Override | ||
public final Integer call() throws Exception { | ||
return call(new BuildToolContext(projectRoot(), runMode, buildOptions, propertiesOptions, new ArrayList<>(), | ||
new ArrayList<>(params))); | ||
} | ||
|
||
public void prepare(BuildToolContext context) { | ||
BuildSystemRunner runner = getRunner(context); | ||
if (runner.getBuildTool() == BuildTool.GRADLE) { | ||
prepareGradle(context); | ||
} | ||
|
||
if (runner.getBuildTool() == BuildTool.MAVEN) { | ||
prepareMaven(context); | ||
} | ||
} | ||
|
||
public void prepareMaven(BuildToolContext context) { | ||
BuildSystemRunner runner = getRunner(context); | ||
BuildSystemRunner.BuildCommandArgs compileArgs = runner.prepareAction("compiler:compile", context.getBuildOptions(), | ||
context.getRunModeOption(), | ||
context.getParams()); | ||
|
||
//Let's check if we need to precompile and if so, include the command to the dry-run output. | ||
//Note, that if the command should delegate to a the parent command dry-run output should be handled by the parent. | ||
if (getParentCommand().isPresent()) { | ||
return; | ||
} else if (context.getRunModeOption().isDryRun()) { | ||
output.info(" " + compileArgs.showCommand()); | ||
} else { | ||
int compileExitCode = runner.run(compileArgs); | ||
if (compileExitCode != ExitCode.OK) { | ||
throw new RuntimeException("Failed to compile. Compilation exited with exit code:" + compileExitCode); | ||
} | ||
} | ||
} | ||
|
||
public void prepareGradle(BuildToolContext context) { | ||
if (!context.getParams().contains(GRADLE_NO_BUILD_CACHE)) { | ||
context.getParams().add(GRADLE_NO_BUILD_CACHE); | ||
} | ||
|
||
if (!context.getParams().contains(GRADLE_NO_DAEMON)) { | ||
context.getParams().add(GRADLE_NO_DAEMON); | ||
} | ||
if (!context.getForcedExtensions().isEmpty()) { | ||
GradleInitScript.populateForExtensions(context.getForcedExtensions(), context.getParams()); | ||
} | ||
} | ||
|
||
public Map<BuildTool, String> getActionMapping() { | ||
return Collections.emptyMap(); | ||
} | ||
|
||
public Optional<String> getAction(BuildToolContext context) { | ||
return getParentCommand().map(cmd -> cmd.getAction(context)) | ||
.orElse(Optional.ofNullable(getActionMapping().get(getRunner(context).getBuildTool()))); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package io.quarkus.cli; | ||
|
||
import java.util.Map; | ||
|
||
import io.quarkus.cli.deploy.Kind; | ||
import io.quarkus.cli.deploy.Knative; | ||
import io.quarkus.cli.deploy.Kubernetes; | ||
import io.quarkus.cli.deploy.Minikube; | ||
import io.quarkus.cli.deploy.Openshift; | ||
import io.quarkus.devtools.project.BuildTool; | ||
import picocli.CommandLine; | ||
|
||
@CommandLine.Command(name = "deploy", sortOptions = false, mixinStandardHelpOptions = false, header = "Deploy application.", subcommands = { | ||
Kubernetes.class, Openshift.class, Knative.class, Kind.class, Minikube.class, | ||
}, headerHeading = "%n", commandListHeading = "%nCommands:%n", synopsisHeading = "%nUsage: ", optionListHeading = "%nOptions:%n") | ||
public class Deploy extends BuildToolDelegatingCommand { | ||
|
||
private static final Map<BuildTool, String> ACTION_MAPPING = Map.of(BuildTool.MAVEN, "quarkus:deploy", | ||
BuildTool.GRADLE, "deploy"); | ||
|
||
@Override | ||
public Map<BuildTool, String> getActionMapping() { | ||
return ACTION_MAPPING; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "Deploy {}"; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.