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

devonfw / IDEasy / 9610011440

21 Jun 2024 07:34AM UTC coverage: 60.67% (-0.007%) from 60.677%
9610011440

push

github

web-flow
#395: Improve uninstall to also support graalvm (#397)

1886 of 3417 branches covered (55.19%)

Branch coverage included in aggregate %.

4940 of 7834 relevant lines covered (63.06%)

2.75 hits per line

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

74.1
cli/src/main/java/com/devonfw/tools/ide/tool/ToolCommandlet.java
1
package com.devonfw.tools.ide.tool;
2

3
import com.devonfw.tools.ide.commandlet.Commandlet;
4
import com.devonfw.tools.ide.common.Tag;
5
import com.devonfw.tools.ide.common.Tags;
6
import com.devonfw.tools.ide.context.IdeContext;
7
import com.devonfw.tools.ide.environment.EnvironmentVariables;
8
import com.devonfw.tools.ide.environment.EnvironmentVariablesType;
9
import com.devonfw.tools.ide.os.MacOsHelper;
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.property.StringProperty;
14
import com.devonfw.tools.ide.version.VersionIdentifier;
15

16
import java.io.IOException;
17
import java.nio.file.Files;
18
import java.nio.file.Path;
19
import java.util.List;
20
import java.util.Set;
21

22
/**
23
 * {@link Commandlet} for a tool integrated into the IDE.
24
 */
