From 86811232f219a00e853e0be572a0194d5fd8f14e Mon Sep 17 00:00:00 2001 From: hkosim Date: Wed, 17 Dec 2025 14:56:56 +0100 Subject: [PATCH 1/3] #1653: Implemented getInstalledVersion and getInstalledEdition for Docker. --- .../tools/ide/tool/ToolCommandlet.java | 8 ++ .../devonfw/tools/ide/tool/docker/Docker.java | 99 ++++++++++++++++++- .../tools/ide/tool/kubectl/KubeCtl.java | 27 +++++ 3 files changed, 130 insertions(+), 4 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/ToolCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/tool/ToolCommandlet.java index 2d80702b3b..d7591daf5b 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/ToolCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/ToolCommandlet.java @@ -920,6 +920,14 @@ public void reset() { this.executionDirectory = null; } + /** + * @param command the binary that will be searched in the PATH e.g. docker + * @return true if the command is available to use + */ + protected boolean isCommandAvailable(String command) { + return this.context.getPath().hasBinaryOnPath(command); + } + /** * @param step the {@link Step} to get {@link Step#asSuccess() success logger} from. May be {@code null}. * @return the {@link IdeSubLogger} from {@link Step#asSuccess()} or {@link IdeContext#success()} as fallback. 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 index 9a57adf4c4..ca334d1e32 100644 --- 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 @@ -4,6 +4,8 @@ import java.util.Arrays; import java.util.List; import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import com.devonfw.tools.ide.common.Tag; import com.devonfw.tools.ide.context.IdeContext; @@ -23,6 +25,14 @@ public class Docker extends GlobalToolCommandlet { private static final String PODMAN = "podman"; + + private static final Pattern RDCTL_CLIENT_VERSION_PATTERN = Pattern.compile("client version:\\s*v([\\d.]+)", Pattern.CASE_INSENSITIVE); + + private static final Pattern DOCKER_DESKTOP_SERVER_VERSION_PATTERN = Pattern.compile("(?m)^\\s*Server:\\s*Docker Desktop\\s*([\\d.]+)\\b"); + + private static final Pattern DOCKER_STATUS_RUNNING_PATTERN = Pattern.compile("(?m)^\\s*Status\\s+running\\b", Pattern.CASE_INSENSITIVE); + + /** * The constructor. * @@ -38,6 +48,14 @@ public String getBinaryName() { return detectContainerRuntime(); } + private boolean isDockerInstalled() { + return isCommandAvailable("docker"); + } + + private boolean isRancherDesktopInstalled() { + return isCommandAvailable("rdctl"); + } + private String detectContainerRuntime() { if (isCommandAvailable(this.tool)) { return this.tool; @@ -48,10 +66,6 @@ private String detectContainerRuntime() { } } - private boolean isCommandAvailable(String command) { - return this.context.getPath().hasBinaryOnPath(command); - } - @Override public boolean isExtract() { @@ -82,6 +96,83 @@ protected List getInstallPackageManagerCommands() { String.format("sudo apt install -y --allow-downgrades rancher-desktop=%s*", resolvedVersion)))); } + @Override + public VersionIdentifier getInstalledVersion() { + + if (!isDockerInstalled()) { + this.context.error("Couldn't get installed version of " + this.getName()); + return null; + } + + if (isRancherDesktopInstalled()) { + return getRancherDesktopClientVersion(); + } else { + boolean isDockerDesktopPreviouslyRunning = isDockerDesktopRunning(); + + if (!isDockerDesktopPreviouslyRunning) { + this.context.newProcess().run("docker", "desktop", "start"); + } + + try { + return getDockerDesktopServerVersion(); + } finally { + if (!isDockerDesktopPreviouslyRunning) { + this.context.newProcess().run("docker", "desktop", "stop"); + } + } + } + } + + private VersionIdentifier getRancherDesktopClientVersion() { + + String output = this.context.newProcess().runAndGetSingleOutput("rdctl", "version"); + Matcher matcher = RDCTL_CLIENT_VERSION_PATTERN.matcher(output); + + if (matcher.find()) { + String version = matcher.group(1); + return VersionIdentifier.of(version); + } else { + this.context.error("Couldn't get installed version of " + this.getName()); + return null; + } + } + + private boolean isDockerDesktopRunning() { + + List outputs = this.context.newProcess().runAndGetOutput("docker", "desktop", "status"); + String singleLineOutput = String.join("\n", outputs); + Matcher matcher = DOCKER_STATUS_RUNNING_PATTERN.matcher(singleLineOutput); + + return matcher.find(); + } + + private VersionIdentifier getDockerDesktopServerVersion() { + List outputs = this.context.newProcess().runAndGetOutput("docker", "version"); + String singleLineOutput = String.join("\n", outputs); + Matcher matcher = DOCKER_DESKTOP_SERVER_VERSION_PATTERN.matcher(singleLineOutput); + + if (matcher.find()) { + String version = matcher.group(1); + return VersionIdentifier.of(version); + } + return null; + } + + + @Override + public String getInstalledEdition() { + if (!isDockerInstalled()) { + this.context.error("Couldn't get installed edition of " + this.getName()); + return null; + } + + if (isRancherDesktopInstalled()) { + return "rancher"; + } else { + return "desktop"; + } + } + @Override public void uninstall() { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/kubectl/KubeCtl.java b/cli/src/main/java/com/devonfw/tools/ide/tool/kubectl/KubeCtl.java index c976413ee6..b3a85e78c4 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/kubectl/KubeCtl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/kubectl/KubeCtl.java @@ -1,17 +1,22 @@ package com.devonfw.tools.ide.tool.kubectl; +import java.util.List; import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import com.devonfw.tools.ide.common.Tag; import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.tool.DelegatingToolCommandlet; import com.devonfw.tools.ide.tool.docker.Docker; +import com.devonfw.tools.ide.version.VersionIdentifier; /** * {@link DelegatingToolCommandlet} for Kubectl. */ public class KubeCtl extends DelegatingToolCommandlet { + private static final Pattern KUBECTL_VERSION_PATTERN = Pattern.compile("Client Version: \\s*v([\\d.]+)"); /** * The constructor. @@ -22,4 +27,26 @@ public KubeCtl(IdeContext context) { super(context, "kubectl", Set.of(Tag.KUBERNETES), Docker.class); } + + @Override + public VersionIdentifier getInstalledVersion() { + + if (!isCommandAvailable(this.tool)) { + return super.getInstalledVersion(); + } + + List outputs = this.context.newProcess().runAndGetOutput(this.tool, "version", "--client"); + String singleLineOutput = String.join("\n", outputs); + + Matcher matcher = KUBECTL_VERSION_PATTERN.matcher(singleLineOutput); + if (matcher.find()) { + String version = matcher.group(1); + this.context.info("Installation found: kubectl v" + version); + return VersionIdentifier.of(version); + } else { + return super.getInstalledVersion(); + } + + } + } From 475c1cecf8c68c318cf7d22c830a539fe685978f Mon Sep 17 00:00:00 2001 From: hkosim Date: Mon, 22 Dec 2025 13:56:03 +0100 Subject: [PATCH 2/3] #1653: Added getVersion for Linux and restructured the methods. --- .../tools/ide/tool/ToolCommandlet.java | 17 +++++ .../devonfw/tools/ide/tool/docker/Docker.java | 65 +++++++------------ .../tools/ide/tool/kubectl/KubeCtl.java | 10 +-- 3 files changed, 42 insertions(+), 50 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/ToolCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/tool/ToolCommandlet.java index d7591daf5b..62d0a80a5e 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/ToolCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/ToolCommandlet.java @@ -6,6 +6,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import com.devonfw.tools.ide.commandlet.Commandlet; import com.devonfw.tools.ide.common.Tag; @@ -928,6 +930,21 @@ protected boolean isCommandAvailable(String command) { return this.context.getPath().hasBinaryOnPath(command); } + /** + * @param output the raw output string from executed command e.g. 'docker version' + * @param pattern Regular Expression pattern that filters out the unnecessary texts. + * @return version that has been processed. + */ + protected VersionIdentifier resolveVersionWithPattern(String output, Pattern pattern) { + Matcher matcher = pattern.matcher(output); + + if (matcher.find()) { + return VersionIdentifier.of(matcher.group(1)); + } else { + return null; + } + } + /** * @param step the {@link Step} to get {@link Step#asSuccess() success logger} from. May be {@code null}. * @return the {@link IdeSubLogger} from {@link Step#asSuccess()} or {@link IdeContext#success()} as fallback. 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 index ca334d1e32..b3ff1063f6 100644 --- 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 @@ -4,7 +4,6 @@ import java.util.Arrays; import java.util.List; import java.util.Set; -import java.util.regex.Matcher; import java.util.regex.Pattern; import com.devonfw.tools.ide.common.Tag; @@ -28,10 +27,9 @@ public class Docker extends GlobalToolCommandlet { private static final Pattern RDCTL_CLIENT_VERSION_PATTERN = Pattern.compile("client version:\\s*v([\\d.]+)", Pattern.CASE_INSENSITIVE); - private static final Pattern DOCKER_DESKTOP_SERVER_VERSION_PATTERN = Pattern.compile("(?m)^\\s*Server:\\s*Docker Desktop\\s*([\\d.]+)\\b"); - - private static final Pattern DOCKER_STATUS_RUNNING_PATTERN = Pattern.compile("(?m)^\\s*Status\\s+running\\b", Pattern.CASE_INSENSITIVE); + private static final Pattern DOCKER_DESKTOP_WINDOWS_VERSION_PATTERN = Pattern.compile("DisplayVersion\\s+REG_SZ\\s+([0-9]+(?:\\.[0-9]+){2})"); + private static final Pattern DOCKER_DESKTOP_LINUX_VERSION_PATTERN = Pattern.compile("^([0-9]+(?:\\.[0-9]+){1,2})"); /** * The constructor. @@ -107,60 +105,45 @@ public VersionIdentifier getInstalledVersion() { if (isRancherDesktopInstalled()) { return getRancherDesktopClientVersion(); } else { - boolean isDockerDesktopPreviouslyRunning = isDockerDesktopRunning(); - - if (!isDockerDesktopPreviouslyRunning) { - this.context.newProcess().run("docker", "desktop", "start"); + VersionIdentifier parsedVersion = switch (this.context.getSystemInfo().getOs()) { + case WINDOWS -> getDockerDesktopVersionWindows(); + case LINUX -> getDockerDesktopVersionLinux(); + default -> null; + }; + + if (parsedVersion == null) { + this.context.error("Couldn't get installed version of " + this.getName()); } - try { - return getDockerDesktopServerVersion(); - } finally { - if (!isDockerDesktopPreviouslyRunning) { - this.context.newProcess().run("docker", "desktop", "stop"); - } - } + return parsedVersion; } } - private VersionIdentifier getRancherDesktopClientVersion() { + private VersionIdentifier getDockerDesktopVersionWindows() { - String output = this.context.newProcess().runAndGetSingleOutput("rdctl", "version"); - Matcher matcher = RDCTL_CLIENT_VERSION_PATTERN.matcher(output); + String dockerDesktopVersionWindowsCommand = "reg query \"HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Docker Desktop\" /v DisplayVersion"; - if (matcher.find()) { - String version = matcher.group(1); - return VersionIdentifier.of(version); - } else { - this.context.error("Couldn't get installed version of " + this.getName()); - return null; - } + List outputs = this.context.newProcess().runAndGetOutput("cmd.exe", "/c", dockerDesktopVersionWindowsCommand); + String singleLineOutput = String.join(" ", outputs); + return super.resolveVersionWithPattern(singleLineOutput, DOCKER_DESKTOP_WINDOWS_VERSION_PATTERN); } - private boolean isDockerDesktopRunning() { - - List outputs = this.context.newProcess().runAndGetOutput("docker", "desktop", "status"); - String singleLineOutput = String.join("\n", outputs); - Matcher matcher = DOCKER_STATUS_RUNNING_PATTERN.matcher(singleLineOutput); + private VersionIdentifier getDockerDesktopVersionLinux() { - return matcher.find(); + String dockerDesktopVersionLinuxCommand = "apt list --installed | grep docker-desktop | awk '{print $2}'"; + String output = this.context.newProcess().runAndGetSingleOutput("bash", "-lc", dockerDesktopVersionLinuxCommand); + return super.resolveVersionWithPattern(output, DOCKER_DESKTOP_LINUX_VERSION_PATTERN); } - private VersionIdentifier getDockerDesktopServerVersion() { - List outputs = this.context.newProcess().runAndGetOutput("docker", "version"); - String singleLineOutput = String.join("\n", outputs); - Matcher matcher = DOCKER_DESKTOP_SERVER_VERSION_PATTERN.matcher(singleLineOutput); + private VersionIdentifier getRancherDesktopClientVersion() { - if (matcher.find()) { - String version = matcher.group(1); - return VersionIdentifier.of(version); - } - return null; + String output = this.context.newProcess().runAndGetSingleOutput("rdctl", "version"); + return super.resolveVersionWithPattern(output, RDCTL_CLIENT_VERSION_PATTERN); } - @Override public String getInstalledEdition() { + if (!isDockerInstalled()) { this.context.error("Couldn't get installed edition of " + this.getName()); return null; diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/kubectl/KubeCtl.java b/cli/src/main/java/com/devonfw/tools/ide/tool/kubectl/KubeCtl.java index b3a85e78c4..d3cd0c25b2 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/kubectl/KubeCtl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/kubectl/KubeCtl.java @@ -2,7 +2,6 @@ import java.util.List; import java.util.Set; -import java.util.regex.Matcher; import java.util.regex.Pattern; import com.devonfw.tools.ide.common.Tag; @@ -38,14 +37,7 @@ public VersionIdentifier getInstalledVersion() { List outputs = this.context.newProcess().runAndGetOutput(this.tool, "version", "--client"); String singleLineOutput = String.join("\n", outputs); - Matcher matcher = KUBECTL_VERSION_PATTERN.matcher(singleLineOutput); - if (matcher.find()) { - String version = matcher.group(1); - this.context.info("Installation found: kubectl v" + version); - return VersionIdentifier.of(version); - } else { - return super.getInstalledVersion(); - } + return super.resolveVersionWithPattern(singleLineOutput, KUBECTL_VERSION_PATTERN); } From b8d300f268b0efbf7f62f700f1b97bcb005e3664 Mon Sep 17 00:00:00 2001 From: hkosim Date: Mon, 22 Dec 2025 14:01:16 +0100 Subject: [PATCH 3/3] #1653: Added a new line in CHANGELOG. --- CHANGELOG.adoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index b60f424883..78ef67e4ba 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -6,6 +6,8 @@ This file documents all notable changes to https://github.com/devonfw/IDEasy[IDE Release with new features and bugfixes: +* https://github.com/devonfw/IDEasy/issues/1653[#1653]: Implementation of getEdition and getVersion for Docker + The full list of changes for this release can be found in https://github.com/devonfw/IDEasy/milestone/39?closed=1[milestone 2026.01.001]. == 2025.12.001