diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java index d79afdf33..df95c0eab 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java @@ -12,6 +12,7 @@ import com.devonfw.tools.ide.tool.aws.Aws; import com.devonfw.tools.ide.tool.az.Azure; import com.devonfw.tools.ide.tool.cobigen.Cobigen; +import com.devonfw.tools.ide.tool.docker.Docker; import com.devonfw.tools.ide.tool.eclipse.Eclipse; import com.devonfw.tools.ide.tool.gcviewer.GcViewer; import com.devonfw.tools.ide.tool.gh.Gh; @@ -84,6 +85,7 @@ public CommandletManagerImpl(IdeContext context) { add(new Aws(context)); add(new Cobigen(context)); add(new Jmc(context)); + add(new Docker(context)); add(new Sonar(context)); } diff --git a/cli/src/main/java/com/devonfw/tools/ide/process/ProcessContextImpl.java b/cli/src/main/java/com/devonfw/tools/ide/process/ProcessContextImpl.java index dea607c2c..2afec83ad 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/process/ProcessContextImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/process/ProcessContextImpl.java @@ -1,13 +1,5 @@ package com.devonfw.tools.ide.process; -import com.devonfw.tools.ide.cli.CliException; -import com.devonfw.tools.ide.common.SystemPath; -import com.devonfw.tools.ide.context.IdeContext; -import com.devonfw.tools.ide.environment.VariableLine; -import com.devonfw.tools.ide.log.IdeSubLogger; -import com.devonfw.tools.ide.os.SystemInfoImpl; -import com.devonfw.tools.ide.util.FilenameUtil; - import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -22,6 +14,14 @@ import java.util.Objects; import java.util.stream.Collectors; +import com.devonfw.tools.ide.cli.CliException; +import com.devonfw.tools.ide.common.SystemPath; +import com.devonfw.tools.ide.context.IdeContext; +import com.devonfw.tools.ide.environment.VariableLine; +import com.devonfw.tools.ide.log.IdeSubLogger; +import com.devonfw.tools.ide.os.SystemInfoImpl; +import com.devonfw.tools.ide.util.FilenameUtil; + /** * Implementation of {@link ProcessContext}. */ @@ -116,7 +116,7 @@ public ProcessResult run(ProcessMode processMode) { if (this.executable == null) { throw new IllegalStateException("Missing executable to run process!"); } - List args = new ArrayList<>(this.arguments.size() + 2); + List args = new ArrayList<>(this.arguments.size() + 4); String interpreter = addExecutable(this.executable.toString(), args); args.addAll(this.arguments); if (this.context.debug().isEnabled()) { @@ -140,7 +140,7 @@ public ProcessResult run(ProcessMode processMode) { List err = null; if (processMode == ProcessMode.DEFAULT_CAPTURE) { - try (BufferedReader outReader = new BufferedReader(new InputStreamReader(process.getInputStream()));) { + try (BufferedReader outReader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { out = outReader.lines().collect(Collectors.toList()); } try (BufferedReader errReader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) { @@ -192,8 +192,7 @@ private String createCommandMessage(String interpreter, String suffix) { } } sb.append(suffix); - String message = sb.toString(); - return message; + return sb.toString(); } private String getSheBang(Path file) { @@ -314,6 +313,10 @@ private String addExecutable(String executable, List args) { } args.add(bash); } + if ("msi".equalsIgnoreCase(fileExtension)) { + args.add(0, "/i"); + args.add(0, "msiexec"); + } args.add(executable); return interpreter; } diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/GlobalToolCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/tool/GlobalToolCommandlet.java index 739337f09..62b5afd7d 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/GlobalToolCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/GlobalToolCommandlet.java @@ -1,5 +1,11 @@ package com.devonfw.tools.ide.tool; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import java.util.Set; + import com.devonfw.tools.ide.common.Tag; import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.io.FileAccess; @@ -9,10 +15,6 @@ import com.devonfw.tools.ide.repo.ToolRepository; import com.devonfw.tools.ide.version.VersionIdentifier; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Set; - /** * {@link ToolCommandlet} that is installed globally. */ @@ -24,13 +26,70 @@ public abstract class GlobalToolCommandlet extends ToolCommandlet { * @param context the {@link IdeContext}. * @param tool the {@link #getName() tool name}. * @param tags the {@link #getTags() tags} classifying the tool. Should be created via {@link Set#of(Object) Set.of} - * method. + * method. */ public GlobalToolCommandlet(IdeContext context, String tool, Set tags) { super(context, tool, tags); } + /** + * Performs the installation of the {@link #getName() tool} via a package manager. + * + * @param silent - {@code true} if called recursively to suppress verbose logging, {@code false} otherwise. + * @param commands - A {@link Map} containing the commands used to perform the installation for each package manager. + * @return {@code true} if the tool was newly installed, {@code false} if the tool was already installed before and + * nothing has changed. + */ + protected boolean installWithPackageManger(Map> commands, boolean silent) { + + Path binaryPath = this.context.getPath().findBinary(Path.of(getBinaryName())); + + if (binaryPath != null && Files.exists(binaryPath) && !this.context.isForceMode()) { + IdeLogLevel level = silent ? IdeLogLevel.DEBUG : IdeLogLevel.INFO; + this.context.level(level).log("{} is already installed at {}", this.tool, binaryPath); + return false; + } + + Path bashPath = this.context.getPath().findBinary(Path.of("bash")); + if (bashPath == null || !Files.exists(bashPath)) { + context.warning("Bash was not found on this machine. Not Proceeding with installation of tool " + this.tool); + return false; + } + + PackageManager foundPackageManager = null; + for (PackageManager pm : commands.keySet()) { + if (Files.exists(this.context.getPath().findBinary(Path.of(pm.toString().toLowerCase())))) { + foundPackageManager = pm; + break; + } + } + + int finalExitCode = 0; + if (foundPackageManager == null) { + context.warning("No supported Package Manager found for installation"); + return false; + } else { + List commandList = commands.get(foundPackageManager); + if (commandList != null) { + for (String command : commandList) { + ProcessContext pc = this.context.newProcess().errorHandling(ProcessErrorHandling.WARNING).executable(bashPath) + .addArgs("-c", command); + finalExitCode = pc.run(); + } + } + } + + if (finalExitCode == 0) { + this.context.success("Successfully installed {}", this.tool); + } else { + this.context.warning("{} was not successfully installed", this.tool); + return false; + } + postInstall(); + return true; + } + @Override protected boolean isExtract() { @@ -79,5 +138,4 @@ protected boolean doInstall(boolean silent) { postInstall(); return true; } - } diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/PackageManager.java b/cli/src/main/java/com/devonfw/tools/ide/tool/PackageManager.java new file mode 100644 index 000000000..9af593534 --- /dev/null +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/PackageManager.java @@ -0,0 +1,5 @@ +package com.devonfw.tools.ide.tool; + +public enum PackageManager { + APT, ZYPPER, YUM, DNF +} diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/docker/Docker.java b/cli/src/main/java/com/devonfw/tools/ide/tool/docker/Docker.java new file mode 100644 index 000000000..0119a3d32 --- /dev/null +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/docker/Docker.java @@ -0,0 +1,79 @@ +package com.devonfw.tools.ide.tool.docker; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.devonfw.tools.ide.common.Tag; +import com.devonfw.tools.ide.context.IdeContext; +import com.devonfw.tools.ide.os.SystemArchitecture; +import com.devonfw.tools.ide.repo.ToolRepository; +import com.devonfw.tools.ide.tool.GlobalToolCommandlet; +import com.devonfw.tools.ide.tool.PackageManager; +import com.devonfw.tools.ide.version.VersionIdentifier; + +public class Docker extends GlobalToolCommandlet { + /** + * The constructor. + * + * @param context the {@link IdeContext}. + */ + public Docker(IdeContext context) { + + super(context, "docker", Set.of(Tag.DOCKER)); + } + + @Override + public boolean isExtract() { + + return switch (context.getSystemInfo().getOs()) { + case WINDOWS -> false; + case MAC -> context.getSystemInfo().getArchitecture().equals(SystemArchitecture.ARM64); + case LINUX -> true; + }; + } + + @Override + protected boolean doInstall(boolean silent) { + + if (context.getSystemInfo().isLinux()) { + return installWithPackageManger(getPackageMangerCommands(), silent); + } else { + return super.doInstall(silent); + } + } + + private Map> getPackageMangerCommands() { + + Map> commands = new HashMap<>(); + + String edition = getEdition(); + ToolRepository toolRepository = this.context.getDefaultToolRepository(); + VersionIdentifier configuredVersion = getConfiguredVersion(); + String resolvedVersion = toolRepository.resolveVersion(this.tool, edition, configuredVersion).toString(); + + commands.put(PackageManager.ZYPPER, Arrays.asList( + "sudo zypper addrepo https://download.opensuse.org/repositories/isv:/Rancher:/stable/rpm/isv:Rancher:stable.repo", + String.format("sudo zypper --no-gpg-checks install rancher-desktop=%s*", resolvedVersion))); + + commands.put(PackageManager.APT, Arrays.asList( + "curl -s https://download.opensuse.org/repositories/isv:/Rancher:/stable/deb/Release.key | gpg --dearmor | sudo dd status=none of=/usr/share/keyrings/isv-rancher-stable-archive-keyring.gpg", + "echo 'deb [signed-by=/usr/share/keyrings/isv-rancher-stable-archive-keyring.gpg] https://download.opensuse.org/repositories/isv:/Rancher:/stable/deb/ ./' | sudo dd status=none of=/etc/apt/sources.list.d/isv-rancher-stable.list", + "sudo apt update", + String.format("sudo apt install -y --allow-downgrades rancher-desktop=%s*", resolvedVersion))); + + return commands; + } + + @Override + protected String getBinaryName() { + + if (context.getSystemInfo().isLinux()) { + return "rancher-desktop"; + } else { + return super.getBinaryName(); + } + } +} \ No newline at end of file