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

devonfw / IDEasy / 9903789173

12 Jul 2024 06:58AM UTC coverage: 61.842% (+0.03%) from 61.812%
9903789173

push

github

web-flow
#428: Improve get version and edition (#455)

2008 of 3567 branches covered (56.29%)

Branch coverage included in aggregate %.

5324 of 8289 relevant lines covered (64.23%)

2.82 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 com.devonfw.tools.ide.common.Tag;
4
import com.devonfw.tools.ide.context.IdeContext;
5
import com.devonfw.tools.ide.io.FileAccess;
6
import com.devonfw.tools.ide.io.FileCopyMode;
7
import com.devonfw.tools.ide.log.IdeLogLevel;
8
import com.devonfw.tools.ide.process.ProcessContext;
9
import com.devonfw.tools.ide.process.ProcessErrorHandling;
10
import com.devonfw.tools.ide.process.ProcessMode;
11
import com.devonfw.tools.ide.repo.ToolRepository;
12
import com.devonfw.tools.ide.step.Step;
13
import com.devonfw.tools.ide.url.model.file.dependencyJson.DependencyInfo;
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.nio.file.StandardOpenOption;
20
import java.util.HashMap;
21
import java.util.List;
22
import java.util.Set;
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
125
   * {@link com.devonfw.tools.ide.commandlet.Commandlet} only in the central software repository without touching the
126
   * IDE installation.
127
   *
128
   * @param version the {@link VersionIdentifier} requested to be installed. May also be a
129
   *     {@link VersionIdentifier#isPattern() version pattern}.
130
   * @param edition the specific edition to install.
131
   * @return the {@link ToolInstallation} in the central software repository matching the given {@code version}.
132
   */
133
  public ToolInstallation installInRepo(VersionIdentifier version, String edition) {
134

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

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

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

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

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

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

198
  }
1✔
199

200
  @Override
201
  public VersionIdentifier getInstalledVersion() {
202

203
    return getInstalledVersion(this.context.getSoftwarePath().resolve(getName()));
9✔
204
  }
205

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

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

234
  @Override
235
  public String getInstalledEdition() {
236

237
    return getInstalledEdition(this.context.getSoftwarePath().resolve(getName()));
9✔
238
  }
239

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

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

265
  }
266

267
  public void uninstall() {
268

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

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

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

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

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

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

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

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

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

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

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

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

336
  private void installDependencies(VersionIdentifier version) {
337

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

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

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

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

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

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

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

373
  protected void setDependencyRepository(VersionIdentifier version) {
374

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

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

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

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

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

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

404
  }
1✔
405

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

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

414
    return dependenciesEnvVariableNames;
×
415
  }
416

417
  private String getDependencyEnvironmentName(String dependencyName) {
418

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

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

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

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

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

433
    return dependencyRepository;
2✔
434
  }
435

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