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

devonfw / IDEasy / 9907372175

12 Jul 2024 11:49AM UTC coverage: 61.142% (-0.02%) from 61.162%
9907372175

push

github

hohwille
fixed tests

1997 of 3595 branches covered (55.55%)

Branch coverage included in aggregate %.

5296 of 8333 relevant lines covered (63.55%)

2.8 hits per line

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

84.02
cli/src/main/java/com/devonfw/tools/ide/tool/LocalToolCommandlet.java
1
package com.devonfw.tools.ide.tool;
2

3
import java.io.IOException;
4
import java.nio.file.Files;
5
import java.nio.file.Path;
6
import java.nio.file.StandardOpenOption;
7
import java.util.HashMap;
8
import java.util.List;
9
import java.util.Set;
10

11
import com.devonfw.tools.ide.common.Tag;
12
import com.devonfw.tools.ide.context.IdeContext;
13
import com.devonfw.tools.ide.io.FileAccess;
14
import com.devonfw.tools.ide.io.FileCopyMode;
15
import com.devonfw.tools.ide.log.IdeLogLevel;
16
import com.devonfw.tools.ide.process.ProcessContext;
17
import com.devonfw.tools.ide.process.ProcessErrorHandling;
18
import com.devonfw.tools.ide.process.ProcessMode;
19
import com.devonfw.tools.ide.repo.ToolRepository;
20
import com.devonfw.tools.ide.step.Step;
21
import com.devonfw.tools.ide.url.model.file.dependencyJson.DependencyInfo;
22
import com.devonfw.tools.ide.version.VersionIdentifier;
23

24
/**
25
 * {@link ToolCommandlet} that is installed locally into the IDE.
26
 */
27
public abstract class LocalToolCommandlet extends ToolCommandlet {
1✔
28

29
  protected HashMap<String, String> dependenciesEnvVariableNames = null;
3✔
30

31
  protected HashMap<String, String> dependenciesEnvVariablePaths = new HashMap<>();
5✔
32

33
  private final Dependency dependency = new Dependency(this.context, this.tool);
9✔
34

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

44
    super(context, tool, tags);
5✔
45
  }
1✔
46

47
  /**
48
   * @return the {@link Path} where the tool is located (installed).
49
   */
50
  public Path getToolPath() {
51

52
    return this.context.getSoftwarePath().resolve(getName());
7✔
53
  }
54

55
  /**
56
   * @return the {@link Path} where the executables of the tool can be found. Typically a "bin" folder inside {@link #getToolPath() tool path}.
57
   */
58
  public Path getToolBinPath() {
59

60
    Path toolPath = getToolPath();
3✔
61
    Path binPath = this.context.getFileAccess().findFirst(toolPath, path -> path.getFileName().toString().equals("bin"), false);
14✔
62
    if ((binPath != null) && Files.isDirectory(binPath)) {
7!
63
      return binPath;
2✔
64
    }
65
    return toolPath;
2✔
66
  }
67

68
  @Override
69
  protected boolean doInstall(boolean silent) {
70

71
    VersionIdentifier configuredVersion = getConfiguredVersion();
3✔
72
    // get installed version before installInRepo actually may install the software
73
    VersionIdentifier installedVersion = getInstalledVersion();
3✔
74
    Step step = this.context.newStep(silent, "Install " + this.tool, configuredVersion);
14✔
75
    try {
76
      // install configured version of our tool in the software repository if not already installed
77
      ToolInstallation installation = installInRepo(configuredVersion);
4✔
78
      // check if we already have this version installed (linked) locally in IDE_HOME/software
79
      VersionIdentifier resolvedVersion = installation.resolvedVersion();
3✔
80
      if (resolvedVersion.equals(installedVersion) && !installation.newInstallation()) {
7!
81
        IdeLogLevel level = silent ? IdeLogLevel.DEBUG : IdeLogLevel.INFO;
6✔
82
        this.context.level(level).log("Version {} of tool {} is already installed", installedVersion, getToolWithEdition());
18✔
83
        step.success();
2✔
84
        return false;
4✔
85
      }
86
      // we need to link the version or update the link.
87
      Path toolPath = getToolPath();
3✔
88
      FileAccess fileAccess = this.context.getFileAccess();
4✔
89
      if (Files.exists(toolPath)) {
5✔
90
        fileAccess.backup(toolPath);
3✔
91
      }
92
      fileAccess.mkdirs(toolPath.getParent());
4✔
93
      fileAccess.symlink(installation.linkDir(), toolPath);
5✔
94
      this.context.getPath().setPath(this.tool, installation.binDir());
8✔
95
      postInstall();
2✔
96
      if (installedVersion == null) {
2!
97
        step.success("Successfully installed {} in version {}", this.tool, resolvedVersion);
15✔
98
      } else {
99
        step.success("Successfully installed {} in version {} replacing previous version {}", this.tool, resolvedVersion, installedVersion);
×
100
      }
101
      return true;
4✔
102
    } catch (RuntimeException e) {
×
103
      step.error(e, true);
×
104
      throw e;
×
105
    } finally {
106
      step.close();
2✔
107
    }
108

109
  }
