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

devonfw / IDEasy / 22241505980

20 Feb 2026 09:16PM UTC coverage: 70.656% (+0.2%) from 70.474%
22241505980

Pull #1710

github

web-flow
Merge 04e4bdacd into 379acdc9d
Pull Request #1710: #404: allow logging via SLF4J

4121 of 6440 branches covered (63.99%)

Branch coverage included in aggregate %.

10704 of 14542 relevant lines covered (73.61%)

3.13 hits per line

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

90.12
cli/src/main/java/com/devonfw/tools/ide/tool/PackageManagerBasedLocalToolCommandlet.java
1
package com.devonfw.tools.ide.tool;
2

3
import java.nio.file.Path;
4
import java.util.List;
5
import java.util.Set;
6

7
import org.slf4j.Logger;
8
import org.slf4j.LoggerFactory;
9

10
import com.devonfw.tools.ide.cache.CachedValue;
11
import com.devonfw.tools.ide.common.Tag;
12
import com.devonfw.tools.ide.context.IdeContext;
13
import com.devonfw.tools.ide.process.ProcessContext;
14
import com.devonfw.tools.ide.process.ProcessErrorHandling;
15
import com.devonfw.tools.ide.process.ProcessMode;
16
import com.devonfw.tools.ide.process.ProcessResult;
17
import com.devonfw.tools.ide.version.VersionIdentifier;
18

19
/**
20
 * {@link LocalToolCommandlet} for tools that have their own {@link #getToolRepository() repository} and do not follow standard installation mechanism.
21
 *
22
 * @param <P> type of the {@link ToolCommandlet} acting as {@link #getPackageManagerClass() package manager}.
23
 */
24
public abstract class PackageManagerBasedLocalToolCommandlet<P extends ToolCommandlet> extends LocalToolCommandlet {
25

26
  private static final Logger LOG = LoggerFactory.getLogger(PackageManagerBasedLocalToolCommandlet.class);
4✔
27

28
  private final CachedValue<VersionIdentifier> installedVersion;
29

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

39
    super(context, tool, tags);
5✔
40
    this.installedVersion = new CachedValue<>(this::determineInstalledVersion);
7✔
41
  }
1✔
42

43
  @Override
44
  protected boolean isIgnoreSoftwareRepo() {
45

46
    // python/pip and node/npm/yarn are messy - see https://github.com/devonfw/IDEasy/issues/352
47
    return true;
2✔
48
  }
49

50
  @Override
51
  public boolean isInstalled() {
52

53
    // Check if parent tool is installed first - if not, this tool cannot be installed
54
    LocalToolCommandlet parentTool = getParentTool();
3✔
55
    if (!parentTool.isInstalled()) {
3!
56
      return false;
2✔
57
    }
58

59
    // Check if the tool binary is found
60
    return getBinaryExecutable() != null;
×
61
  }
62

63
  protected abstract Class<P> getPackageManagerClass();
64

65
  /**
66
   * @return the package name of this tool in the underlying repository (e.g. MVN repo, NPM registry, PyPI). Typically, this is the same as the
67
   *     {@link #getName() tool name} but may be overridden in some special cases.
68
   */
69
  public String getPackageName() {
70

71
    return this.tool;
3✔
72
  }
73

74
  /**
75
   * @param request the {@link PackageManagerRequest}.
76
   * @return the {@link ProcessResult}.
77
   */
78
  public ProcessResult runPackageManager(PackageManagerRequest request) {
79
    return runPackageManager(request, false);
5✔
80
  }
81

82
  /**
83
   * @param request the {@link PackageManagerRequest}.
84
   * @param skipInstallation {@code true} if the caller can guarantee that this package manager tool is already installed, {@code false} otherwise (run
85
   *     install method again to ensure the tool is installed).
86
   * @return the {@link ProcessResult}.
87
   */
88
  public ProcessResult runPackageManager(PackageManagerRequest request, boolean skipInstallation) {
89

90
    completeRequest(request);
3✔
91
    ProcessContext pc = request.getProcessContext();
3✔
92
    ToolCommandlet pm = request.getPackageManager();
3✔
93
    if (!skipInstallation) { // See Node.postInstallOnNewInstallation
2✔
94
      ToolInstallRequest installRequest = new ToolInstallRequest(true);
5✔
95
      installRequest.setProcessContext(pc.createChild());
4✔
96
      pm.install(installRequest);
4✔
97
    }
98
    return pm.runTool(pc, request.getProcessMode(), request.getArgs());
8✔
99
  }
100

