Skip to content

#861/#1688: Add functionality to global tool commandlets for installation and uninstallation under windows and linux - #1862

Closed
jakozian wants to merge 26 commits into
devonfw:mainfrom
jakozian:fix/861-installation-of-pgadmin
Closed

#861/#1688: Add functionality to global tool commandlets for installation and uninstallation under windows and linux#1862
jakozian wants to merge 26 commits into
devonfw:mainfrom
jakozian:fix/861-installation-of-pgadmin

Conversation

@jakozian

@jakozian jakozian commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

This PR fixes #861 and #1688

Implemented changes:

(With this generic global tool logic the exception thrown while installing pgadmin is fixed #861)


Checklist for this PR

Make sure everything is checked before merging this PR. For further info please also see
our DoD.

  • When running mvn clean test locally all tests pass and build is successful
  • PR title is of the form #«issue-id»: «brief summary» (e.g. #921: fixed setup.bat). If no issue ID exists, title only.
  • PR top-level comment summarizes what has been done and contains link to addressed issue(s)
  • PR and issue(s) have suitable labels
  • Issue is set to In Progress and assigned to you or there is no issue (might happen for very small PRs)
  • You followed all coding conventions
  • You have added the issue implemented by your PR in CHANGELOG.adoc unless issue is labeled
    with internal

jakozian added 24 commits April 17, 2026 09:49
…-installation-of-pgadmin

# Conflicts:
#	CHANGELOG.adoc
…-installation-of-pgadmin

# Conflicts:
#	CHANGELOG.adoc
@github-project-automation github-project-automation Bot moved this to 🆕 New in IDEasy board Apr 27, 2026
@jakozian jakozian added windows specific for Microsoft Windows OS linux specific for linux OS (debian, ubunutu, suse, etc.) labels Apr 27, 2026
@coveralls

coveralls commented Apr 27, 2026

Copy link
Copy Markdown
Collaborator

Coverage Report for CI Build 25037187462

Warning

Build has drifted: This PR's base is out of sync with its target branch, so coverage data may include unrelated changes.
Quick fix: rebase this PR. Learn more →

Coverage decreased (-0.4%) to 70.272%

Details

  • Coverage decreased (-0.4%) from the base build.
  • Patch coverage: No coverable lines changed in this PR.
  • 205 coverage regressions across 7 files.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

205 previously-covered lines in 7 files lost coverage.

File Lines Losing Coverage Coverage
com/devonfw/tools/ide/tool/GlobalToolCommandlet.java 80 1.51%
com/devonfw/tools/ide/os/WindowsHelperImpl.java 46 31.03%
com/devonfw/tools/ide/tool/docker/Docker.java 38 5.56%
com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java 21 82.99%
com/devonfw/tools/ide/tool/pgadmin/PgAdmin.java 11 10.53%
com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java 8 90.31%
com/devonfw/tools/ide/url/tool/squirrelsql/SquirrelSqlUrlUpdater.java 1 84.21%

Coverage Stats

Coverage Status
Relevant Lines: 15414
Covered Lines: 11296
Line Coverage: 73.28%
Relevant Branches: 6898
Covered Branches: 4383
Branch Coverage: 63.54%
Branches in Coverage %: Yes
Coverage Strength: 3.1 hits per line

💛 - Coveralls

@jakozian jakozian added docker docker and esp. DockerDesktop pgadmin postgres admin UI integration rancher Rancher-Desktop labels Apr 27, 2026
@jakozian
jakozian marked this pull request as ready for review April 27, 2026 13:21
@jakozian jakozian moved this from 🆕 New to Team Review in IDEasy board Apr 27, 2026
@jakozian
jakozian requested a review from MarvMa April 27, 2026 13:37

@MarvMa MarvMa left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You found a really smart way of uninstalling Tools via Win Registry, Nice Work 👍 I left some comments regarding readability and maintainability.

if (this.context.isForceMode()) {
// has to be set to null so the already installed tool doesn't open
// during the installation process in force mode
installationPath = null;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using null here as a control signal to prevent the tool from being started makes the flow harder to understand.
It might be safer and clearer to introduce an explicit flag (e.g. startTool / skipStart) or a small result type to model this state explicitly and avoid the usage of null as a flag.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are absolutely right! Changing that...

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This filecurrently mixes several responsibilities (error handling, force‑mode behavior, user messaging, and installation result creation).
Splitting the implementation into smaller more focused methods could improve readability and make the control flow easier to understand and test.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with you on that but as @hohwille suggested this would not be part of this issue.

LOG.warn("The tool {} is about to be installed. Please complete the installation and if required "
+ "reboot your machine. Then run the command to start the tool.", this.tool);
} else {
throw new CliException("The tool " + this.tool + " is about to be installed. Please complete the installation and if required "

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The exit code 2 is not clear here.
Introducing a named constant or enum (e.g. EXIT_USER_ACTION_REQUIRED) would make the intention clearer and improve maintainability/readability.

@hohwille hohwille left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jakozian thanks for your PR. I really appreciate that you found a solution how to find the version of any installed software on Windows from the registry. That is great progress. Also #1688 is easily solved by your PR. 👍
However, after looking at all the details, I got the impression that we went the wrong way. This PR is getting more and more complex since you seem to have hit a pitfall.
GlobalToolCommandlet is a mess and that is not your fault. I was not aware of that and solving this is not an easy task.
Therefore I would go back and suggest the following steps as individual PRs:

  1. PR fixing only #1688 by just removing the unwanted LOG.error messages you also removed here. IMHO that is all we need to implement that story (besides a CHANGELOG entry).
  2. PR to improve our WindowsHelper with ability to determine installed version of an installed tool from Windows registry. Move your changes to WindowsHelper* into that PR and try to improve your implementation addressing my review concern. Maybe by asking copilot or some other AI you can find even a direct query method to search the key where DisplayName matches a given prefix.
  3. When all this is merged, lets stepwise improve GlobalToolCommandlet and its sub-classes. Maybe we will even split this into multiple PRs e.g. per OS. The closer we look, the more we will find. I also just noticed, that when podman is installed, this will be used as an alias for docker what totally makes sense. But in such case the edition would rather be podman and the version is yet fully unclear. Also for Linux I see tons of improvements required since things are not working on non Debian based distributions, etc.
    Mac is not yet supported by this at all.

Sorry, that I did not see this upfront. But if we keep trying to make this perfect in this single PR we will both not have fun. So lets go for divide & conquer what should always be the pattern to solve complex problems.

Comment thread cli/src/main/java/com/devonfw/tools/ide/tool/pgadmin/PgAdmin.java Outdated
String displayVersion = windowsHelper.getRegistryValueBySearch(getWindowsAppName(), "DisplayVersion");
return VersionIdentifier.of(displayVersion);
} else if (this.context.getSystemInfo().isLinux()) {
String output = this.context.newProcess().runAndGetSingleOutput(getBinaryName(), "version");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not look like a reliable way to determine the version:

$ pgadmin4 version
bash: pgadmin4: command not found
$ docker version
Client:
 Version:           29.1.4-rd
 API version:       1.52
 Go version:        go1.25.5
 Git commit:        3c6914c
 Built:             Fri Jan  9 20:47:42 2026
 OS/Arch:           windows/amd64
 Context:           default

Server:
 Engine:
  Version:          29.1.3
  API version:      1.52 (minimum version 1.41)
  Go version:       go1.25.5
  Git commit:       fbf3ed25f893e6ce21336f1101590e40a13934f4
  Built:            Sun Dec 14 05:31:58 2025
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          v2.2.0
  GitCommit:        1c4457e00facac03ce1d75f7b6777a7a851e5c41
 runc:
  Version:          1.4.0
  GitCommit:        8bd78a9977e604c4d5f67a7415d7b8b8c109cdc4
 docker-init:
  Version:          0.19.0
  GitCommit:

You would rather need to ask the package manager for the version.
This is not trivial to implement since e.g. on Ubuntu you would install with

sudo apt install pgadmin4-desktop

But to determine the version you would rather do:

dpkg -s pgadmin4-desktop | grep '^Version:'

I just noticed that on Linux we currently install and uninstall pgadmin4 in a very odd and crazy way.
According to https://www.pgadmin.org/download/pgadmin-4-apt/ we should just do sudo apt install pgadmin4-desktop to install it.

My vision was that if we have such reasonable installation commands, we could derive the command to determine the version automatically from that.
Obviously if we do such crazy stuff this does not seem to be possible.
Still my approach would be to implement the automatically generated command to determine the version on Linux in a protected method based on the PackageManagerCommand.
IMHO also the uninstallation could be automatically derived from the installation PackageManagerCommand.
Only in edge-cases, we could then override the protected method if the default does not work.

And how about Mac?
To me it seems the entire "world" of GlobalToolCommandlet is more in a messy alpha/beta state but something reasonable we crafted by design.


Pattern pattern = Pattern.compile(displayNameRegex, Pattern.CASE_INSENSITIVE);
for (String path : REGISTRY_PATHS) {
List<String> output = runReg("query", path, "/s");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is giving me the details of all installed software.
On my machine ~4000 lines of text.
That seems like a very inefficient way to solve this.
The idea of a registry query is to select only the data we really need.
A first improvement would be to run

reg query path /S /v DisplayName

BTW: it is also slightly confusing that you allow to pass any value for the 2nd parameter key but name the first parameter displayNameRegex so you actually assume that the key is DisplayName.
It can be a good idea to generalise a method to allow more flexibility.
On the other hand then you also need to implement it in a way that it solves all the potential cases properly.
I would recommend to focus on creating a method dedicated to determine the version of a product given the (prefix of) the DisplayName.

Comment on lines 98 to +107
ProcessResult result = this.context.newProcess().errorHandling(ProcessErrorHandling.LOG_WARNING).executable("reg").addArgs("query", path, "/v", key)
.run(ProcessMode.DEFAULT_CAPTURE);
if (!result.isSuccessful()) {
return null;
}
List<String> out = result.getOut();
return retrieveRegString(key, out);
List<String> out = runReg("query", path, "/v", key);
if (out != null) {
return retrieveRegString(key, out);
}
return null;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great that you extracted the call of reg as process for reuse via method runReg.
By here you are now calling it twice:

  1. in line 98/99 there is the leftover of the original call.
  2. In line 103 you call it again.

You should remove lines 98-102 that do not make sense any more.

Comment thread cli/src/main/java/com/devonfw/tools/ide/tool/docker/Docker.java
@hohwille

Copy link
Copy Markdown
Member

BTW: If you would put breakpoints into getInstalledVersion() and getInstalledEdition and run e.g. Docker we would notice that the same things are called multiple times what is kind of waste.
We could even think of a new method for ToolCommandlet like this:

ToolEditionAndVersion getInstalledEditionAndVersion();

Then the existing getInstalledVersion() and getInstalledEdition method could just delegate to that new method and we could determine both together avoiding to do the same potentially expensive computations redundantly...
So far just a thought that came to my mind. We also do not have to over-engineer it, in case my idea would not help to make things easier...

@jakozian

Copy link
Copy Markdown
Contributor Author

Closing due to too complex implementations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docker docker and esp. DockerDesktop linux specific for linux OS (debian, ubunutu, suse, etc.) pgadmin postgres admin UI integration rancher Rancher-Desktop windows specific for Microsoft Windows OS

Projects

Status: ✅ Done

Development

Successfully merging this pull request may close these issues.

install of pgadmin throws an IllegalStateException when the install wizzard starts

4 participants