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

devonfw / IDEasy / 9698821877

27 Jun 2024 02:51PM UTC coverage: 60.328% (-0.01%) from 60.34%
9698821877

push

github

web-flow
#410: Refactor get version and edition (#411)

1898 of 3456 branches covered (54.92%)

Branch coverage included in aggregate %.

4989 of 7960 relevant lines covered (62.68%)

2.74 hits per line

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

77.33
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.repo.ToolRepository;
9
import com.devonfw.tools.ide.step.Step;
10
import com.devonfw.tools.ide.version.VersionIdentifier;
11

12
import java.io.IOException;
13
import java.nio.file.Files;
14
import java.nio.file.Path;
15
import java.nio.file.StandardOpenOption;
16
import java.util.Set;
17

18
/**
19
 * {@link ToolCommandlet} that is installed locally into the IDE.
20
 */
21
public abstract class LocalToolCommandlet extends ToolCommandlet {
1✔
22

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

32
    super(context, tool, tags);
5✔
33
  }
1✔
34

35
  /**
36
   * @return the {@link Path} where the tool is located (installed).
37
   */
38
  public Path getToolPath() {
39

40
    return this.context.getSoftwarePath().resolve(getName());
7✔
41
  }
42

43
  /**
44
   * @return the {@link Path} where the executables of the tool can be found. Typically a "bin" folder inside {@link #getToolPath() tool path}.
45
   */
46
  public Path getToolBinPath() {
47

48
    Path toolPath = getToolPath();
×
49
    Path binPath = this.context.getFileAccess().findFirst(toolPath, path -> path.getFileName().toString().equals("bin"), false);
×
50
    if ((binPath != null) && Files.isDirectory(binPath)) {
×
51
      return binPath;
×
52
    }
53
    return toolPath;
×
54
  }
55

56
  @Override
57
  protected boolean doInstall(boolean silent) {
58

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

97
  }
98

99
  /**
100
   * Performs the installation of the {@link #getName() tool} managed by this {@link com.devonfw.tools.ide.commandlet.Commandlet} only in the central software
101
   * repository without touching the IDE installation.
102
   *
103
   * @param version the {@link VersionIdentifier} requested to be installed. May also be a {@link VersionIdentifier#isPattern() version pattern}.
104
   * @return the {@link ToolInstallation} in the central software repository matching the given {@code version}.
105
   */
106
  public ToolInstallation installInRepo(VersionIdentifier version) {
107

108
    return installInRepo(version, getEdition());
6✔
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
   * @param edition the specific edition to install.
117
   * @return the {@link ToolInstallation} in the central software repository matching the given {@code version}.
118
   */
119
  public ToolInstallation installInRepo(VersionIdentifier version, String edition) {
120

121
    return installInRepo(version, edition, this.context.getDefaultToolRepository());
8✔
122
  }
123

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

135
    VersionIdentifier resolvedVersion = toolRepository.resolveVersion(this.tool, edition, version);
7✔
136
    Path toolPath = this.context.getSoftwareRepositoryPath().resolve(toolRepository.getId()).resolve(this.tool).resolve(edition)
12✔
137
            .resolve(resolvedVersion.toString());
3✔
138
    Path toolVersionFile = toolPath.resolve(IdeContext.FILE_SOFTWARE_VERSION);
4✔
139
    FileAccess fileAccess = this.context.getFileAccess();
4✔
140
    if (Files.isDirectory(toolPath)) {
5✔
141
      if (Files.exists(toolVersionFile)) {
5!
142
        if (this.context.isForceMode()) {
4!
143
          fileAccess.delete(toolPath);
×
144
        } else {
145
          this.context.debug("Version {} of tool {} is already installed at {}", resolvedVersion, getToolWithEdition(this.tool, edition), toolPath);
21✔
146
          return createToolInstallation(toolPath, resolvedVersion, toolVersionFile);
6✔
147
        }
148
      } else {
149
        this.context.warning("Deleting corrupted installation at {}", toolPath);
×
150
        fileAccess.delete(toolPath);
×
151
      }
152
    }
153
    Path target = toolRepository.download(this.tool, edition, resolvedVersion);
7✔
154
    fileAccess.mkdirs(toolPath.getParent());
4✔
155
    boolean extract = isExtract();
3✔
156
    if (!extract) {
2✔
157
      this.context.trace("Extraction is disabled for '{}' hence just moving the downloaded file {}.", this.tool, target);
15✔
158
    }
159
    fileAccess.extract(target, toolPath, this::postExtract, extract);
7✔
160
    try {
161
      Files.writeString(toolVersionFile, resolvedVersion.toString(), StandardOpenOption.CREATE_NEW);
11✔
162
    } catch (IOException e) {
×
163
      throw new IllegalStateException("Failed to write version file " + toolVersionFile, e);
×
164
    }
1✔
165
    // newInstallation results in above conditions to be true if isForceMode is true or if the tool version file was
166
    // missing
167
    return createToolInstallation(toolPath, resolvedVersion, toolVersionFile, true);
7✔
168
  }
169

