From 6d8e1fed33b4dfbbb39cec0191959162a792a855 Mon Sep 17 00:00:00 2001 From: jakozian Date: Tue, 28 Apr 2026 09:09:26 +0200 Subject: [PATCH 01/21] #1688: Remove unnecessary log messages --- .../java/com/devonfw/tools/ide/tool/GlobalToolCommandlet.java | 2 -- cli/src/main/java/com/devonfw/tools/ide/tool/docker/Docker.java | 2 -- 2 files changed, 4 deletions(-) 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 95c520a3b6..9cfffa4379 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 @@ -190,14 +190,12 @@ protected List getInstallPackageManagerCommands() { @Override public VersionIdentifier getInstalledVersion() { //TODO: handle "get-version " - LOG.error("Couldn't get installed version of " + this.getName()); return null; } @Override public String getInstalledEdition() { //TODO: handle "get-edition " - LOG.error("Couldn't get installed edition of " + this.getName()); return null; } 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 01b3881c82..4533c2fe99 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 @@ -100,7 +100,6 @@ protected List getInstallPackageManagerCommands() { public VersionIdentifier getInstalledVersion() { if (!isDockerInstalled()) { - LOG.error("Couldn't get installed version of " + this.getName()); return null; } @@ -148,7 +147,6 @@ private VersionIdentifier getRancherDesktopClientVersion() { public String getInstalledEdition() { if (!isDockerInstalled()) { - LOG.error("Couldn't get installed edition of {}", this.getName()); return null; } From a1f92f1b1194714df103d3737d7fe2d3e6956c19 Mon Sep 17 00:00:00 2001 From: jakozian Date: Tue, 28 Apr 2026 09:12:42 +0200 Subject: [PATCH 02/21] #1688: Add to CHANGELOG --- CHANGELOG.adoc | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 8094e25c47..af837488a4 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -16,6 +16,7 @@ Release with new features and bugfixes: * https://github.com/devonfw/IDEasy/issues/1724[#1724]: Add gui commandlet * https://github.com/devonfw/IDEasy/issues/1853[#1853]: Add ARM releases for VSCode on Mac * https://github.com/devonfw/IDEasy/issues/1723[#1723]: Add commandlet for GitHub Copilot CLI +* https://github.com/devonfw/IDEasy/issues/1688[#1688]: Remove unnecessary message in the CLI when installing a new tool The full list of changes for this release can be found in https://github.com/devonfw/IDEasy/milestone/44?closed=1[milestone 2026.05.001]. From 3968af7399b80a5397112cc07cb4cbc962873dd9 Mon Sep 17 00:00:00 2001 From: jakozian Date: Tue, 28 Apr 2026 09:57:40 +0200 Subject: [PATCH 03/21] #1688: Override installation path getter to look in windows registry --- .../tools/ide/tool/pgadmin/PgAdmin.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/pgadmin/PgAdmin.java b/cli/src/main/java/com/devonfw/tools/ide/tool/pgadmin/PgAdmin.java index 46e1499497..9652701931 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/pgadmin/PgAdmin.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/pgadmin/PgAdmin.java @@ -1,5 +1,8 @@ package com.devonfw.tools.ide.tool.pgadmin; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -7,6 +10,7 @@ import com.devonfw.tools.ide.common.Tag; import com.devonfw.tools.ide.context.IdeContext; +import com.devonfw.tools.ide.os.WindowsHelper; import com.devonfw.tools.ide.tool.GlobalToolCommandlet; import com.devonfw.tools.ide.tool.NativePackageManager; import com.devonfw.tools.ide.tool.PackageManagerCommand; @@ -72,4 +76,30 @@ protected String getBinaryName() { return "pgadmin4"; } + + @Override + protected Path getInstallationPath(String edition, VersionIdentifier resolvedVersion) { + if (super.getInstallationPath(edition, resolvedVersion) == null) { + if (this.context.getSystemInfo().isWindows()) { + return getExecutableFolderFromWindowsRegistry(); + } + } + return null; + } + + private Path getExecutableFolderFromWindowsRegistry() { + + WindowsHelper windowsHelper = WindowsHelper.get(this.context); + String registryPath = "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\pgAdmin 4v9_is1"; + String displayIcon = windowsHelper.getRegistryValue(registryPath, "DisplayIcon"); + if (displayIcon != null) { + Path executablePath = Paths.get(displayIcon); + if (Files.isExecutable(executablePath)) { + Path installationDir = executablePath.getParent(); + this.context.getPath().setPath(getName(), installationDir); + return installationDir; + } + } + return null; + } } From 6a1c228baeb1a20ec04538b4b5ae530202c1b2a8 Mon Sep 17 00:00:00 2001 From: jakozian Date: Tue, 28 Apr 2026 09:58:35 +0200 Subject: [PATCH 04/21] #1688: Fix installation flow and thus mention exception --- .../com/devonfw/tools/ide/tool/GlobalToolCommandlet.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 95c520a3b6..920c0eae48 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 @@ -159,7 +159,7 @@ protected ToolInstallation doInstall(ToolInstallRequest request) { executable = fileAccess.findFirst(downloadBinaryPath, Files::isExecutable, false); } ProcessContext pc = this.context.newProcess().errorHandling(ProcessErrorHandling.LOG_WARNING).executable(executable); - int exitCode = pc.run(ProcessMode.BACKGROUND).getExitCode(); + int exitCode = pc.run(ProcessMode.BACKGROUND_SILENT).getExitCode(); if (tmpDir != null) { fileAccess.delete(tmpDir); } @@ -174,7 +174,8 @@ protected ToolInstallation doInstall(ToolInstallRequest request) { } installationPath = getInstallationPath(toolEdition.edition(), resolvedVersion); if (installationPath == null) { - LOG.warn("Could not find binary {} on PATH after installation.", getBinaryName()); + throw new CliException("The tool " + this.tool + " is about to be installed. Please complete the installation and if required " + + "reboot your machine. Then rerun the command to start the tool.", 2); } return createToolInstallation(installationPath, resolvedVersion, true, pc, false); } From 3c5b463abe8d55cee89ee5ad849503eae41e6a99 Mon Sep 17 00:00:00 2001 From: jakozian Date: Tue, 28 Apr 2026 10:05:02 +0200 Subject: [PATCH 05/21] #861: Add to CHANGELOG --- CHANGELOG.adoc | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 8094e25c47..3a90e194b5 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -16,6 +16,7 @@ Release with new features and bugfixes: * https://github.com/devonfw/IDEasy/issues/1724[#1724]: Add gui commandlet * https://github.com/devonfw/IDEasy/issues/1853[#1853]: Add ARM releases for VSCode on Mac * https://github.com/devonfw/IDEasy/issues/1723[#1723]: Add commandlet for GitHub Copilot CLI +* https://github.com/devonfw/IDEasy/issues/861[#861]: Fix install of pgadmin throws IllegalStateException when the install wizard starts The full list of changes for this release can be found in https://github.com/devonfw/IDEasy/milestone/44?closed=1[milestone 2026.05.001]. From faa6b1be666470075becd0d5c2deabfe359eac2b Mon Sep 17 00:00:00 2001 From: jakozian Date: Tue, 28 Apr 2026 16:51:41 +0200 Subject: [PATCH 06/21] #1869: Extract process of running reg into private method --- .../devonfw/tools/ide/os/WindowsHelperImpl.java | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java b/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java index ee6d55eb2f..29bc0b9a7a 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java @@ -60,13 +60,23 @@ public String getUserEnvironmentValue(String key) { @Override public String getRegistryValue(String path, String key) { - ProcessResult result = this.context.newProcess().errorHandling(ProcessErrorHandling.LOG_WARNING).executable("reg").addArgs("query", path, "/v", key) + List out = runReg("query", path, "/v", key); + if (out != null) { + return retrieveRegString(key, out); + } + return null; + } + + private List runReg(String... args) { + ProcessResult result = this.context.newProcess() + .errorHandling(ProcessErrorHandling.LOG_WARNING) + .executable("reg") + .addArgs(args) .run(ProcessMode.DEFAULT_CAPTURE); if (!result.isSuccessful()) { return null; } - List out = result.getOut(); - return retrieveRegString(key, out); + return result.getOut(); } /** From 53fff3f7d11a73096caa327a11240c6882d44214 Mon Sep 17 00:00:00 2001 From: jakozian Date: Wed, 29 Apr 2026 11:15:58 +0200 Subject: [PATCH 07/21] #1869: Extend WindowsHelper interface with methods to retrieve entries from registry --- .../devonfw/tools/ide/os/WindowsHelper.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelper.java b/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelper.java index 266b4f8179..29bc8ebfa8 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelper.java +++ b/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelper.java @@ -32,6 +32,30 @@ public interface WindowsHelper { */ String getRegistryValue(String path, String key); + /** + * @param appName the application name to search for in the Windows registry. + * @return the DisplayName entry if the application is found in the Windows registry or {@code null} if nothing was found. + */ + String getDisplayNameFromRegistry(String appName); + + /** + * @param appName the application name to search for in the Windows registry. + * @return the DisplayIcon entry if the application is found in the Windows registry or {@code null} if nothing was found. + */ + String getDisplayIconFromRegistry(String appName); + + /** + * @param appName the application name to search for in the Windows registry. + * @return the UninstallString entry if the application is found in the Windows registry or {@code null} if nothing was found. + */ + String getUninstallStringFromRegistry(String appName); + + /** + * @param appName the application name to search for in the Windows registry. + * @return the InstallLocation entry if the application is found in the Windows registry or {@code null} if nothing was found. + */ + String getInstallLocationFromRegistry(String appName); + /** * @param context the {@link IdeContext}. * @return the instance of {@link WindowsHelper}. From d1230886657c27f19b59fabe9aa225675b7a8985 Mon Sep 17 00:00:00 2001 From: jakozian Date: Wed, 29 Apr 2026 11:20:58 +0200 Subject: [PATCH 08/21] #1869: Add interface methods to WindowsHelperImpl --- .../tools/ide/os/WindowsHelperImpl.java | 46 ++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java b/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java index 29bc0b9a7a..46354626e5 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java @@ -21,6 +21,13 @@ public class WindowsHelperImpl implements WindowsHelper { /** Registry key for the users environment variables. */ public static final String HKCU_ENVIRONMENT = "HKCU\\Environment"; + /** Common Windows registry base paths containing (uninstall) information for installed applications (system-wide and per-user). */ + private static final String[] REGISTRY_BASE_PATHS = { + "HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall", + "HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall", + "HKLM\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall" + }; + private final IdeContext context; /** @@ -67,7 +74,44 @@ public String getRegistryValue(String path, String key) { return null; } - private List runReg(String... args) { + @Override + public String getDisplayNameFromRegistry(String appName) { + return getRegistryValueBySearch(appName, "DisplayName"); + } + + @Override + public String getDisplayIconFromRegistry(String appName) { + return getRegistryValueBySearch(appName, "DisplayIcon"); + } + + @Override + public String getUninstallStringFromRegistry(String appName) { + return getRegistryValueBySearch(appName, "UninstallString"); + } + + @Override + public String getInstallLocationFromRegistry(String appName) { + return getRegistryValueBySearch(appName, "InstallLocation"); + } + + private String getRegistryValueBySearch(String appName, String key) { + + for (String registryBasePath : REGISTRY_BASE_PATHS) { + List out = runReg("query", registryBasePath, "/s", "/f", appName); + if (out != null) { + return retrieveRegString(key, out); + } + } + return null; + } + + /** + * Executes a Windows registry command and returns its output. + * + * @param args the registry command arguments. + * @return the command output lines, or {@code null} if the command failed + */ + protected List runReg(String... args) { ProcessResult result = this.context.newProcess() .errorHandling(ProcessErrorHandling.LOG_WARNING) .executable("reg") From 7c8f432c3b682320f25ed86bef2d88f40e5a15c6 Mon Sep 17 00:00:00 2001 From: jakozian Date: Wed, 29 Apr 2026 11:21:33 +0200 Subject: [PATCH 09/21] #1869: Add interface methods to WindowsHelperMock --- .../tools/ide/os/WindowsHelperMock.java | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperMock.java b/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperMock.java index f2245f40ef..dc8539a38f 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperMock.java +++ b/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperMock.java @@ -1,5 +1,6 @@ package com.devonfw.tools.ide.os; +import java.util.List; import java.util.Properties; import com.devonfw.tools.ide.context.IdeContext; @@ -11,6 +12,18 @@ public class WindowsHelperMock extends WindowsHelperImpl { private final Properties env; + private static final String MOCK_APP_NAME = "TestApp"; + + private static final String MOCK_DISPLAY_NAME = "Test Application"; + + private static final String MOCK_INSTALL_LOCATION = "C:\\Program Files\\TestApp"; + + private static final String MOCK_DISPLAY_ICON = + "C:\\Program Files\\TestApp\\testapp.exe,0"; + private static final String MOCK_UNINSTALL_STRING = + "\"C:\\Program Files\\TestApp\\uninstall.exe\""; + + /** * The constructor. */ @@ -41,6 +54,26 @@ public String getUserEnvironmentValue(String key) { return this.env.getProperty(key); } + @Override + public String getDisplayNameFromRegistry(String appName) { + return matchesApp(appName) ? MOCK_DISPLAY_NAME : null; + } + + @Override + public String getDisplayIconFromRegistry(String appName) { + return matchesApp(appName) ? MOCK_DISPLAY_ICON : null; + } + + @Override + public String getUninstallStringFromRegistry(String appName) { + return matchesApp(appName) ? MOCK_UNINSTALL_STRING : null; + } + + @Override + public String getInstallLocationFromRegistry(String appName) { + return matchesApp(appName) ? MOCK_INSTALL_LOCATION : null; + } + @Override public String getRegistryValue(String path, String key) { @@ -51,4 +84,35 @@ public String getRegistryValue(String path, String key) { } return null; } + + private boolean matchesApp(String appName) { + return appName != null + && appName.equalsIgnoreCase(MOCK_APP_NAME); + } + + @Override + protected List runReg(String... args) { + + // simulate reg.exe filtering: "/f " + String searchValue = null; + for (int i = 0; i < args.length - 1; i++) { + if ("/f".equalsIgnoreCase(args[i])) { + searchValue = args[i + 1]; + break; + } + } + + // Only return output if searched app matches + if (!"TestApp".equalsIgnoreCase(searchValue)) { + return List.of(); // same behavior as reg.exe: no results + } + + return List.of( + "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\TestApp", + " DisplayName REG_SZ Test Application", + " DisplayIcon REG_SZ C:\\Program Files\\TestApp\\testapp.exe,0", + " InstallLocation REG_SZ C:\\Program Files\\TestApp", + " UninstallString REG_SZ \"C:\\Program Files\\TestApp\\uninstall.exe\"" + ); + } } From 9693b955657abaa8b634284b47bfee3a0307747c Mon Sep 17 00:00:00 2001 From: jakozian Date: Wed, 29 Apr 2026 11:22:27 +0200 Subject: [PATCH 10/21] #1869: Add test-seam class to simulate running of reg.exe command --- .../ide/os/WindowsHelperImplTestable.java | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperImplTestable.java diff --git a/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperImplTestable.java b/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperImplTestable.java new file mode 100644 index 0000000000..54037f6813 --- /dev/null +++ b/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperImplTestable.java @@ -0,0 +1,48 @@ +package com.devonfw.tools.ide.os; + +import java.util.List; + +import com.devonfw.tools.ide.context.IdeContext; + +/** + * Test-specific subclass of {@link WindowsHelperImpl}. + * + *

