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

devonfw / IDEasy / 13440922988

20 Feb 2025 05:16PM UTC coverage: 67.945% (-0.01%) from 67.959%
13440922988

push

github

web-flow
#1053: fix setup #1044: setup rewritten in Java (#1055)

Co-authored-by: jan-vcapgemini <59438728+jan-vcapgemini@users.noreply.github.com>

2992 of 4849 branches covered (61.7%)

Branch coverage included in aggregate %.

7765 of 10983 relevant lines covered (70.7%)

3.08 hits per line

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

70.09
cli/src/main/java/com/devonfw/tools/ide/tool/IdeasyCommandlet.java
1
package com.devonfw.tools.ide.tool;
2

3
import java.nio.file.Files;
4
import java.nio.file.Path;
5
import java.util.ArrayList;
6
import java.util.Iterator;
7
import java.util.List;
8
import java.util.Set;
9

10
import com.devonfw.tools.ide.cli.CliException;
11
import com.devonfw.tools.ide.commandlet.UpgradeMode;
12
import com.devonfw.tools.ide.common.Tag;
13
import com.devonfw.tools.ide.context.IdeContext;
14
import com.devonfw.tools.ide.io.FileAccess;
15
import com.devonfw.tools.ide.os.WindowsHelper;
16
import com.devonfw.tools.ide.os.WindowsPathSyntax;
17
import com.devonfw.tools.ide.tool.mvn.MvnArtifact;
18
import com.devonfw.tools.ide.tool.mvn.MvnBasedLocalToolCommandlet;
19
import com.devonfw.tools.ide.tool.repository.MavenRepository;
20
import com.devonfw.tools.ide.variable.IdeVariables;
21
import com.devonfw.tools.ide.version.IdeVersion;
22
import com.devonfw.tools.ide.version.VersionIdentifier;
23

24
/**
25
 * {@link MvnBasedLocalToolCommandlet} for IDEasy (ide-cli).
26
 */