110

111
  /**
112
   * Performs the installation of the {@link #getName() tool} managed by this {@link com.devonfw.tools.ide.commandlet.Commandlet} only in the central software
113
   * repository without touching the IDE installation.
114
   *
115
   * @param version the {@link VersionIdentifier} requested to be installed. May also be a {@link VersionIdentifier#isPattern() version pattern}.
116
   * @return the {@link ToolInstallation} in the central software repository matching the given {@code version}.
117
   */
118
  public ToolInstallation installInRepo(VersionIdentifier version) {
119

120
    return installInRepo(version, getConfiguredEdition());
6✔
121
  }
122

123
  /**
124
   * Performs the installation of the {@link #getName() tool} managed by this {@link com.devonfw.tools.ide.commandlet.Commandlet} only in the central software
125
   * repository without touching the IDE installation.
126
   *
127
   * @param version the {@link VersionIdentifier} requested to be installed. May also be a {@link VersionIdentifier#isPattern() version pattern}.
128
   * @param edition the specific edition to install.
129
   * @return the {@link ToolInstallation} in the central software repository matching the given {@code version}.
130
   */
131
  public ToolInstallation installInRepo(VersionIdentifier version, String edition) {
132

133
    return installInRepo(version, edition, this.context.getDefaultToolRepository());
8✔
134
  }
135

136
  /**
137
   * Performs the installation of the {@link #getName() tool} managed by this {@link com.devonfw.tools.ide.commandlet.Commandlet} only in the central software
138
   * repository without touching the IDE installation.
139
   *
140
   * @param version the {@link VersionIdentifier} requested to be installed. May also be a {@link VersionIdentifier#isPattern() version pattern}.
141
   * @param edition the specific edition to install.
142
   * @param toolRepository the {@link ToolRepository} to use.
143
   * @return the {@link ToolInstallation} in the central software repository matching the given {@code version}.
144
   */
145
  public ToolInstallation installInRepo(VersionIdentifier version, String edition, ToolRepository toolRepository) {
146

147
    VersionIdentifier resolvedVersion = toolRepository.resolveVersion(this.tool, edition, version);
7✔
148

149
    if (Files.exists(this.dependency.getDependencyJsonPath(getConfiguredEdition()))) {
9✔
150
      installDependencies(resolvedVersion);
4✔
151
    } else {
152
      this.context.trace("No Dependencies file found");
4✔
153
    }
154

155
    Path toolPath = this.context.getSoftwareRepositoryPath().resolve(toolRepository.getId()).resolve(this.tool).resolve(edition)
12✔
156
        .resolve(resolvedVersion.toString());
3✔
157
    Path toolVersionFile = toolPath.resolve(IdeContext.FILE_SOFTWARE_VERSION);
4✔
158
    FileAccess fileAccess = this.context.getFileAccess();
4✔
159
    if (Files.isDirectory(toolPath)) {
5✔
160
      if (Files.exists(toolVersionFile)) {
5!
161
        if (this.context.isForceMode()) {
4!
162
          fileAccess.delete(toolPath);
×
163
        } else {
164
          this.context.debug("Version {} of tool {} is already installed at {}", resolvedVersion, getToolWithEdition(this.tool, edition), toolPath);
21✔
165
          return createToolInstallation(toolPath, resolvedVersion, toolVersionFile);
6✔
166
        }
167
      } else {
168
        this.context.warning("Deleting corrupted installation at {}", toolPath);
×
169
        fileAccess.delete(toolPath);
×
170
      }
171
    }
172
    Path target = toolRepository.download(this.tool, edition, resolvedVersion);
7✔
173
    fileAccess.mkdirs(toolPath.getParent());
4✔
174
    boolean extract = isExtract();
3✔
175
    if (!extract) {
2✔
176
      this.context.trace("Extraction is disabled for '{}' hence just moving the downloaded file {}.", this.tool, target);
15✔
177
    }
178
    fileAccess.extract(target, toolPath, this::postExtract, extract);
7✔
179
    try {
180
      Files.writeString(toolVersionFile, resolvedVersion.toString(), StandardOpenOption.CREATE_NEW);
11✔
181
    } catch (IOException e) {
×
182
      throw new IllegalStateException("Failed to write version file " + toolVersionFile, e);
×
183
    }
1✔
184
    // newInstallation results in above conditions to be true if isForceMode is true or if the tool version file was
185
    // missing
186
    return createToolInstallation(toolPath, resolvedVersion, toolVersionFile, true);
7✔
187
  }