+ * Mainly used as a test seam to simulate the reg.exe command for test purposes. + *

+ */ +public class WindowsHelperImplTestable extends WindowsHelperImpl { + + /** + * The constructor. + * + * @param context the {@link IdeContext}. + */ + public WindowsHelperImplTestable(IdeContext context) { + + super(context); + } + + @Override + protected List runReg(String... args) { + + // simulate reg.exe filtering: "/f " + String searchValue = null; + for (int i = 0; i < args.length - 1; i++) { + if ("/f".equalsIgnoreCase(args[i])) { + searchValue = args[i + 1]; + break; + } + } + if (!"TestApp".equalsIgnoreCase(searchValue)) { + return List.of(); + } + return List.of( + "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\TestApp", + " DisplayName REG_SZ Test Application", + " DisplayIcon REG_SZ C:\\Program Files\\TestApp\\testapp.exe,0", + " InstallLocation REG_SZ C:\\Program Files\\TestApp", + " UninstallString REG_SZ \"C:\\Program Files\\TestApp\\uninstall.exe\"" + ); + } +} From ccb14d315494888bafd765ba557cb2352a99aabc Mon Sep 17 00:00:00 2001 From: jakozian Date: Wed, 29 Apr 2026 11:43:16 +0200 Subject: [PATCH 11/21] #1869: Add tests for WindowsHelperImpl --- .../tools/ide/os/WindowsHelperImplTest.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperImplTest.java b/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperImplTest.java index 85935fb72a..bbe84a040a 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperImplTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperImplTest.java @@ -14,6 +14,10 @@ */ class WindowsHelperImplTest extends AbstractIdeContextTest { + private static final String TEST_APP_NAME = "TestApp"; + + private static final String UNKNOWN_TEST_APP_NAME = "UnknownApp"; + /** * Tests if the USER_PATH registry entry can be parsed properly. */ @@ -48,4 +52,47 @@ void testWindowsHelperParseEmptyRegStringReturnsNull() { assertThat(regString).isNull(); } + /** + * Tests if correct keys can be found in registry output for app name filter. + */ + @Test + void testRegistryLookupReturnsCorrectEntryIfFound() { + // arrange + AbstractIdeTestContext context = new IdeTestContext(); + WindowsHelperImpl helper = new WindowsHelperImplTestable(context); + + // act + String displayName = helper.getDisplayNameFromRegistry(TEST_APP_NAME); + String icon = helper.getDisplayIconFromRegistry(TEST_APP_NAME); + String uninstall = helper.getUninstallStringFromRegistry(TEST_APP_NAME); + String location = helper.getInstallLocationFromRegistry(TEST_APP_NAME); + + // assert + assertThat(displayName).isEqualTo("Test Application"); + assertThat(icon).isEqualTo("C:\\Program Files\\TestApp\\testapp.exe,0"); + assertThat(uninstall).isEqualTo("\"C:\\Program Files\\TestApp\\uninstall.exe\""); + assertThat(location).isEqualTo("C:\\Program Files\\TestApp"); + } + + /** + * Tests if registry lookup return nulls on unknown app name filter. + */ + @Test + void testRegistryLookupReturnsNullIfNotFound() { + // arrange + AbstractIdeTestContext context = new IdeTestContext(); + WindowsHelperImpl helper = new WindowsHelperImplTestable(context); + + // act + String displayName = helper.getDisplayNameFromRegistry(UNKNOWN_TEST_APP_NAME); + String icon = helper.getDisplayIconFromRegistry(UNKNOWN_TEST_APP_NAME); + String uninstall = helper.getUninstallStringFromRegistry(UNKNOWN_TEST_APP_NAME); + String location = helper.getInstallLocationFromRegistry(UNKNOWN_TEST_APP_NAME); + + // assert + assertThat(displayName).isNull(); + assertThat(icon).isNull(); + assertThat(uninstall).isNull(); + assertThat(location).isNull(); + } } From 8b4d51d834a880b58f52b3807a851ae8c5d830a9 Mon Sep 17 00:00:00 2001 From: jakozian Date: Mon, 4 May 2026 10:12:36 +0200 Subject: [PATCH 12/21] Update cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java Co-authored-by: MarvMa --- .../main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java b/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java index 46354626e5..1d40730354 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java @@ -98,7 +98,7 @@ private String getRegistryValueBySearch(String appName, String key) { for (String registryBasePath : REGISTRY_BASE_PATHS) { List out = runReg("query", registryBasePath, "/s", "/f", appName); - if (out != null) { + if (out != null && !out.isEmpty()) { return retrieveRegString(key, out); } } From a35910b288759f9189274bc92571557b97b1500c Mon Sep 17 00:00:00 2001 From: jakozian Date: Tue, 5 May 2026 15:09:11 +0200 Subject: [PATCH 13/21] #1869: Replace string with constant --- .../test/java/com/devonfw/tools/ide/os/WindowsHelperMock.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperMock.java b/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperMock.java index dc8539a38f..66af992ac4 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperMock.java +++ b/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperMock.java @@ -103,7 +103,7 @@ protected List runReg(String... args) { } // Only return output if searched app matches - if (!"TestApp".equalsIgnoreCase(searchValue)) { + if (!MOCK_APP_NAME .equalsIgnoreCase(searchValue)) { return List.of(); // same behavior as reg.exe: no results } From 6ed1306df795d7a0eb5ff9ac629a8f0c1704915c Mon Sep 17 00:00:00 2001 From: jakozian Date: Wed, 6 May 2026 09:35:08 +0200 Subject: [PATCH 14/21] #1869: Change display name to display version --- .../java/com/devonfw/tools/ide/os/WindowsHelper.java | 2 +- .../com/devonfw/tools/ide/os/WindowsHelperImpl.java | 4 ++-- .../devonfw/tools/ide/os/WindowsHelperImplTest.java | 8 ++++---- .../tools/ide/os/WindowsHelperImplTestable.java | 2 +- .../com/devonfw/tools/ide/os/WindowsHelperMock.java | 10 +++++----- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelper.java b/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelper.java index 29bc8ebfa8..765a452f93 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelper.java +++ b/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelper.java @@ -36,7 +36,7 @@ public interface WindowsHelper { * @param appName the application name to search for in the Windows registry. * @return the DisplayName entry if the application is found in the Windows registry or {@code null} if nothing was found. */ - String getDisplayNameFromRegistry(String appName); + String getDisplayVersionFromRegistry(String appName); /** * @param appName the application name to search for in the Windows registry. diff --git a/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java b/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java index 1d40730354..c78b60af72 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java @@ -75,8 +75,8 @@ public String getRegistryValue(String path, String key) { } @Override - public String getDisplayNameFromRegistry(String appName) { - return getRegistryValueBySearch(appName, "DisplayName"); + public String getDisplayVersionFromRegistry(String appName) { + return getRegistryValueBySearch(appName, "DisplayVersion"); } @Override diff --git a/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperImplTest.java b/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperImplTest.java index bbe84a040a..47a5a1557c 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperImplTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperImplTest.java @@ -62,13 +62,13 @@ void testRegistryLookupReturnsCorrectEntryIfFound() { WindowsHelperImpl helper = new WindowsHelperImplTestable(context); // act - String displayName = helper.getDisplayNameFromRegistry(TEST_APP_NAME); + String displayVersion = helper.getDisplayVersionFromRegistry(TEST_APP_NAME); String icon = helper.getDisplayIconFromRegistry(TEST_APP_NAME); String uninstall = helper.getUninstallStringFromRegistry(TEST_APP_NAME); String location = helper.getInstallLocationFromRegistry(TEST_APP_NAME); // assert - assertThat(displayName).isEqualTo("Test Application"); + assertThat(displayVersion).isEqualTo("1.1.1"); assertThat(icon).isEqualTo("C:\\Program Files\\TestApp\\testapp.exe,0"); assertThat(uninstall).isEqualTo("\"C:\\Program Files\\TestApp\\uninstall.exe\""); assertThat(location).isEqualTo("C:\\Program Files\\TestApp"); @@ -84,13 +84,13 @@ void testRegistryLookupReturnsNullIfNotFound() { WindowsHelperImpl helper = new WindowsHelperImplTestable(context); // act - String displayName = helper.getDisplayNameFromRegistry(UNKNOWN_TEST_APP_NAME); + String displayVersion = helper.getDisplayVersionFromRegistry(UNKNOWN_TEST_APP_NAME); String icon = helper.getDisplayIconFromRegistry(UNKNOWN_TEST_APP_NAME); String uninstall = helper.getUninstallStringFromRegistry(UNKNOWN_TEST_APP_NAME); String location = helper.getInstallLocationFromRegistry(UNKNOWN_TEST_APP_NAME); // assert - assertThat(displayName).isNull(); + assertThat(displayVersion).isNull(); assertThat(icon).isNull(); assertThat(uninstall).isNull(); assertThat(location).isNull(); diff --git a/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperImplTestable.java b/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperImplTestable.java index 54037f6813..a63946e71a 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperImplTestable.java +++ b/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperImplTestable.java @@ -39,7 +39,7 @@ protected List runReg(String... args) { } return List.of( "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\TestApp", - " DisplayName REG_SZ Test Application", + " DisplayVersion REG_SZ 1.1.1", " DisplayIcon REG_SZ C:\\Program Files\\TestApp\\testapp.exe,0", " InstallLocation REG_SZ C:\\Program Files\\TestApp", " UninstallString REG_SZ \"C:\\Program Files\\TestApp\\uninstall.exe\"" diff --git a/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperMock.java b/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperMock.java index 66af992ac4..e96fef6652 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperMock.java +++ b/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperMock.java @@ -14,7 +14,7 @@ public class WindowsHelperMock extends WindowsHelperImpl { private static final String MOCK_APP_NAME = "TestApp"; - private static final String MOCK_DISPLAY_NAME = "Test Application"; + private static final String MOCK_DISPLAY_VERSION = "1.1.1"; private static final String MOCK_INSTALL_LOCATION = "C:\\Program Files\\TestApp"; @@ -55,8 +55,8 @@ public String getUserEnvironmentValue(String key) { } @Override - public String getDisplayNameFromRegistry(String appName) { - return matchesApp(appName) ? MOCK_DISPLAY_NAME : null; + public String getDisplayVersionFromRegistry(String appName) { + return matchesApp(appName) ? MOCK_DISPLAY_VERSION : null; } @Override @@ -103,13 +103,13 @@ protected List runReg(String... args) { } // Only return output if searched app matches - if (!MOCK_APP_NAME .equalsIgnoreCase(searchValue)) { + if (!MOCK_APP_NAME.equalsIgnoreCase(searchValue)) { return List.of(); // same behavior as reg.exe: no results } return List.of( "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\TestApp", - " DisplayName REG_SZ Test Application", + " DisplayName REG_SZ 1.1.1", " DisplayIcon REG_SZ C:\\Program Files\\TestApp\\testapp.exe,0", " InstallLocation REG_SZ C:\\Program Files\\TestApp", " UninstallString REG_SZ \"C:\\Program Files\\TestApp\\uninstall.exe\"" From 2d40318a66864b2da76f7a3eed444fce669e78e7 Mon Sep 17 00:00:00 2001 From: jakozian Date: Wed, 6 May 2026 11:24:28 +0200 Subject: [PATCH 15/21] #1869: Change search to find exact path first and the keys in this path --- .../tools/ide/os/WindowsHelperImpl.java | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java b/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java index c78b60af72..a69a588d10 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java @@ -96,13 +96,33 @@ public String getInstallLocationFromRegistry(String appName) { private String getRegistryValueBySearch(String appName, String key) { + String uninstallKey = findUninstallKey(appName); + if (uninstallKey == null) { + return null; + } + List out = runReg("query", uninstallKey); + if (out != null) { + return retrieveRegString(key, out); + } + return null; + } + + private String findUninstallKey(String appName) { + for (String registryBasePath : REGISTRY_BASE_PATHS) { List out = runReg("query", registryBasePath, "/s", "/f", appName); - if (out != null && !out.isEmpty()) { - return retrieveRegString(key, out); + if (out == null) { + continue; + } + for (String line : out) { + line = line.trim(); + if (line.startsWith("HKEY_")) { + return line; // exact registry path (key) for tool + } } } return null; + } /** From 16446ab08f7e41e045a1bf5103157705d741bf5d Mon Sep 17 00:00:00 2001 From: jakozian Date: Wed, 6 May 2026 11:27:09 +0200 Subject: [PATCH 16/21] #1870: Add method for windows app name to super class of global tools --- .../com/devonfw/tools/ide/tool/GlobalToolCommandlet.java | 8 ++++++++ 1 file changed, 8 insertions(+) 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 acff7cc211..ec8588aafe 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 @@ -188,6 +188,14 @@ protected List getInstallPackageManagerCommands() { return List.of(); } + /** + * @return the app name to look for in the Windows registry + */ + public String getWindowsRegistryAppName() { + + return this.tool; + } + @Override public VersionIdentifier getInstalledVersion() { //TODO: handle "get-version " From 0e9a9d93880170cd24f9dd41ff655412d9dc77c0 Mon Sep 17 00:00:00 2001 From: jakozian Date: Wed, 6 May 2026 11:28:40 +0200 Subject: [PATCH 17/21] #1870: Add logic to get version for global tools under windows --- .../com/devonfw/tools/ide/tool/GlobalToolCommandlet.java | 5 +++++ 1 file changed, 5 insertions(+) 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 ec8588aafe..ea0050f2fc 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 @@ -14,6 +14,7 @@ import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.io.FileAccess; import com.devonfw.tools.ide.log.IdeLogLevel; +import com.devonfw.tools.ide.os.WindowsHelper; import com.devonfw.tools.ide.process.ProcessContext; import com.devonfw.tools.ide.process.ProcessErrorHandling; import com.devonfw.tools.ide.process.ProcessMode; @@ -199,6 +200,10 @@ public String getWindowsRegistryAppName() { @Override public VersionIdentifier getInstalledVersion() { //TODO: handle "get-version " + String version = WindowsHelper.get(this.context).getDisplayVersionFromRegistry(getWindowsRegistryAppName()); + if (version != null) { + return VersionIdentifier.of(version); + } return null; } From e05473ad9d2ee7ee07ccde83ed0c0f50dc269022 Mon Sep 17 00:00:00 2001 From: jakozian Date: Wed, 6 May 2026 12:57:34 +0200 Subject: [PATCH 18/21] #1870: Fix mock logic of runReg --- .../ide/os/WindowsHelperImplTestable.java | 48 ++++++++++++------ .../tools/ide/os/WindowsHelperMock.java | 50 ++++++++++++------- 2 files changed, 67 insertions(+), 31 deletions(-) diff --git a/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperImplTestable.java b/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperImplTestable.java index a63946e71a..6e43b0fdd4 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperImplTestable.java +++ b/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperImplTestable.java @@ -26,23 +26,43 @@ public WindowsHelperImplTestable(IdeContext context) { @Override protected List runReg(String... args) { - // simulate reg.exe filtering: "/f " - String searchValue = null; + String searchValue = extractFilterValue(args); + // Case: reg query /s /f + if (searchValue != null) { + if (!"TestApp".equalsIgnoreCase(searchValue)) { + return List.of(); + } + return List.of( + "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\TestApp", + " DisplayName REG_SZ TestApp" + ); + } + // Case: reg query + if (args.length >= 2 && + args[0].equalsIgnoreCase("query") && + args[1].endsWith("\\Uninstall\\TestApp")) { + + return List.of( + "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\TestApp", + " DisplayName REG_SZ TestApp", + " DisplayVersion REG_SZ 1.1.1", + " DisplayIcon REG_SZ C:\\Program Files\\TestApp\\testapp.exe,0", + " InstallLocation REG_SZ C:\\Program Files\\TestApp", + " UninstallString REG_SZ \"C:\\Program Files\\TestApp\\uninstall.exe\"" + ); + } + + return List.of(); + } + + + private static String extractFilterValue(String[] args) { + for (int i = 0; i < args.length - 1; i++) { if ("/f".equalsIgnoreCase(args[i])) { - searchValue = args[i + 1]; - break; + return args[i + 1]; } } - if (!"TestApp".equalsIgnoreCase(searchValue)) { - return List.of(); - } - return List.of( - "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\TestApp", - " DisplayVersion REG_SZ 1.1.1", - " DisplayIcon REG_SZ C:\\Program Files\\TestApp\\testapp.exe,0", - " InstallLocation REG_SZ C:\\Program Files\\TestApp", - " UninstallString REG_SZ \"C:\\Program Files\\TestApp\\uninstall.exe\"" - ); + return null; } } diff --git a/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperMock.java b/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperMock.java index e96fef6652..1f1148271b 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperMock.java +++ b/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperMock.java @@ -93,26 +93,42 @@ private boolean matchesApp(String appName) { @Override protected List runReg(String... args) { - // simulate reg.exe filtering: "/f " - String searchValue = null; - for (int i = 0; i < args.length - 1; i++) { - if ("/f".equalsIgnoreCase(args[i])) { - searchValue = args[i + 1]; - break; + String searchValue = extractFilterValue(args); + // Case: reg query /s /f + if (searchValue != null) { + if (!"TestApp".equalsIgnoreCase(searchValue)) { + return List.of(); } + return List.of( + "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\TestApp", + " DisplayName REG_SZ TestApp" + ); } - - // Only return output if searched app matches - if (!MOCK_APP_NAME.equalsIgnoreCase(searchValue)) { - return List.of(); // same behavior as reg.exe: no results + // Case: reg query + if (args.length >= 2 && + args[0].equalsIgnoreCase("query") && + args[1].endsWith("\\Uninstall\\TestApp")) { + + return List.of( + "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\TestApp", + " DisplayName REG_SZ TestApp", + " DisplayVersion REG_SZ 1.1.1", + " DisplayIcon REG_SZ C:\\Program Files\\TestApp\\testapp.exe,0", + " InstallLocation REG_SZ C:\\Program Files\\TestApp", + " UninstallString REG_SZ \"C:\\Program Files\\TestApp\\uninstall.exe\"" + ); } - return List.of( - "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\TestApp", - " DisplayName REG_SZ 1.1.1", - " DisplayIcon REG_SZ C:\\Program Files\\TestApp\\testapp.exe,0", - " InstallLocation REG_SZ C:\\Program Files\\TestApp", - " UninstallString REG_SZ \"C:\\Program Files\\TestApp\\uninstall.exe\"" - ); + return List.of(); + } + + private static String extractFilterValue(String[] args) { + + for (int i = 0; i < args.length - 1; i++) { + if ("/f".equalsIgnoreCase(args[i])) { + return args[i + 1]; + } + } + return null; } } From 72b94b5dc094603f6b62e47b01111678b5d5ce3d Mon Sep 17 00:00:00 2001 From: jakozian Date: Thu, 7 May 2026 10:34:50 +0200 Subject: [PATCH 19/21] #1870: Add check for Windows OS --- .../com/devonfw/tools/ide/tool/GlobalToolCommandlet.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) 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 ea0050f2fc..7fc9ab9d1e 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 @@ -200,9 +200,11 @@ public String getWindowsRegistryAppName() { @Override public VersionIdentifier getInstalledVersion() { //TODO: handle "get-version " - String version = WindowsHelper.get(this.context).getDisplayVersionFromRegistry(getWindowsRegistryAppName()); - if (version != null) { - return VersionIdentifier.of(version); + if (this.context.getSystemInfo().isWindows()) { + String version = WindowsHelper.get(this.context).getDisplayVersionFromRegistry(getWindowsRegistryAppName()); + if (version != null) { + return VersionIdentifier.of(version); + } } return null; } From 655e7647bab7fc1f48195011f9a207b0c5ab1611 Mon Sep 17 00:00:00 2001 From: jakozian Date: Thu, 7 May 2026 13:34:04 +0200 Subject: [PATCH 20/21] #1870: Remove TODO --- .../java/com/devonfw/tools/ide/tool/GlobalToolCommandlet.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 7fc9ab9d1e..bca57d8bd9 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 @@ -199,7 +199,7 @@ public String getWindowsRegistryAppName() { @Override public VersionIdentifier getInstalledVersion() { - //TODO: handle "get-version " + if (this.context.getSystemInfo().isWindows()) { String version = WindowsHelper.get(this.context).getDisplayVersionFromRegistry(getWindowsRegistryAppName()); if (version != null) { From e96d5698f5c61213c6e906eb45e4631141f9344f Mon Sep 17 00:00:00 2001 From: jakozian Date: Thu, 7 May 2026 13:39:43 +0200 Subject: [PATCH 21/21] #1870: Add to CHANGELOG --- CHANGELOG.adoc | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index e55eb94759..58fcf76d9c 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -23,6 +23,7 @@ Release with new features and bugfixes: * https://github.com/devonfw/IDEasy/issues/1688[#1688]: Remove unnecessary message in the CLI when installing a new tool * https://github.com/devonfw/IDEasy/issues/861[#861]: Fix install of pgadmin throws IllegalStateException when the install wizard starts * https://github.com/devonfw/IDEasy/issues/1685[#1685]: Add Nest CLI to IDEasy commandlets +* https://github.com/devonfw/IDEasy/issues/1870[#1870]: Add generic get-version implementation for global tools under windows The full list of changes for this release can be found in https://github.com/devonfw/IDEasy/milestone/44?closed=1[milestone 2026.05.001].