170
  /**
171
   * Post-extraction hook that can be overridden to add custom processing after unpacking and before moving to the final destination folder.
172
   *
173
   * @param extractedDir the {@link Path} to the folder with the unpacked tool.
174
   */
175
  protected void postExtract(Path extractedDir) {
176

177
  }
1✔
178

179
  @Override
180
  public VersionIdentifier getInstalledVersion() {
181

182
    return getInstalledVersion(this.context.getSoftwarePath().resolve(getName()));
9✔
183
  }
184

185
  /**
186
   * @param toolPath the installation {@link Path} where to find the version file.
187
   * @return the currently installed {@link VersionIdentifier version} of this tool or {@code null} if not installed.
188
   */
189
  protected VersionIdentifier getInstalledVersion(Path toolPath) {
190

191
    if (!Files.isDirectory(toolPath)) {
5✔
192
      this.context.debug("Tool {} not installed in {}", getName(), toolPath);
15✔
193
      return null;
2✔
194
    }
195
    Path toolVersionFile = toolPath.resolve(IdeContext.FILE_SOFTWARE_VERSION);
4✔
196
    if (!Files.exists(toolVersionFile)) {
5✔
197
      Path legacyToolVersionFile = toolPath.resolve(IdeContext.FILE_LEGACY_SOFTWARE_VERSION);
4✔
198
      if (Files.exists(legacyToolVersionFile)) {
5✔
199
        toolVersionFile = legacyToolVersionFile;
3✔
200
      } else {
201
        this.context.warning("Tool {} is missing version file in {}", getName(), toolVersionFile);
15✔
202
        return null;
2✔
203
      }
204
    }
205
    try {
206
      String version = Files.readString(toolVersionFile).trim();
4✔
207
      return VersionIdentifier.of(version);
3✔
208
    } catch (IOException e) {
×
209
      throw new IllegalStateException("Failed to read file " + toolVersionFile, e);
×
210
    }
211
  }
212

213
  @Override
214
  public String getInstalledEdition() {
215

216
    return getInstalledEdition(this.context.getSoftwarePath().resolve(getName()));
9✔
217
  }
218

219
  /**
220
   * @param toolPath the installation {@link Path} where to find currently installed tool. The name of the parent
221
   *     directory of the real path corresponding to the passed {@link Path path} must be the name of the edition.
222
   * @return the installed edition of this tool or {@code null} if not installed.
223
   */
224
  public String getInstalledEdition(Path toolPath) {
225

226
    if (!Files.isDirectory(toolPath)) {
5!
227
      this.context.debug("Tool {} not installed in {}", getName(), toolPath);
×
228
      return null;
×
229
    }
230
    try {
231
      String edition = toolPath.toRealPath().getParent().getFileName().toString();
8✔
232
      if (!this.context.getUrls().getSortedEditions(getName()).contains(edition)) {
9!
233
        edition = getEdition();
3✔
234
      }
235
      return edition;
2✔
236
    } catch (IOException e) {
×
237
      throw new IllegalStateException(
×
238
          "Couldn't determine the edition of " + getName() + " from the directory structure of its software path "
×
239
              + toolPath
240
              + ", assuming the name of the parent directory of the real path of the software path to be the edition "
241
              + "of the tool.", e);
242
    }
243

244
  }
245

246
  public void uninstall() {
247

248
    try {
249
      Path softwarePath = getToolPath();
3✔
250
      if (Files.exists(softwarePath)) {
5✔
251
        try {
252
          context.getFileAccess().delete(softwarePath);
5✔
253
          this.context.success("Successfully uninstalled " + this.tool);
6✔
254
        } catch (Exception e) {
1✔
255
          this.context.error("Couldn't uninstall " + this.tool);
6✔
256
        }
2✔
257
      } else {
258
        this.context.warning("An installed version of " + this.tool + " does not exist");
6✔
259
      }
260
    } catch (Exception e) {
×
261
      this.context.error(e.getMessage());
×
262
    }
1✔
263
  }
1✔
264

265
  private ToolInstallation createToolInstallation(Path rootDir, VersionIdentifier resolvedVersion, Path toolVersionFile,
266
                                                  boolean newInstallation) {
267

268
    Path linkDir = getMacOsHelper().findLinkDir(rootDir, this.tool);
7✔
269
    Path binDir = linkDir;
2✔
270
    Path binFolder = binDir.resolve(IdeContext.FOLDER_BIN);
4✔
271
    if (Files.isDirectory(binFolder)) {
5✔
272
      binDir = binFolder;
2✔
273
    }
274
    if (linkDir != rootDir) {
3✔
275
      assert (!linkDir.equals(rootDir));
5!
276
      this.context.getFileAccess().copy(toolVersionFile, linkDir, FileCopyMode.COPY_FILE_OVERRIDE);
7✔
277
    }
278
    return new ToolInstallation(rootDir, linkDir, binDir, resolvedVersion, newInstallation);
9✔
279
  }
280

281
  private ToolInstallation createToolInstallation(Path rootDir, VersionIdentifier resolvedVersion, Path toolVersionFile) {
282

283
    return createToolInstallation(rootDir, resolvedVersion, toolVersionFile, false);
7✔
284
  }
285

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