27
public class IdeasyCommandlet extends MvnBasedLocalToolCommandlet {
28

29
  /** The {@link MvnArtifact} for IDEasy. */
30
  public static final MvnArtifact ARTIFACT = MvnArtifact.ofIdeasyCli("*!", "tar.gz", "${os}-${arch}");
6✔
31

32
  private static final String BASH_CODE_SOURCE_FUNCTIONS = "source \"$IDE_ROOT/_ide/installation/functions\"";
33

34
  /** The {@link #getName() tool name}. */
35
  public static final String TOOL_NAME = "ideasy";
36

37
  private final UpgradeMode mode;
38

39
  /**
40
   * The constructor.
41
   *
42
   * @param context the {@link IdeContext}.
43
   */
44
  public IdeasyCommandlet(IdeContext context) {
45
    this(context, UpgradeMode.STABLE);
4✔
46
  }
1✔
47

48
  /**
49
   * The constructor.
50
   *
51
   * @param context the {@link IdeContext}.
52
   * @param mode the {@link UpgradeMode}.
53
   */
54
  public IdeasyCommandlet(IdeContext context, UpgradeMode mode) {
55

56
    super(context, TOOL_NAME, ARTIFACT, Set.of(Tag.PRODUCTIVITY, Tag.IDE));
8✔
57
    this.mode = mode;
3✔
58
  }
1✔
59

60
  @Override
61
  public VersionIdentifier getInstalledVersion() {
62

63
    return IdeVersion.getVersionIdentifier();
×
64
  }
65

66
  @Override
67
  public String getConfiguredEdition() {
68

69
    return this.tool;
×
70
  }
71

72
  @Override
73
  public VersionIdentifier getConfiguredVersion() {
74

75
    UpgradeMode upgradeMode = this.mode;
×
76
    if (upgradeMode == null) {
×
77
      if (IdeVersion.getVersionString().contains("SNAPSHOT")) {
×
78
        upgradeMode = UpgradeMode.SNAPSHOT;
×
79
      } else {
80
        if (IdeVersion.getVersionIdentifier().getDevelopmentPhase().isStable()) {
×
81
          upgradeMode = UpgradeMode.STABLE;
×
82
        } else {
83
          upgradeMode = UpgradeMode.UNSTABLE;
×
84
        }
85
      }
86
    }
87
    return upgradeMode.getVersion();
×
88
  }
89

90
  @Override
91
  public Path getToolPath() {
92

93
    return this.context.getIdeInstallationPath();
×
94
  }
95

96
  @Override
97
  public boolean install(boolean silent) {
98

99
    if (IdeVersion.isUndefined()) {
2!
100
      this.context.warning("You are using IDEasy version {} which indicates local development - skipping upgrade.", IdeVersion.getVersionString());
10✔
101
      return false;
2✔
102
    }
103
    return super.install(silent);
×
104
  }
105

106
  /**
107
   * @return the latest released {@link VersionIdentifier version} of IDEasy.
108
   */
109
  public VersionIdentifier getLatestVersion() {
110

111
    VersionIdentifier currentVersion = IdeVersion.getVersionIdentifier();
×
112
    if (IdeVersion.isUndefined()) {
×
113
      return currentVersion;
×
114
    }
115
    VersionIdentifier configuredVersion = getConfiguredVersion();
×
116
    return getToolRepository().resolveVersion(this.tool, getConfiguredEdition(), configuredVersion, this);
×
117
  }
118

119
  /**
120
   * Checks if an update is available and logs according information.
121
   *
122
   * @return {@code true} if an update is available, {@code false} otherwise.
123
   */
124
  public boolean checkIfUpdateIsAvailable() {
125
    VersionIdentifier installedVersion = getInstalledVersion();
×
126
    VersionIdentifier latestVersion = getLatestVersion();
×
127
    if (installedVersion.equals(latestVersion)) {
×
128
      this.context.success("Your version of IDEasy is {} which is the latest released version.", installedVersion);
×
129
      return false;
×
130
    } else {
131
      this.context.interaction("Your version of IDEasy is {} but version {} is available. Please run the following command to upgrade to the latest version:\n"
×
132
          + "ide upgrade", installedVersion, latestVersion);
133
      return true;
×
134
    }
135
  }
136

137
  /**
138
   * Initial installation of IDEasy.
139
   *
140
   * @param cwd the {@link Path} to the current working directory.
141
   * @see com.devonfw.tools.ide.commandlet.InstallCommandlet
142
   */
143
  public void installIdeasy(Path cwd) {
144
    Path ideRoot = determineIdeRoot(cwd);
4✔
145
    Path idePath = ideRoot.resolve(IdeContext.FOLDER_UNDERSCORE_IDE);
4✔
146
    Path installationPath = idePath.resolve(IdeContext.FOLDER_INSTALLATION);
4✔
147
    Path ideasySoftwarePath = idePath.resolve(IdeContext.FOLDER_SOFTWARE).resolve(MavenRepository.ID).resolve(IdeasyCommandlet.TOOL_NAME)
8✔
148
        .resolve(IdeasyCommandlet.TOOL_NAME);
2✔
149
    Path ideasyVersionPath = ideasySoftwarePath.resolve(IdeVersion.getVersionString());
4✔
150
    if (Files.isDirectory(ideasyVersionPath)) {
5!
151
      throw new CliException("IDEasy is already installed at " + ideasyVersionPath + " - if your installation is broken, delete it manually and rerun setup!");
×
152
    }
153
    FileAccess fileAccess = this.context.getFileAccess();
4✔
154
    List<Path> installationArtifacts = new ArrayList<>();
4✔
155
    boolean success = true;
2✔
156
    success &= addInstallationArtifact(cwd, "bin", true, installationArtifacts);
9✔
157
    success &= addInstallationArtifact(cwd, "functions", true, installationArtifacts);
9✔
158
    success &= addInstallationArtifact(cwd, "internal", true, installationArtifacts);
9✔
159
    success &= addInstallationArtifact(cwd, "system", true, installationArtifacts);
9✔
160
    success &= addInstallationArtifact(cwd, "IDEasy.pdf", true, installationArtifacts);
9✔
161
    success &= addInstallationArtifact(cwd, "setup", true, installationArtifacts);
9✔
162
    success &= addInstallationArtifact(cwd, "setup.bat", false, installationArtifacts);
9✔
163
    if (!success) {
2!
164
      throw new CliException("IDEasy release is inconsistent at " + cwd);
×
165
    }
166
    fileAccess.mkdirs(ideasyVersionPath);
3✔
167
    for (Path installationArtifact : installationArtifacts) {
10✔
168
      fileAccess.copy(installationArtifact, ideasyVersionPath);
4✔
169
    }
1✔
170
    fileAccess.symlink(ideasyVersionPath, installationPath);
4✔
171
    addToShellRc(".bashrc", ideRoot, null);
5✔
172
    addToShellRc(".zshrc", ideRoot, "autoload -U +X bashcompinit && bashcompinit");
5✔
173
    installIdeasyWindowsEnv(ideRoot, installationPath);
4✔
174
    this.context.success("IDEasy has been installed successfully on your system.");
4✔
175
    this.context.warning("IDEasy has been setup for new shells but it cannot work in your current shell(s).\n"
4✔
176
        + "Reboot or open a new terminal to make it work.");
177
  }
1✔
178

179
  private void installIdeasyWindowsEnv(Path ideRoot, Path installationPath) {
180
    if (this.context.getSystemInfo().isWindows()) {
5!
181
      WindowsHelper helper = WindowsHelper.get(this.context);
4✔
182
      helper.setUserEnvironmentValue(IdeVariables.IDE_ROOT.getName(), ideRoot.toString());
6✔
183
      String userPath = helper.getUserEnvironmentValue(IdeVariables.PATH.getName());
5✔
184
      if (userPath == null) {
2!
185
        this.context.error("Could not read user PATH from registry!");
×
186
      } else {
187
        this.context.info("Found user PATH={}", userPath);
10✔
188
        Path ideasyBinPath = installationPath.resolve("bin");
4✔
189
        userPath = removeObsoleteEntryFromWindowsPath(userPath);
3✔
190
        if (userPath.isEmpty()) {
3!
191
          this.context.warning("ATTENTION:\n"
×
192
              + "Your user specific PATH variable seems to be empty.\n"
193
              + "You can double check this by pressing [Windows][r] and launch the program SystemPropertiesAdvanced.\n"
194
              + "Then click on 'Environment variables' and check if 'PATH' is set in the 'user variables' from the upper list.\n"
195
              + "In case 'PATH' is defined there non-empty and you get this message, please abort and give us feedback:\n"
196
              + "https://github.com/devonfw/IDEasy/issues\n"
197
              + "Otherwise all is correct and you can continue.");
198
          this.context.askToContinue("Are you sure you want to override your PATH?");
×
199
          userPath = ideasyBinPath.toString();
×
200
        } else {
201
          userPath = userPath + ";" + ideasyBinPath;
5✔
202
        }
203
        helper.setUserEnvironmentValue(IdeVariables.PATH.getName(), userPath);
5✔
204
      }
205
    }
206
  }
1✔
207

208
  static String removeObsoleteEntryFromWindowsPath(String userPath) {
209
    int len = userPath.length();
3✔
210
    int start = 0;
2✔
211
    while ((start >= 0) && (start < len)) {
5!
212
      int end = userPath.indexOf(';', start);
5✔
213
      if (end < 0) {
2✔
214
        end = len;
2✔
215
      }
216
      String entry = userPath.substring(start, end);
5✔
217
      if (entry.endsWith("\\_ide\\bin")) {
4✔
218
        String prefix = "";
2✔
219
        int offset = 1;
2✔
220
        if (start > 0) {
2✔
221
          prefix = userPath.substring(0, start - 1);
7✔
222
          offset = 0;
2✔
223
        }
224
        if (end == len) {
3✔
225
          return prefix;
2✔
226
        } else {
227
          return prefix + userPath.substring(end + offset);
8✔
228
        }
229
      }
230
      start = end + 1;
4✔
231
    }
1✔
232
    return userPath;
2✔
233
  }
234

235
  /**
236
   * Adds ourselves to the shell RC (run-commands) configuration file.
237
   *
238
   * @param filename the name of the RC file.
239
   * @param ideRoot the IDE_ROOT {@link Path}.
240
   */
241
  private void addToShellRc(String filename, Path ideRoot, String extraLine) {
242

243
    this.context.info("Configuring IDEasy in {}", filename);
10✔
244
    Path rcFile = this.context.getUserHome().resolve(filename);
6✔
245
    FileAccess fileAccess = this.context.getFileAccess();
4✔
246
    List<String> lines = fileAccess.readFileLines(rcFile);
4✔
247
    if (lines == null) {
2✔
248
      lines = new ArrayList<>();
5✔
249
    } else {
250
      // since it is unspecified if the returned List may be immutable we want to get sure
251
      lines = new ArrayList<>(lines);
5✔
252
    }
253
    Iterator<String> iterator = lines.iterator();
3✔
254
    int removeCount = 0;
2✔
255
    while (iterator.hasNext()) {
3✔
256
      String line = iterator.next();
4✔
257
      line = line.trim();
3✔
258
      if (isObsoleteRcLine(line)) {
3✔
259
        this.context.info("Removing obsolete line from {}: {}", filename, line);
14✔
260
        iterator.remove();
2✔
261
        removeCount++;
2✔
262
      } else if (line.equals(extraLine)) {
4✔
263
        extraLine = null;
2✔
264
      }
265
    }
1✔
266
    if (extraLine != null) {
2!
267
      lines.add(extraLine);
×
268
    }
269
    if (this.context.getSystemInfo().isWindows()) {
5!
270
      lines.add("export IDE_ROOT=\"" + WindowsPathSyntax.MSYS.format(ideRoot) + "\"");
7✔
271
    }
272
    lines.add(BASH_CODE_SOURCE_FUNCTIONS);
4✔
273
    fileAccess.writeFileLines(lines, rcFile);
4✔
274
  }
1✔
275

276
  private static boolean isObsoleteRcLine(String line) {
277
    if (line.startsWith("alias ide=")) {
4!
278
      return true;
×
279
    } else if (line.startsWith("export IDE_ROOT=")) {
4!
280
      return true;
×
281
    } else if (line.equals("ide")) {
4✔
282
      return true;
2✔
283
    } else if (line.equals("ide init")) {
4✔
284
      return true;
2✔
285
    } else if (line.startsWith("source \"$IDE_ROOT/_ide/")) {
4✔
286
      return true;
2✔
287
    }
288
    return false;
2✔
289
  }
290

291
  private boolean addInstallationArtifact(Path cwd, String artifactName, boolean required, List<Path> installationArtifacts) {
292

293
    Path artifactPath = cwd.resolve(artifactName);
4✔
294
    if (Files.exists(artifactPath)) {
5!
295
      installationArtifacts.add(artifactPath);
5✔
296
    } else if (required) {
×
297
      this.context.error("Missing required file {}", artifactName);
×
298
      return false;
×
299
    }
300
    return true;
2✔
301
  }
302

303
  private Path determineIdeRoot(Path cwd) {
304
    Path ideRoot = this.context.getIdeRoot();
4✔
305
    if (ideRoot == null) {
2!
306
      Path home = this.context.getUserHome();
4✔
307
      Path installRoot = home;
2✔
308
      if (this.context.getSystemInfo().isWindows()) {
5!
309
        if (!cwd.startsWith(home)) {
4!
310
          installRoot = cwd.getRoot();
×
311
        }
312
      }
313
      ideRoot = installRoot.resolve(IdeContext.FOLDER_PROJECTS);
4✔
314
    } else {
1✔
315
      assert (Files.isDirectory(ideRoot)) : "IDE_ROOT directory does not exist!";
×
316
    }
317
    return ideRoot;
2✔
318
  }
319

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