• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

devonfw / IDEasy / 27450388724

13 Jun 2026 12:11AM UTC coverage: 70.811% (-0.2%) from 71.052%
27450388724

Pull #2018

github

web-flow
Merge e98a6e89f into 8b8989cb3
Pull Request #2018: fixed pgAdmin macOS dmg installation

4538 of 7102 branches covered (63.9%)

Branch coverage included in aggregate %.

11750 of 15900 relevant lines covered (73.9%)

3.13 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

10.77
cli/src/main/java/com/devonfw/tools/ide/tool/GlobalToolCommandlet.java
1
package com.devonfw.tools.ide.tool;
2

3
import java.nio.file.Files;
4
import java.nio.file.Path;
5
import java.util.Arrays;
6
import java.util.List;
7
import java.util.Set;
8

9
import org.slf4j.Logger;
10
import org.slf4j.LoggerFactory;
11

12
import com.devonfw.tools.ide.cli.CliException;
13
import com.devonfw.tools.ide.common.Tag;
14
import com.devonfw.tools.ide.context.IdeContext;
15
import com.devonfw.tools.ide.io.FileAccess;
16
import com.devonfw.tools.ide.log.IdeLogLevel;
17
import com.devonfw.tools.ide.process.ProcessContext;
18
import com.devonfw.tools.ide.process.ProcessErrorHandling;
19
import com.devonfw.tools.ide.process.ProcessMode;
20
import com.devonfw.tools.ide.step.Step;
21
import com.devonfw.tools.ide.tool.repository.ToolRepository;
22
import com.devonfw.tools.ide.version.VersionIdentifier;
23

24
/**
25
 * {@link ToolCommandlet} that is installed globally.
26
 */