188

189
  /**
190
   * Post-extraction hook that can be overridden to add custom processing after unpacking and before moving to the final destination folder.
191
   *
192
   * @param extractedDir the {@link Path} to the folder with the unpacked tool.
193
   */
194
  protected void postExtract(Path extractedDir) {
195

196
  }
1✔
197

198
  @Override
199
  public VersionIdentifier getInstalledVersion() {
200

201
    return getInstalledVersion(this.context.getSoftwarePath().resolve(getName()));
9✔
202
  }
203

204
  /**
205
   * @param toolPath the installation {@link Path} where to find the version file.
206
   * @return the currently installed {@link VersionIdentifier version} of this tool or {@code null} if not installed.
207
   */
208
  protected VersionIdentifier getInstalledVersion(Path toolPath) {
209

210
    if (!Files.isDirectory(toolPath)) {
5✔
211
      this.context.debug("Tool {} not installed in {}", getName(), toolPath);
15✔
212
      return null;
2✔
213
    }
214
    Path toolVersionFile = toolPath.resolve(IdeContext.FILE_SOFTWARE_VERSION);
4✔
215
    if (!Files.exists(toolVersionFile)) {
5✔
216
      Path legacyToolVersionFile = toolPath.resolve(IdeContext.FILE_LEGACY_SOFTWARE_VERSION);
4✔
217
      if (Files.exists(legacyToolVersionFile)) {
5✔
218
        toolVersionFile = legacyToolVersionFile;
3✔
219
      } else {
220
        this.context.warning("Tool {} is missing version file in {}", getName(), toolVersionFile);
15✔
221
        return null;
2✔
222
      }
223
    }
224
    try {
225
      String version = Files.readString(toolVersionFile).trim();
4✔
226
      return VersionIdentifier.of(version);
3✔
227
    } catch (IOException e) {
×
228
      throw new IllegalStateException("Failed to read file " + toolVersionFile, e);
×
229
    }
230
  }
231

232
  @Override
233
  public String getInstalledEdition() {
234

235
    return getInstalledEdition(this.context.getSoftwarePath().resolve(getName()));
9✔
236
  }
237

238
  /**
239
   * @param toolPath the installation {@link Path} where to find currently installed tool. The name of the parent directory of the real path corresponding to
240
   * the passed {@link Path path} must be the name of the edition.
241
   * @return the installed edition of this tool or {@code null} if not installed.
242
   */
243
  public String getInstalledEdition(Path toolPath) {
244

245
    if (!Files.isDirectory(toolPath)) {
5!
246
      this.context.debug("Tool {} not installed in {}", getName(), toolPath);
×
247
      return null;
×
248
    }
249
    try {
250
      String edition = toolPath.toRealPath().getParent().getFileName().toString();
8✔
251
      if (!this.context.getUrls().getSortedEditions(getName()).contains(edition)) {
9!
252
        edition = getConfiguredEdition();
3✔
253
      }
254
      return edition;
2✔
255
    } catch (IOException e) {
×
256
      throw new IllegalStateException(
×
257
          "Couldn't determine the edition of " + getName() + " from the directory structure of its software path "
×
258
              + toolPath
259
              + ", assuming the name of the parent directory of the real path of the software path to be the edition "
260
              + "of the tool.", e);
261
    }
262

263
  }