25
public abstract class ToolCommandlet extends Commandlet implements Tags {
26

27
  /** @see #getName() */
28
  protected final String tool;
29

30
  private final Set<Tag> tags;
31

32
  /** The commandline arguments to pass to the tool. */
33
  public final StringProperty arguments;
34

35
  private MacOsHelper macOsHelper;
36

37
  /**
38
   * The constructor.
39
   *
40
   * @param context the {@link IdeContext}.
41
   * @param tool the {@link #getName() tool name}.
42
   * @param tags the {@link #getTags() tags} classifying the tool. Should be created via {@link Set#of(Object) Set.of} method.
43
   */
44
  public ToolCommandlet(IdeContext context, String tool, Set<Tag> tags) {
45

46
    super(context);
3✔
47
    this.tool = tool;
3✔
48
    this.tags = tags;
3✔
49
    addKeyword(tool);
3✔
50
    this.arguments = new StringProperty("", false, true, "args");
9✔
51
    initProperties();
2✔
52
  }
1✔
53

54
  /**
55
   * Add initial Properties to the tool
56
   */
57
  protected void initProperties() {
58

59
    add(this.arguments);
5✔
60
  }
1✔
61

62
  /**
63
   * @return the name of the tool (e.g. "java", "mvn", "npm", "node").
64
   */
65
  @Override
66
  public String getName() {
67

68
    return this.tool;
3✔
69
  }
70

71
  /**
72
   * @return the name of the binary executable for this tool.
73
   */
74
  protected String getBinaryName() {
75

76
    return this.tool;
3✔
77
  }
78

79
  @Override
80
  public final Set<Tag> getTags() {
81

82
    return this.tags;
×
83
  }
84

85
  @Override
86
  public void run() {
87

88
    runTool(ProcessMode.DEFAULT, null, this.arguments.asArray());
7✔
89
  }
1✔
90

91
  /**
92
   * Ensures the tool is installed and then runs this tool with the given arguments.
93
   *
94
   * @param processMode see {@link ProcessMode}
95
   * @param toolVersion the explicit version (pattern) to run. Typically {@code null} to ensure the configured version is installed and use that one. Otherwise,
96
   * the specified version will be installed in the software repository without touching and IDE installation and used to run.
97
   * @param args the command-line arguments to run the tool.
98
   */
99
  public void runTool(ProcessMode processMode, VersionIdentifier toolVersion, String... args) {
100

101
    Path binaryPath;
102
    Path toolPath = Path.of(getBinaryName());
6✔
103
    if (toolVersion == null) {
2!
104
      install(true);
4✔
105
      binaryPath = toolPath;
3✔
106
    } else {
107
      throw new UnsupportedOperationException("Not yet implemented!");
×
108
    }
109
    ProcessContext pc = this.context.newProcess().errorHandling(ProcessErrorHandling.WARNING).executable(binaryPath).addArgs(args);
10✔
110

111
    pc.run(processMode);
4✔
112
  }
1✔
113

114
  /**
115
   * @param toolVersion the explicit {@link VersionIdentifier} of the tool to run.
116
   * @param args the command-line arguments to run the tool.
117
   * @see ToolCommandlet#runTool(ProcessMode, VersionIdentifier, String...)
118
   */
119
  public void runTool(VersionIdentifier toolVersion, String... args) {
120

121
    runTool(ProcessMode.DEFAULT, toolVersion, args);
5✔
122
  }
1✔
123

124
  /**
125
   * @return the {@link EnvironmentVariables#getToolEdition(String) tool edition}.
126
   */
127
  public String getEdition() {
128

129
    return this.context.getVariables().getToolEdition(getName());
7✔
130
  }
131

132
  /**
133
   * @return the {@link #getName() tool} with its {@link #getEdition() edition}. The edition will be omitted if same as tool.
134
   * @see #getToolWithEdition(String, String)
135
   */
136
  protected final String getToolWithEdition() {
137

138
    return getToolWithEdition(getName(), getEdition());
6✔
139
  }
140

141
  /**
142
   * @param tool the tool name.
143
   * @param edition the edition.
144
   * @return the {@link #getName() tool} with its {@link #getEdition() edition}. The edition will be omitted if same as tool.
145
   */
146
  protected final static String getToolWithEdition(String tool, String edition) {
147

148
    if (tool.equals(edition)) {
4!
149
      return tool;
2✔
150
    }
151
    return tool + "/" + edition;
×
152
  }
153

154
  /**
155
   * @return the {@link EnvironmentVariables#getToolVersion(String) tool version}.
156
   */
157
  public VersionIdentifier getConfiguredVersion() {
158

159
    return this.context.getVariables().getToolVersion(getName());
7✔
160
  }
161

162
  /**
163
   * Method to be called for {@link #install(boolean)} from dependent {@link com.devonfw.tools.ide.commandlet.Commandlet}s.
164
   *
165
   * @return {@code true} if the tool was newly installed, {@code false} if the tool was already installed before and nothing has changed.
166
   */
167
  public boolean install() {
168

169
    return install(true);
4✔
170
  }
171

172
  /**
173
   * Performs the installation of the {@link #getName() tool} managed by this {@link com.devonfw.tools.ide.commandlet.Commandlet}.
174
   *
175
   * @param silent - {@code true} if called recursively to suppress verbose logging, {@code false} otherwise.
176
   * @return {@code true} if the tool was newly installed, {@code false} if the tool was already installed before and nothing has changed.
177
   */
178
  public boolean install(boolean silent) {
179

180
    return doInstall(silent);
4✔
181
  }
182

183
  /**
184
   * Installs or updates the managed {@link #getName() tool}.
185
   *
186
   * @param silent - {@code true} if called recursively to suppress verbose logging, {@code false} otherwise.
187
   * @return {@code true} if the tool was newly installed, {@code false} if the tool was already installed before and nothing has changed.
188
   */
189
  protected abstract boolean doInstall(boolean silent);
190

191
  /**
192
   * This method is called after the tool has been newly installed or updated to a new version.
193
   */
194
  protected void postInstall() {
195

196
    // nothing to do by default
197
  }
1✔
198

199
  /**
200
   * @return {@code true} to extract (unpack) the downloaded binary file, {@code false} otherwise.
201
   */
202
  protected boolean isExtract() {
203

204
    return true;
2✔
205
  }
206

207
  /**
208
   * @return the {@link MacOsHelper} instance.
209
   */
210
  protected MacOsHelper getMacOsHelper() {
211

212
    if (this.macOsHelper == null) {
3✔
213
      this.macOsHelper = new MacOsHelper(this.context);
7✔
214
    }
215
    return this.macOsHelper;
3✔
216
  }
217

218
  /**
219
   * @return the currently installed {@link VersionIdentifier version} of this tool or {@code null} if not installed.
220
   */
221
  public VersionIdentifier getInstalledVersion() {
222

223
    return getInstalledVersion(this.context.getSoftwarePath().resolve(getName()));
9✔
224
  }
225

226
  /**
227
   * @param toolPath the installation {@link Path} where to find the version file.
228
   * @return the currently installed {@link VersionIdentifier version} of this tool or {@code null} if not installed.
229
   */
230
  protected VersionIdentifier getInstalledVersion(Path toolPath) {
231

232
    if (!Files.isDirectory(toolPath)) {
5✔
233
      this.context.debug("Tool {} not installed in {}", getName(), toolPath);
15✔
234
      return null;
2✔
235
    }
236
    Path toolVersionFile = toolPath.resolve(IdeContext.FILE_SOFTWARE_VERSION);
4✔
237
    if (!Files.exists(toolVersionFile)) {
5✔
238
      Path legacyToolVersionFile = toolPath.resolve(IdeContext.FILE_LEGACY_SOFTWARE_VERSION);
4✔
239
      if (Files.exists(legacyToolVersionFile)) {
5✔
240
        toolVersionFile = legacyToolVersionFile;
3✔
241
      } else {
242
        this.context.warning("Tool {} is missing version file in {}", getName(), toolVersionFile);
15✔
243
        return null;
2✔
244
      }
245
    }
246
    try {
247
      String version = Files.readString(toolVersionFile).trim();
4✔
248
      return VersionIdentifier.of(version);
3✔
249
    } catch (IOException e) {
×
250
      throw new IllegalStateException("Failed to read file " + toolVersionFile, e);
×
251
    }
252
  }
253

254
  /**
255
   * @return the installed edition of this tool or {@code null} if not installed.
256
   */
257
  public String getInstalledEdition() {
258

259
    return getInstalledEdition(this.context.getSoftwarePath().resolve(getName()));
9✔
260
  }
261

262
  /**
263
   * @param toolPath the installation {@link Path} where to find currently installed tool. The name of the parent directory of the real path corresponding to
264
   * the passed {@link Path path} must be the name of the edition.
265
   * @return the installed edition of this tool or {@code null} if not installed.
266
   */
267
  public String getInstalledEdition(Path toolPath) {
268

269
    if (!Files.isDirectory(toolPath)) {
5!
270
      this.context.debug("Tool {} not installed in {}", getName(), toolPath);
×
271
      return null;
×
272
    }
273
    try {
274
      String edition = toolPath.toRealPath().getParent().getFileName().toString();
8✔
275
      if (!this.context.getUrls().getSortedEditions(getName()).contains(edition)) {
9!
276
        edition = getEdition();
3✔
277
      }
278
      return edition;
2✔
279
    } catch (IOException e) {
×
280
      throw new IllegalStateException("Couldn't determine the edition of " + getName() + " from the directory structure of its software path " + toolPath
×
281
          + ", assuming the name of the parent directory of the real path of the software path to be the edition " + "of the tool.", e);
282
    }
283

284
  }
285

286
  /**
287
   * Uninstalls the {@link #getName() tool}.
288
   */
289
  public abstract void uninstall();
290

291
  /**
292
   * List the available editions of this tool.
293
   */
294
  public void listEditions() {
295

296
    List<String> editions = this.context.getUrls().getSortedEditions(getName());
7✔
297
    for (String edition : editions) {
10✔
298
      this.context.info(edition);
4✔
299
    }
1✔
300
  }
1✔
301

302
  /**
303
   * List the available versions of this tool.
304
   */
305
  public void listVersions() {
306

307
    List<VersionIdentifier> versions = this.context.getUrls().getSortedVersions(getName(), getEdition());
9✔
308
    for (VersionIdentifier vi : versions) {
10✔
309
      this.context.info(vi.toString());
5✔
310
    }
1✔
311
  }
1✔
312

313
  /**
314
   * Sets the tool version in the environment variable configuration file.
315
   *
316
   * @param version the version (pattern) to set.
317
   */
318
  public void setVersion(String version) {
319

320
    if ((version == null) || version.isBlank()) {
×
321
      throw new IllegalStateException("Version has to be specified!");
×
322
    }
323
    VersionIdentifier configuredVersion = VersionIdentifier.of(version);
×
324
    if (!configuredVersion.isPattern() && !configuredVersion.isValid()) {
×
325
      this.context.warning("Version {} seems to be invalid", version);
×
326
    }
327
    setVersion(configuredVersion, true);
×
328
  }
×
329

330
  /**
331
   * Sets the tool version in the environment variable configuration file.
332
   *
333
   * @param version the version to set. May also be a {@link VersionIdentifier#isPattern() version pattern}.
334
   * @param hint - {@code true} to print the installation hint, {@code false} otherwise.
335
   */
336
  public void setVersion(VersionIdentifier version, boolean hint) {
337

338
    String edition = getEdition();
3✔
339
    this.context.getUrls().getVersionFolder(tool, edition, version); // CliException is thrown if the version is not existing
9✔
340

341
    EnvironmentVariables variables = this.context.getVariables();
4✔
342
    EnvironmentVariables settingsVariables = variables.getByType(EnvironmentVariablesType.SETTINGS);
4✔
343
    String name = EnvironmentVariables.getToolVersionVariable(this.tool);
4✔
344
    VersionIdentifier resolvedVersion = this.context.getUrls().getVersion(this.tool, edition, version);
9✔
345
    if (version.isPattern()) {
3!
346
      this.context.debug("Resolved version {} to {} for tool {}/{}", version, resolvedVersion, this.tool, edition);
×
347
    }
348
    settingsVariables.set(name, resolvedVersion.toString(), false);
7✔
349
    settingsVariables.save();
2✔
350
    this.context.info("{}={} has been set in {}", name, version, settingsVariables.getSource());
19✔
351
    EnvironmentVariables declaringVariables = variables.findVariable(name);
4✔
352
    if ((declaringVariables != null) && (declaringVariables != settingsVariables)) {
5!
353
      this.context.warning("The variable {} is overridden in {}. Please remove the overridden declaration in order to make the change affect.", name,
×
354
          declaringVariables.getSource());
×
355
    }
356
    if (hint) {
2✔
357
      this.context.info("To install that version call the following command:");
4✔
358
      this.context.info("ide install {}", this.tool);
11✔
359
    }
360
  }
1✔
361

362
  /**
363
   * Sets the tool edition in the environment variable configuration file.
364
   *
365
   * @param edition the edition to set.
366
   */
367
  public void setEdition(String edition) {
368

369
    setEdition(edition, true);
4✔
370
  }
1✔
371

372
  /**
373
   * Sets the tool edition in the environment variable configuration file.
374
   *
375
   * @param edition the edition to set
376
   * @param hint - {@code true} to print the installation hint, {@code false} otherwise.
377
   */
378
  public void setEdition(String edition, boolean hint) {
379

380
    if ((edition == null) || edition.isBlank()) {
5!
381
      throw new IllegalStateException("Edition has to be specified!");
×
382
    }
383

384
    if (!Files.exists(this.context.getUrls().getEdition(getName(), edition).getPath())) {
12!
385
      this.context.warning("Edition {} seems to be invalid", edition);
10✔
386

387
    }
388
    EnvironmentVariables variables = this.context.getVariables();
4✔
389
    EnvironmentVariables settingsVariables = variables.getByType(EnvironmentVariablesType.SETTINGS);
4✔
390
    String name = EnvironmentVariables.getToolEditionVariable(this.tool);
4✔
391
    settingsVariables.set(name, edition, false);
6✔
392
    settingsVariables.save();
2✔
393

394
    this.context.info("{}={} has been set in {}", name, edition, settingsVariables.getSource());
19✔
395
    EnvironmentVariables declaringVariables = variables.findVariable(name);
4✔
396
    if ((declaringVariables != null) && (declaringVariables != settingsVariables)) {
5!
397
      this.context.warning("The variable {} is overridden in {}. Please remove the overridden declaration in order to make the change affect.", name,
×
398
          declaringVariables.getSource());
×
399
    }
400
    if (hint) {
2!
401
      this.context.info("To install that edition call the following command:");
4✔
402
      this.context.info("ide install {}", this.tool);
11✔
403
    }
404
  }
1✔
405

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