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

devonfw / IDEasy / 21099258243

17 Jan 2026 06:52PM UTC coverage: 70.349% (-0.006%) from 70.355%
21099258243

push

github

hohwille
Merge branch 'maybeec-feature/#1667-fix-env-install-trigger'

4017 of 6298 branches covered (63.78%)

Branch coverage included in aggregate %.

10444 of 14258 relevant lines covered (73.25%)

3.17 hits per line

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

88.75
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
      return pm.runTool(pc, request.getProcessMode(), request.getArgs());
8✔
90
    } else {
91
      ToolInstallRequest installRequest = new ToolInstallRequest(true);
5✔
92
      installRequest.setProcessContext(pc);
3✔
93
      return pm.runTool(installRequest, request.getProcessMode(), request.getArgs());
8✔
94
    }
95
  }
96

97
  protected void completeRequest(PackageManagerRequest request) {
98

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

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

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

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

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

149
  @Override
150
  protected boolean isIgnoreMissingSoftwareVersionFile() {
151

152
    return true;
×
153
  }
154

155
  private VersionIdentifier determineInstalledVersion() {
156

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

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

173
  @Override
174
  public VersionIdentifier getInstalledVersion() {
175

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

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

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

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

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

208
  protected abstract LocalToolCommandlet getParentTool();
209

210
  @Override
211
  public Path getToolPath() {
212

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

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