264

265
  @Override
266
  public void uninstall() {
267

268
    try {
269
      Path softwarePath = getToolPath();
3✔
270
      if (Files.exists(softwarePath)) {
5✔
271
        try {
272
          this.context.getFileAccess().delete(softwarePath);
5✔
273
          this.context.success("Successfully uninstalled " + this.tool);
6✔
274
        } catch (Exception e) {
1✔
275
          this.context.error("Couldn't uninstall " + this.tool);
6✔
276
        }
2✔
277
      } else {
278
        this.context.warning("An installed version of " + this.tool + " does not exist");
6✔
279
      }
280
    } catch (Exception e) {
×
281
      this.context.error(e.getMessage());
×
282
    }
1✔
283
  }
1✔
284

285
  private ToolInstallation createToolInstallation(Path rootDir, VersionIdentifier resolvedVersion, Path toolVersionFile,
286
      boolean newInstallation) {
287

288
    Path linkDir = getMacOsHelper().findLinkDir(rootDir, getBinaryName());
7✔
289
    Path binDir = linkDir;
2✔
290
    Path binFolder = binDir.resolve(IdeContext.FOLDER_BIN);
4✔
291
    if (Files.isDirectory(binFolder)) {
5✔
292
      binDir = binFolder;
2✔
293
    }
294
    if (linkDir != rootDir) {
3✔
295
      assert (!linkDir.equals(rootDir));
5!
296
      this.context.getFileAccess().copy(toolVersionFile, linkDir, FileCopyMode.COPY_FILE_OVERRIDE);
7✔
297
    }
298
    return new ToolInstallation(rootDir, linkDir, binDir, resolvedVersion, newInstallation);
9✔
299
  }
300

301
  private ToolInstallation createToolInstallation(Path rootDir, VersionIdentifier resolvedVersion, Path toolVersionFile) {
302

303
    return createToolInstallation(rootDir, resolvedVersion, toolVersionFile, false);
7✔
304
  }
305

306
  @Override
307
  public void runTool(ProcessMode processMode, VersionIdentifier toolVersion, String... args) {
308

309
    Path binaryPath;
310
    Path toolPath = Path.of(getBinaryName());
6✔
311
    if (toolVersion == null) {
2!
312
      install(true);
4✔
313
      binaryPath = toolPath;
3✔
314
    } else {
315
      throw new UnsupportedOperationException("Not yet implemented!");
×
316
    }
317

318
    if (Files.exists(this.dependency.getDependencyJsonPath(getConfiguredEdition()))) {
9✔
319
      setDependencyRepository(getInstalledVersion());
5✔
320
    } else {
321
      this.context.trace("No Dependencies file found");
4✔
322
    }
323

324
    ProcessContext pc = this.context.newProcess().errorHandling(ProcessErrorHandling.WARNING).executable(binaryPath).addArgs(args);
10✔
325

326
    for (String key : this.dependenciesEnvVariablePaths.keySet()) {
12✔
327

328
      String dependencyPath = this.dependenciesEnvVariablePaths.get(key);
6✔
329
      pc = pc.withEnvVar(key, dependencyPath);
5✔
330
    }
1✔
331

332
    pc.run(processMode);
4✔
333
  }
1✔
334

335
  private void installDependencies(VersionIdentifier version) {
336

337
    List<DependencyInfo> dependencies = this.dependency.readJson(version, getConfiguredEdition());
7✔
338

339
    for (DependencyInfo dependencyInfo : dependencies) {
10✔
340

341
      String dependencyName = dependencyInfo.getTool();
3✔
342
      VersionIdentifier dependencyVersionToInstall = this.dependency.findDependencyVersionToInstall(dependencyInfo);
5✔
343
      if (dependencyVersionToInstall == null) {
2!
344
        continue;
×
345
      }
346

347
      ToolCommandlet dependencyTool = this.context.getCommandletManager().getToolCommandlet(dependencyName);
6✔
348
      Path dependencyRepository = getDependencySoftwareRepository(dependencyName,
5✔
349
          dependencyTool.getConfiguredEdition());
1✔
350

351
      if (!Files.exists(dependencyRepository)) {
5!
352
        installDependencyInRepo(dependencyName, dependencyTool, dependencyVersionToInstall);
×
353
      } else {
354
        Path versionExistingInRepository = this.dependency.versionExistsInRepository(dependencyRepository, dependencyInfo.getVersionRange());
7✔
355
        if (versionExistingInRepository.equals(Path.of(""))) {
7✔
356
          installDependencyInRepo(dependencyName, dependencyTool, dependencyVersionToInstall);
6✔
357
        } else {
358
          this.context.info("Necessary version of the dependency {} is already installed in repository", dependencyName);
10✔
359
        }
360
      }
361
    }
1✔
362
  }