27
public abstract class GlobalToolCommandlet extends ToolCommandlet {
28

29
  private static final Logger LOG = LoggerFactory.getLogger(GlobalToolCommandlet.class);
4✔
30

31
  /**
32
   * The constructor.
33
   *
34
   * @param context the {@link IdeContext}.
35
   * @param tool the {@link #getName() tool name}.
36
   * @param tags the {@link #getTags() tags} classifying the tool. Should be created via {@link Set#of(Object) Set.of} method.
37
   */
38
  public GlobalToolCommandlet(IdeContext context, String tool, Set<Tag> tags) {
39

40
    super(context, tool, tags);
5✔
41
  }
1✔
42

43
  /**
44
   * Performs the installation or uninstallation of the {@link #getName() tool} via a package manager.
45
   *
46
   * @param silent {@code true} if called recursively to suppress verbose logging, {@code false} otherwise.
47
   * @param commandStrings commandStrings The package manager command strings to execute.
48
   * @return {@code true} if installation or uninstallation succeeds with any of the package manager commands, {@code false} otherwise.
49
   */
50
  protected boolean runWithPackageManager(boolean silent, String... commandStrings) {
51

52
    List<PackageManagerCommand> pmCommands = Arrays.stream(commandStrings).map(PackageManagerCommand::of).toList();
×
53
    return runWithPackageManager(silent, pmCommands);
×
54
  }
55

56
  /**
57
   * Performs the installation or uninstallation of the {@link #getName() tool} via a package manager.
58
   *
59
   * @param silent {@code true} if called recursively to suppress verbose logging, {@code false} otherwise.
60
   * @param pmCommands A list of {@link PackageManagerCommand} to be used for installation or uninstallation.
61
   * @return {@code true} if installation or uninstallation succeeds with any of the package manager commands, {@code false} otherwise.
62
   */
63
  protected boolean runWithPackageManager(boolean silent, List<PackageManagerCommand> pmCommands) {
64

65
    for (PackageManagerCommand pmCommand : pmCommands) {
×
66
      NativePackageManager packageManager = pmCommand.packageManager();
×
67
      Path packageManagerPath = this.context.getPath().findBinary(Path.of(packageManager.getBinaryName()));
×
68
      if (packageManagerPath == null || !Files.exists(packageManagerPath)) {
×
69
        LOG.debug("{} is not installed", packageManager.toString());
×
70
        continue; // Skip to the next package manager command
×
71
      }
72

73
      if (executePackageManagerCommand(pmCommand, silent)) {
×
74
        return true; // Success
×
75
      }
76
    }
×
77
    return false; // None of the package manager commands were successful
×
78
  }
79

80
  /**
81
   * Logs the privileged commands before execution so the user knows why sudo/root permissions are requested.
82
   *
83
   * @param commands the privileged commands to log.
84
   */
85
  protected void logPrivilegedCommands(List<String> commands) {
86

87
    IdeLogLevel level = IdeLogLevel.INTERACTION;
×
88
    level.log(LOG, "We need to run the following privileged command(s):");
×
89
    for (String command : commands) {
×
90
      level.log(LOG, command);
×
91
    }
×
92
    level.log(LOG, "This will require root permissions!");
×
93
  }
×
94

95
  private void logPackageManagerCommands(PackageManagerCommand pmCommand) {
96

97
    logPrivilegedCommands(pmCommand.commands());
×
98
  }
×
99

100
  /**
101
   * Executes the provided package manager command.
102
   *
103
   * @param pmCommand The {@link PackageManagerCommand} containing the commands to execute.
104
   * @param silent {@code true} if called recursively to suppress verbose logging, {@code false} otherwise.
105
   * @return {@code true} if the package manager commands execute successfully, {@code false} otherwise.
106
   */
107
  private boolean executePackageManagerCommand(PackageManagerCommand pmCommand, boolean silent) {
108

109
    String bashPath = this.context.findBashRequired().toString();
×
110
    logPackageManagerCommands(pmCommand);
×
111
    for (String command : pmCommand.commands()) {
×
112
      ProcessContext pc = this.context.newProcess().errorHandling(ProcessErrorHandling.LOG_WARNING).executable(bashPath)
×
113
          .addArgs("-c", command);
×
114
      int exitCode = pc.run();
×
115
      if (exitCode != 0) {
×
116
        LOG.warn("{} command did not execute successfully", command);
×
117
        return false;
×
118
      }
119
    }
×
120

121
    if (!silent) {
×
122
      IdeLogLevel.SUCCESS.log(LOG, "Successfully installed {}", this.tool);
×
123
    }
124
    return true;
×
125
  }
126

127
  @Override
128
  protected boolean isExtract() {
129

130
    // for global tools we usually download installers and do not want to extract them (e.g. installer.msi file shall
131
    // not be extracted)
132
    return false;
×
133
  }
134

135
  @Override
136
  protected ToolInstallation doInstall(ToolInstallRequest request) {
137

138
    VersionIdentifier resolvedVersion = request.getRequested().getResolvedVersion();
×
139
    if (this.context.getSystemInfo().isLinux()) {
×
140
      // on Linux global tools are typically installed via the package manager of the OS
141
      // if a global tool implements this method to return at least one PackageManagerCommand, then we install this way.
142
      List<PackageManagerCommand> commands = getInstallPackageManagerCommands();
×
143
      if (!commands.isEmpty()) {
×
144
        boolean newInstallation = runWithPackageManager(request.isSilent(), commands);
×
145
        Path rootDir = getInstallationPath(getConfiguredEdition(), resolvedVersion);
×
146
        return createToolInstallation(rootDir, resolvedVersion, newInstallation, request.getProcessContext(), request.isAdditionalInstallation());
×
147
      }
148
    }
149

150
    ToolEdition toolEdition = getToolWithConfiguredEdition();
×
151
    Path installationPath = getInstallationPath(toolEdition.edition(), resolvedVersion);
×
152
    // if force mode is enabled, go through with the installation even if the tool is already installed
153
    if ((installationPath != null) && !this.context.isForceMode()) {
×
154
      return toolAlreadyInstalled(request);
×
155
    }
156
    String edition = toolEdition.edition();
×
157
    ToolRepository toolRepository = this.context.getDefaultToolRepository();
×
158
    resolvedVersion = cveCheck(request);
×
159
    // download and install the global tool
160
    FileAccess fileAccess = this.context.getFileAccess();
×
161
    Path target = toolRepository.download(this.tool, edition, resolvedVersion, this);
×
162
    Path executable = target;
×
163
    Path tmpDir = null;
×
164
    boolean extract = isExtract();
×
165
    if (extract) {
×
166
      tmpDir = fileAccess.createTempDir(getName());
×
167
      Path downloadBinaryPath = tmpDir.resolve(target.getFileName());
×
168
      fileAccess.extract(target, downloadBinaryPath);
×
169
      executable = fileAccess.findFirst(downloadBinaryPath, Files::isExecutable, false);
×
170
    }
171
    ProcessContext pc = this.context.newProcess().errorHandling(ProcessErrorHandling.LOG_WARNING).executable(executable);
×
172
    int exitCode = pc.run(ProcessMode.BACKGROUND_SILENT).getExitCode();
×
173
    if (tmpDir != null) {
×
174
      fileAccess.delete(tmpDir);
×
175
    }
176
    if (exitCode == 0) {
×
177
      IdeLogLevel.SUCCESS.log(LOG, "Installation process for {} in version {} has started", this.tool, resolvedVersion);
×
178
      Step step = request.getStep();
×
179
      if (step != null) {
×
180
        step.success(true);
×
181
      }
182
    } else {
×
183
      throw new CliException("Installation process for " + this.tool + " in version " + resolvedVersion + " failed with exit code " + exitCode + "!");
×
184
    }
185
    installationPath = getInstallationPath(toolEdition.edition(), resolvedVersion);
×
186
    if (installationPath == null) {
×
187
      throw new CliException("The tool " + this.tool + " is about to be installed. Please complete the installation and if required "
×
188
          + "reboot your machine. Then rerun the command to start the tool.", 2);
189
    }
190
    return createToolInstallation(installationPath, resolvedVersion, true, pc, false);
×
191
  }
192

193
  /**
194
   * @return the {@link List} of {@link PackageManagerCommand}s to use on Linux to install this tool. If empty, no package manager installation will be
195
   *     triggered on Linux.
196
   */
197
  protected List<PackageManagerCommand> getInstallPackageManagerCommands() {
198
    return List.of();
×
199
  }
200

201
  @Override
202
  public VersionIdentifier getInstalledVersion() {
203
    //TODO: handle "get-version <globaltool>"
204
    return null;
×
205
  }
206

207
  @Override
208
  public String getInstalledEdition() {
209
    //TODO: handle "get-edition <globaltool>"
210
    return null;
×
211
  }
212

213
  @Override
214
  protected Path getInstallationPath(String edition, VersionIdentifier resolvedVersion) {
215

216
    Path toolBinary = Path.of(getBinaryName());
6✔
217
    Path binaryPath = this.context.getPath().findBinary(toolBinary);
6✔
218
    if ((binaryPath == toolBinary) || !Files.exists(binaryPath)) {
8!
219
      return null;
2✔
220
    }
221
    Path binPath = binaryPath.getParent();
3✔
222
    if (binPath == null) {
2!
223
      return null;
×
224
    }
225
    return this.context.getFileAccess().getBinParentPath(binPath);
6✔
226
  }
227

228
  @Override
229
  public void uninstall() {
230
    //TODO: handle "uninstall <globaltool>"
231
    LOG.error("Couldn't uninstall " + this.getName());
×
232
  }
×
233
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc