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

devonfw / IDEasy / 21174240071

20 Jan 2026 02:00PM UTC coverage: 70.447% (-0.02%) from 70.471%
21174240071

Pull #1681

github

web-flow
Merge a3e58d5e7 into 365b39a49
Pull Request #1681: #1679: fix determine version if npm list has exit code 1

4031 of 6308 branches covered (63.9%)

Branch coverage included in aggregate %.

10479 of 14289 relevant lines covered (73.34%)

3.18 hits per line

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

90.0
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 com.devonfw.tools.ide.cache.CachedValue;
8
import com.devonfw.tools.ide.common.Tag;
9
import com.devonfw.tools.ide.context.IdeContext;
10
import com.devonfw.tools.ide.process.ProcessContext;
11
import com.devonfw.tools.ide.process.ProcessErrorHandling;
12
import com.devonfw.tools.ide.process.ProcessMode;
13
import com.devonfw.tools.ide.process.ProcessResult;
14
import com.devonfw.tools.ide.version.VersionIdentifier;
15

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

23
  private final CachedValue<VersionIdentifier> installedVersion;
24

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

34
    super(context, tool, tags);
5✔
35
    this.installedVersion = new CachedValue<>(this::determineInstalledVersion);
7✔
36
  }
1✔
37

38
  @Override
39
  protected boolean isIgnoreSoftwareRepo() {
40

41
    // python/pip and node/npm/yarn are messy - see https://github.com/devonfw/IDEasy/issues/352
42
    return true;
2✔
43
  }
44

45
  @Override
46
  public boolean isInstalled() {
47

48
    // Check if parent tool is installed first - if not, this tool cannot be installed
49
    LocalToolCommandlet parentTool = getParentTool();
3✔
50
    if (!parentTool.isInstalled()) {
3!
51
      return false;
2✔
52
    }
53

54
    // Check if the tool binary is found
55
    return getBinaryExecutable() != null;
×
56
  }
57

58
  protected abstract Class<P> getPackageManagerClass();
59

60
  /**
61
   * @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
62
   *     {@link #getName() tool name} but may be overridden in some special cases.
63
   */
64
  public String getPackageName() {
65

66
    return this.tool;
3✔
67
  }
68

69
  /**
70
   * @param request the {@link PackageManagerRequest}.
71
   * @return the {@link ProcessResult}.
72
   */
73
  public ProcessResult runPackageManager(PackageManagerRequest request) {
74
    return runPackageManager(request, false);
5✔
75
  }
76

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

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

96
  protected void completeRequest(PackageManagerRequest request) {
97

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

112
  /**
113
   * @param request the {@link PackageManagerRequest} with currently {@link List#isEmpty() empty} {@link PackageManagerRequest#getArgs() args}.
114
   */
115
  protected void completeRequestArgs(PackageManagerRequest request) {
116

117
    String toolWithVersion = request.getTool();
3✔
118
    VersionIdentifier version = request.getVersion();
3✔
119
    if (version != null) {
2✔
120
      toolWithVersion = appendVersion(toolWithVersion, version);
5✔
121
    }
122
    request.addArg(request.getType());
5✔
123
    String option = completeRequestOption(request);
4✔
124
    if (option != null) {
2✔
125
      request.addArg(option);
4✔
126
    }
127
    request.addArg(toolWithVersion);
4✔
128
  }
1✔
129

130
  /**
131
   * @param request the {@link PackageManagerRequest}.
132
   * @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
133
   *     option.
134
   */
135
  protected String completeRequestOption(PackageManagerRequest request) {
136
    return null;
2✔
137
  }
138

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

148
  @Override
149
  protected boolean isIgnoreMissingSoftwareVersionFile() {
150

151
    return true;
×
152
  }
153

154
  private VersionIdentifier determineInstalledVersion() {
155

156
    try {
157
      return computeInstalledVersion();
3✔
158
    } catch (Exception e) {
×
159
      this.context.debug().log(e, "Failed to compute installed version of {}", this.tool);
×
160
      return null;
×
161
    }
162
  }
163

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

172
  @Override
173
  public VersionIdentifier getInstalledVersion() {
174

175
    return this.installedVersion.get();
5✔
176
  }
177

178
  @Override
179
  protected final void performToolInstallation(ToolInstallRequest request, Path installationPath) {
180

181
    PackageManagerRequest packageManagerRequest = new PackageManagerRequest(PackageManagerRequest.TYPE_INSTALL, getPackageName())
7✔
182
        .setProcessContext(request.getProcessContext()).setVersion(request.getRequested().getResolvedVersion());
7✔
183
    runPackageManager(packageManagerRequest, true).failOnError();
5✔
184
    this.installedVersion.invalidate();
3✔
185
  }
1✔
186

187
  /**
188
   * @return {@code true} if the tool can be uninstalled, {@code false} if not.
189
   */
190
  protected boolean canBeUninstalled() {
191
    return true;
2✔
192
  }
193

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

207
  protected abstract LocalToolCommandlet getParentTool();
208

209
  @Override
210
  public Path getToolPath() {
211

212
    return getParentTool().getToolPath();
4✔
213
  }
214

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