1✔
363

364
  private void installDependencyInRepo(String dependencyName, ToolCommandlet dependencyTool, VersionIdentifier dependencyVersionToInstall) {
365

366
    this.context.info("The version {} of the dependency {} is being installed", dependencyVersionToInstall, dependencyName);
14✔
367
    LocalToolCommandlet dependencyLocal = (LocalToolCommandlet) dependencyTool;
3✔
368
    dependencyLocal.installInRepo(dependencyVersionToInstall);
4✔
369
    this.context.info("The version {} of the dependency {} was successfully installed", dependencyVersionToInstall, dependencyName);
14✔
370
  }
1✔
371

372
  protected void setDependencyRepository(VersionIdentifier version) {
373

374
    List<DependencyInfo> dependencies = this.dependency.readJson(version, getConfiguredEdition());
7✔
375

376
    for (DependencyInfo dependencyInfo : dependencies) {
10✔
377
      String dependencyName = dependencyInfo.getTool();
3✔
378
      VersionIdentifier dependencyVersionToInstall = this.dependency.findDependencyVersionToInstall(dependencyInfo);
5✔
379
      if (dependencyVersionToInstall == null) {
2!
380
        continue;
×
381
      }
382

383
      ToolCommandlet dependencyTool = this.context.getCommandletManager().getToolCommandlet(dependencyName);
6✔
384
      Path dependencyRepository = getDependencySoftwareRepository(dependencyName,
5✔
385
          dependencyTool.getConfiguredEdition());
1✔
386
      Path versionExistingInRepository = this.dependency.versionExistsInRepository(dependencyRepository,
6✔
387
          dependencyInfo.getVersionRange());
1✔
388
      Path dependencyPath;
389

390
      if (versionExistingInRepository.equals(Path.of(""))) {
7!
391
        dependencyPath = dependencyRepository.resolve(dependencyVersionToInstall.toString());
×
392
      } else {
393
        dependencyPath = dependencyRepository.resolve(versionExistingInRepository);
4✔
394
      }
395
      setDependencyEnvironmentPath(getDependencyEnvironmentName(dependencyName), dependencyPath);
6✔
396
    }
1✔
397
  }
1✔
398

399
  private void setDependencyEnvironmentPath(String dependencyEnvironmentName, Path dependencyPath) {
400

401
    this.dependenciesEnvVariablePaths.put(dependencyEnvironmentName, dependencyPath.toString());
7✔
402

403
  }
1✔
404

405
  /**
406
   * Method to return the list of the environment variable name for the dependencies. If necessary, it should be overridden in the specific tool
407
   *
408
   * @return the {@link HashMap} with the dependency name mapped to the env variable, for example ( java: JAVA_HOME )
409
   */
410

411
  protected HashMap<String, String> listOfDependencyEnvVariableNames() {
412

413
    return this.dependenciesEnvVariableNames;
×
414
  }
415

416
  private String getDependencyEnvironmentName(String dependencyName) {
417

418
    HashMap<String, String> envVariableName = listOfDependencyEnvVariableNames();
3✔
419

420
    if (envVariableName != null) {
2!
421
      return envVariableName.get(dependencyName);
5✔
422
    }
423

424
    return dependencyName.toUpperCase() + "_HOME";
×
425
  }
426

427
  private Path getDependencySoftwareRepository(String dependencyName, String dependencyEdition) {
428

429
    String defaultToolRepositoryId = this.context.getDefaultToolRepository().getId();
5✔
430
    Path dependencyRepository = this.context.getSoftwareRepositoryPath().resolve(defaultToolRepositoryId).resolve(dependencyName).resolve(dependencyEdition);
10✔
431

432
    return dependencyRepository;
2✔
433
  }
434

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