101
  protected void completeRequest(PackageManagerRequest request) {
102

103
    if (request.getProcessContext() == null) {
3✔
104
      request.setProcessContext(this.context.newProcess().errorHandling(ProcessErrorHandling.THROW_CLI));
8✔
105
    }
106
    if (request.getPackageManager() == null) {
3✔
107
      request.setPackageManager(this.context.getCommandletManager().getCommandlet(getPackageManagerClass()));
10✔
108
    }
109
    if (request.getProcessMode() == null) {
3✔
110
      request.setProcessMode(ProcessMode.DEFAULT);
4✔
111
    }
112
    if (request.getArgs().isEmpty()) {
4✔
113
      completeRequestArgs(request);
3✔
114
    }
115
  }
1✔
116

117
  /**
118
   * @param request the {@link PackageManagerRequest} with currently {@link List#isEmpty() empty} {@link PackageManagerRequest#getArgs() args}.
119
   */
120
  protected void completeRequestArgs(PackageManagerRequest request) {
121

122
    String toolWithVersion = request.getTool();
3✔
123
    VersionIdentifier version = request.getVersion();
3✔
124
    if (version != null) {
2✔
125
      toolWithVersion = appendVersion(toolWithVersion, version);
5✔
126
    }
127
    request.addArg(request.getType());
5✔
128
    String option = completeRequestOption(request);
4✔
129
    if (option != null) {
2✔
130
      request.addArg(option);
4✔
131
    }
132
    request.addArg(toolWithVersion);
4✔
133
  }
1✔
134

135
  /**
136
   * @param request the {@link PackageManagerRequest}.
137
   * @return the option to {@link PackageManagerRequest#addArg(String) add as argument} to the package manager sub-command (e.g. "-gf") or {@code null} for no
138
   *     option.
139
   */
140
  protected String completeRequestOption(PackageManagerRequest request) {
141
    return null;
2✔
142
  }
143

144
  /**
145
   * @param tool the {@link PackageManagerRequest#getTool() tool} to manage (e.g. install).
146
   * @param version the {@link PackageManagerRequest#getVersion() version} to append.
147
   * @return the combination of {@code tool} and {@code version} in the syntax of the package manager.
148
   */
149
  protected String appendVersion(String tool, VersionIdentifier version) {
150
    return tool + '@' + version;
5✔
151
  }
152

153
  @Override
154
  protected boolean isIgnoreMissingSoftwareVersionFile() {
155

156
    return true;
×
157
  }
158

159
  private VersionIdentifier determineInstalledVersion() {
160

161
    try {
162
      return computeInstalledVersion();
3✔
163
    } catch (Exception e) {
×
164
      LOG.debug("Failed to compute installed version of {}", this.tool, e);
×
165
      return null;
×
166
    }
167
  }
168

169
  /**
170
   * @return the computed value of the {@link #getInstalledVersion() installed version}.
171
   * @implNote Implementations of this method should NOT trigger any tool installation or download. If you need to call
172
   *     {@link #runPackageManager(PackageManagerRequest)}, make sure to use {@link #runPackageManager(PackageManagerRequest, boolean)} with
173
   *     {@code skipInstallation=true} to avoid inadvertently triggering installations when only checking the version.
174
   */
175
  protected abstract VersionIdentifier computeInstalledVersion();
176

177
  @Override
178
  public VersionIdentifier getInstalledVersion() {
179

180
    return this.installedVersion.get();
5✔
181
  }
182

183
  @Override
184
  protected final void performToolInstallation(ToolInstallRequest request, Path installationPath) {
185

186
    PackageManagerRequest packageManagerRequest = new PackageManagerRequest(PackageManagerRequest.TYPE_INSTALL, getPackageName())
7✔
187
        .setProcessContext(request.getProcessContext()).setVersion(request.getRequested().getResolvedVersion());
7✔
188
    runPackageManager(packageManagerRequest, true).failOnError();
5✔
189
    this.installedVersion.invalidate();
3✔
190
  }
1✔
191

192
  /**
193
   * @return {@code true} if the tool can be uninstalled, {@code false} if not.
194
   */
195
  protected boolean canBeUninstalled() {
196
    return true;
2✔
197
  }
198

199
  @Override
200
  protected final void performUninstall(Path toolPath) {
201
    if (canBeUninstalled()) {
3✔
202
      PackageManagerRequest request = new PackageManagerRequest(PackageManagerRequest.TYPE_UNINSTALL, getPackageName());
7✔
203
      runPackageManager(request).failOnError();
4✔
204
      this.installedVersion.invalidate();
3✔
205
    } else {
1✔
206
      LOG.info("IDEasy does not support uninstalling the tool {} since this will break your installation.\n"
6✔
207
          + "If you really want to uninstall it, please uninstall its parent tool via:\n"
208
          + "ide uninstall {}", this.tool, getParentTool().getName());
2✔
209
    }
210
  }
1✔
211

212
  protected abstract LocalToolCommandlet getParentTool();
213

214
  @Override
215
  public Path getToolPath() {
216

217
    return getParentTool().getToolPath();
4✔
218
  }
219

220
}
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