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

devonfw / IDEasy / 25167683820

30 Apr 2026 01:19PM UTC coverage: 70.487% (-0.2%) from 70.671%
25167683820

Pull #1878

github

web-flow
Merge 154ff6c85 into 12d5f877f
Pull Request #1878: #1695: Clone settings to temporary directory, analyse, and then move

4366 of 6846 branches covered (63.77%)

Branch coverage included in aggregate %.

11275 of 15344 relevant lines covered (73.48%)

3.11 hits per line

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

77.17
cli/src/main/java/com/devonfw/tools/ide/commandlet/AbstractUpdateCommandlet.java
1
package com.devonfw.tools.ide.commandlet;
2

3
import java.io.IOException;
4
import java.nio.file.Files;
5
import java.nio.file.Path;
6
import java.util.HashSet;
7
import java.util.Iterator;
8
import java.util.List;
9
import java.util.Set;
10
import java.util.stream.Stream;
11

12
import org.slf4j.Logger;
13
import org.slf4j.LoggerFactory;
14

15
import com.devonfw.tools.ide.cli.CliException;
16
import com.devonfw.tools.ide.context.AbstractIdeContext;
17
import com.devonfw.tools.ide.context.IdeContext;
18
import com.devonfw.tools.ide.context.IdeStartContextImpl;
19
import com.devonfw.tools.ide.git.GitContext;
20
import com.devonfw.tools.ide.git.GitUrl;
21
import com.devonfw.tools.ide.git.repository.RepositoryCommandlet;
22
import com.devonfw.tools.ide.io.FileAccess;
23
import com.devonfw.tools.ide.io.FileCopyMode;
24
import com.devonfw.tools.ide.property.FlagProperty;
25
import com.devonfw.tools.ide.property.StringProperty;
26
import com.devonfw.tools.ide.step.Step;
27
import com.devonfw.tools.ide.tool.LocalToolCommandlet;
28
import com.devonfw.tools.ide.tool.ToolCommandlet;
29
import com.devonfw.tools.ide.tool.ToolEdition;
30
import com.devonfw.tools.ide.tool.ToolEditionAndVersion;
31
import com.devonfw.tools.ide.tool.ToolInstallRequest;
32
import com.devonfw.tools.ide.tool.custom.CustomToolCommandlet;
33
import com.devonfw.tools.ide.tool.custom.CustomToolMetadata;
34
import com.devonfw.tools.ide.tool.extra.ExtraToolInstallation;
35
import com.devonfw.tools.ide.tool.extra.ExtraTools;
36
import com.devonfw.tools.ide.tool.extra.ExtraToolsMapper;
37
import com.devonfw.tools.ide.variable.IdeVariables;
38
import com.devonfw.tools.ide.version.VersionIdentifier;
39
import com.devonfw.tools.ide.environment.EnvironmentVariables;
40

41
/**
42
 * Abstract {@link Commandlet} base-class for both {@link UpdateCommandlet} and {@link CreateCommandlet}.
43
 */
44
public abstract class AbstractUpdateCommandlet extends Commandlet {
45

46
  private static final Logger LOG = LoggerFactory.getLogger(AbstractUpdateCommandlet.class);
4✔
47

48
  private static final String MESSAGE_CODE_REPO_URL = """
49
      No code repository was given after '--code'.
50
      Further details can be found here: https://github.com/devonfw/IDEasy/blob/main/documentation/settings.adoc
51
      Please enter the code repository below that includes your settings folder.""";
52

53
  private static final String MESSAGE_SETTINGS_REPO_URL = """
54
      No settings found at {} and no SETTINGS_URL is defined.
55
      Further details can be found here: https://github.com/devonfw/IDEasy/blob/main/documentation/settings.adoc
56
      Please contact the technical lead of your project to get the SETTINGS_URL for your project to enter.
57
      In case you just want to test IDEasy you may simply hit return to install the default settings.""";
58

59
  /** {@link StringProperty} for the settings repository URL. */
60
  public final StringProperty settingsRepo;
61

62
  /** {@link FlagProperty} for skipping installation/updating of tools. */
63
  public final FlagProperty skipTools;
64

65
  /** {@link FlagProperty} for skipping the setup of git repositories. */
66
  public final FlagProperty skipRepositories;
67

68
  /** {@link FlagProperty} to force the update of the settings git repository. */
69
  public final FlagProperty forcePull;
70

71
  /** {@link FlagProperty} to force the installation/update of plugins. */
72
  public final FlagProperty forcePlugins;
73

74
  /** {@link FlagProperty} to force the setup of git repositories. */
75
  public final FlagProperty forceRepositories;
76

77
  /**
78
   * The constructor.
79
   *
80
   * @param context the {@link IdeContext}.
81
   */
82
  public AbstractUpdateCommandlet(IdeContext context) {
83

84
    super(context);
3✔
85
    addKeyword(getName());
4✔
86
    this.skipTools = add(new FlagProperty("--skip-tools"));
9✔
87
    this.skipRepositories = add(new FlagProperty("--skip-repositories"));
9✔
88
    this.forcePull = add(new FlagProperty("--force-pull"));
9✔
89
    this.forcePlugins = add(new FlagProperty("--force-plugins"));
9✔
90
    this.forceRepositories = add(new FlagProperty("--force-repositories"));
9✔
91
    this.settingsRepo = new StringProperty("", false, "settingsRepository");
8✔
92
  }
1✔
93

94
  @Override
95
  protected void doRun() {
96

97
    IdeStartContextImpl startContext = ((AbstractIdeContext) this.context).getStartContext();
5✔
98
    startContext.setForcePull(forcePull.isTrue());
5✔
99
    startContext.setForcePlugins(forcePlugins.isTrue());
5✔
100
    startContext.setForceRepositories(forceRepositories.isTrue());
5✔
101

102
    if (!this.context.isSettingsRepositorySymlinkOrJunction() || this.context.isForceMode() || forcePull.isTrue()) {
4!
103
      updateSettings();
2✔
104
      // Check if instance of create commandlet. Only then will we analyze the project
105
      if (this instanceof CreateCommandlet) {
3✔
106
        analyzeProject();
2✔
107
      }
108
    }
109
    updateConf();
2✔
110
    reloadContext();
2✔
111

112
    updateSoftware();
2✔
113
    updateRepositories();
2✔
114
    createStartScripts();
2✔
115
  }
1✔
116

117
  /**
118
   * This method is invoked when a new porject is created. It analyzes the cloned repository to check if it is a valid IDEasy repository. The repository can either be a settings repository (with ide.properties or devon.properties on the top level)
119
   * or a code repository (with a settings folder on the top level containing such a file). Otherwise, the project creatio fails and an error message is logged.
120
   */
121
  private void analyzeProject() {
122
    // Settings repository: ide.properties on top levels (or devon.properties for legacy users)
123
    // Code repository: settings folder on top level with ide.properties inside (or devon.properties for legacy users)
124
    String projectName = this.context.getProjectName();
4✔
125
    Path actualProjectPath;
126
    FileAccess fileAccess = this.context.getFileAccess();
4✔
127
    Path settingsPath = this.context.getSettingsPath();
4✔
128

129
    // Check whether the repository is a valid settings repository, code repository, or neither
130
    if (isSettingsRepository(settingsPath)) {
4✔
131
      LOG.info("The repository seems to be a settings repository based on the presence of " + EnvironmentVariables.DEFAULT_PROPERTIES + " or " + EnvironmentVariables.LEGACY_PROPERTIES + " on the top level.");
3✔
132
      actualProjectPath = this.context.getIdeRoot();
4✔
133
      moveProject(this.context.getIdeHome(), actualProjectPath);
7✔
134

135
    } else if (isCodeRepository(settingsPath)) {
4!
136
      LOG.info(EnvironmentVariables.DEFAULT_PROPERTIES + " or " + EnvironmentVariables.LEGACY_PROPERTIES + " found in settings subfolder. This indicates a code repository with a settings folder on the top level.");
×
137
      // Move settings folder contents containing code into workspace/main/<project_name>
138
      actualProjectPath = this.context.getIdeRoot().resolve(projectName).resolve("workspaces/main/").resolve(projectName);
×
139
      for (Path child : fileAccess.listChildren(settingsPath, f -> true)) {
×
140
        System.out.println("Child: " + child);
×
141
        moveProject(child, actualProjectPath);
×
142
      }
×
143
      // Move remaining folders into IDE_ROOT/<project_name>
144
      actualProjectPath = this.context.getIdeRoot();
×
145
      moveProject(this.context.getIdeHome(), actualProjectPath);
×
146
      // Delete empty settings folder in IDE_ROOT/<project_name> so we can create a symlink in the next step
147
      fileAccess.delete(actualProjectPath.resolve(projectName).resolve("settings"));
×
148
      // Link settings folder in IDE_HOME to settings folder in code repository
149
      fileAccess.symlink(actualProjectPath.resolve(projectName).resolve("workspaces/main").resolve(projectName).resolve("settings"), actualProjectPath.resolve(projectName).resolve("settings"));
×
150
      // Final cleanup in temp location
151
      fileAccess.delete(this.context.getIdeHome());
×
152

153
    } else {
154
      // Repository seems to be invalid. Clean up temporary location and return error
155
      fileAccess.delete(this.context.getIdeHome());
5✔
156
      throw new CliException("This repository does not include an " + EnvironmentVariables.DEFAULT_PROPERTIES + " or " + EnvironmentVariables.LEGACY_PROPERTIES + " file at the top level or a settings folder with such a file. "
5✔
157
      + "The repository does not seem to be a valid IDEasy repository. Please verify the repository and try again.");
158
    }
159
    // Set IDE_HOME to new (and actual) project location
160
    this.context.setIdeHome(this.context.getIdeRoot().resolve(projectName));
8✔
161
  }
1✔
162

163
  /**
164
   * Moves files of a new projectfrom the temporary location to the final project location.
165
   * @param oldPath - The path of the file or directory to be moved.
166
   * @param newPath - The path of the destination.
167
   */
168
  private void moveProject(Path oldPath, Path newPath) {
169
    FileAccess fileAccess = this.context.getFileAccess();
4✔
170
    try {
171
      fileAccess.copy(oldPath, newPath, FileCopyMode.COPY_TREE_OVERRIDE_FILES);
5✔
172
      fileAccess.delete(oldPath);
3✔
173
    } catch (Exception e) {
×
174
      LOG.error("Failed to move project from {} to {}. Please move it manually.", oldPath, newPath, e);
×
175
    }
1✔
176
  }
1✔
177

178
  /**
179
   * Checks whether te given repository is a settings repository by checking for the presence of ide.properties or devon.properties on the top level.
180
   * @param repositoryPath - The path of the repository to be checked.
181
   */
182
  private boolean isSettingsRepository(Path repositoryPath) {
183
    return Files.exists(repositoryPath.resolve(EnvironmentVariables.DEFAULT_PROPERTIES)) || Files.exists(repositoryPath.resolve(EnvironmentVariables.LEGACY_PROPERTIES));
18!
184
  }
185

186
  /**
187
   * Checks whether te given repository is a code repository by checking for the presence of ide.properties or devon.properties within a settings folder on the top level.
188
   * @param repositoryPath - The path of the repository to be checked.
189
   */
190
  private boolean isCodeRepository(Path repositoryPath) {
191
    return Files.exists(repositoryPath.resolve("settings").resolve(EnvironmentVariables.DEFAULT_PROPERTIES)) || Files.exists(repositoryPath.resolve("settings").resolve(EnvironmentVariables.LEGACY_PROPERTIES));
20!
192
  }
193

194

195

196
  private void reloadContext() {
197

198
    ((AbstractIdeContext) this.context).reload();
4✔
199
  }
1✔
200

201
  private void updateConf() {
202

203
    Path templatesFolder = this.context.getSettingsPath().resolve(IdeContext.FOLDER_TEMPLATES);
6✔
204
    if (!Files.exists(templatesFolder)) {
5✔
205
      Path legacyTemplatesFolder = this.context.getSettingsPath().resolve(IdeContext.FOLDER_LEGACY_TEMPLATES);
6✔
206
      if (Files.exists(legacyTemplatesFolder)) {
5!
207
        templatesFolder = legacyTemplatesFolder;
×
208
      } else {
209
        LOG.warn("Templates folder is missing in settings repository.");
3✔
210
        return;
1✔
211
      }
212
    }
213

214
    Step step = this.context.newStep("Copy configuration templates", templatesFolder);
11✔
215
    final Path finalTemplatesFolder = templatesFolder;
2✔
216
    step.run(() -> setupConf(finalTemplatesFolder, this.context.getIdeHome()));
13✔
217
  }
1✔
218

219
  private void setupConf(Path template, Path conf) {
220

221
    List<Path> children = this.context.getFileAccess().listChildren(template, f -> true);
9✔
222
    for (Path child : children) {
10✔
223

224
      String basename = child.getFileName().toString();
4✔
225
      Path confPath = conf.resolve(basename);
4✔
226

227
      if (Files.isDirectory(child)) {
5✔
228
        if (!Files.isDirectory(confPath)) {
5!
229
          this.context.getFileAccess().mkdirs(confPath);
5✔
230
        }
231
        setupConf(child, confPath);
5✔
232
      } else if (Files.isRegularFile(child)) {
5!
233
        if (Files.isRegularFile(confPath)) {
5!
234
          LOG.debug("Configuration {} already exists - skipping to copy from {}", confPath, child);
×
235
        } else {
236
          if (!basename.equals("settings.xml")) {
4!
237
            LOG.info("Copying template {} to {}.", child, conf);
5✔
238
            this.context.getFileAccess().copy(child, conf);
6✔
239
          }
240
        }
241
      }
242
    }
1✔
243
  }
1✔
244

245
  /**
246
   * Updates the settings repository in IDE_HOME/settings by either cloning if no such repository exists or pulling if the repository exists then saves the
247
   * latest current commit ID in the file ".commit.id".
248
   */
249
  protected void updateSettings() {
250

251
    this.context.newStep(getStepMessage()).run(this::updateSettingsInStep);
9✔
252
  }
1✔
253

254
  protected String getStepMessage() {
255

256
    return "update (pull) settings repository";
2✔
257
  }
258

259
  private void updateSettingsInStep() {
260
    Path settingsPath = this.context.getSettingsPath();
4✔
261
    GitContext gitContext = this.context.getGitContext();
4✔
262
    // here we do not use pullOrClone to prevent asking a pointless question for repository URL...
263
    if (Files.isDirectory(settingsPath) && this.context.getGitContext().isGitRepo(settingsPath) || this.context.isSettingsRepositorySymlinkOrJunction()) {
15!
264
      if (this.context.isForcePull() || this.context.isForceMode()) {
8!
265
        if (gitContext.hasUntrackedFiles(settingsPath)) {
×
266
          gitContext.pullSafelyWithStash(settingsPath);
×
267
        } else {
268
          gitContext.pull(settingsPath);
×
269
        }
270
        this.context.getGitContext().saveCurrentCommitId(settingsPath, this.context.getSettingsCommitIdPath());
×
271
      } else {
272
        LOG.info("Skipping git pull in settings due to code repository. Use --force-pull to enforce pulling.");
4✔
273
      }
274
    } else {
275
      if (!this.context.getFileAccess().isEmptyDir(settingsPath)) {
6!
276
        this.context.askToContinue(
×
277
          "Your settings repository seems to be broken ('.git' folder not present). We can fix this by moving "
278
          + " your settings the backed up. You will be asked for the settings git URL and your settings will be cloned from scratch. Do you want to proceed?"
279
        );
280

281
        this.context.getFileAccess().backup(settingsPath);
×
282
      }
283

284
      GitUrl gitUrl = getOrAskSettingsUrl();
3✔
285
      initializeRepository(gitUrl);
3✔
286
    }
287
  }
1✔
288

289
  private GitUrl getOrAskSettingsUrl() {
290

291
    String repository = this.settingsRepo.getValue();
5✔
292
    repository = handleDefaultRepository(repository);
4✔
293
    String userPromt = "Repository URL [" + IdeContext.DEFAULT_SETTINGS_REPO_URL + "]:";
2✔
294
    String defaultUrl = IdeContext.DEFAULT_SETTINGS_REPO_URL;
2✔
295
    LOG.info(MESSAGE_SETTINGS_REPO_URL, this.context.getSettingsPath());
6✔
296

297
    GitUrl gitUrl = null;
2✔
298
    if (repository != null) {
2✔
299
      gitUrl = GitUrl.of(repository);
3✔
300
    }
301
    while ((gitUrl == null) || !gitUrl.isValid()) {
5!
302
      repository = this.context.askForInput(userPromt, defaultUrl);
6✔
303
      repository = handleDefaultRepository(repository);
4✔
304
      gitUrl = GitUrl.of(repository);
3✔
305
      if (!gitUrl.isValid()) {
3!
306
        LOG.warn("The input URL is not valid, please try again.");
×
307
      }
308
    }
309
    return gitUrl;
2✔
310
  }
311

312
  private String handleDefaultRepository(String repository) {
313
    if ("-".equals(repository)) {
4✔
314
      LOG.info("'-' was found for the repository, the default settings repository '{}' will be used.", IdeContext.DEFAULT_SETTINGS_REPO_URL);
4✔
315
      repository = IdeContext.DEFAULT_SETTINGS_REPO_URL;
2✔
316
    }
317
    return repository;
2✔
318
  }
319

320
  private void initializeRepository(GitUrl gitUrl) {
321

322
    GitContext gitContext = this.context.getGitContext();
4✔
323
    Path settingsPath = this.context.getSettingsPath();
4✔
324
    Path repoPath = settingsPath;
2✔
325
    gitContext.pullOrClone(gitUrl, repoPath);
4✔
326
    this.context.getGitContext().saveCurrentCommitId(settingsPath, this.context.getSettingsCommitIdPath());
8✔
327
  }
1✔
328

329
  private void updateSoftware() {
330

331
    if (this.skipTools.isTrue()) {
4✔
332
      LOG.info("Skipping installation/update of tools as specified by the user.");
3✔
333
      return;
1✔
334
    }
335
    Step step = this.context.newStep("Install or update software");
5✔
336
    step.run(() -> doUpdateSoftwareStep(step));
10✔
337
  }
1✔
338

339
  private void doUpdateSoftwareStep(Step step) {
340

341
    Set<ToolCommandlet> toolCommandlets = new HashSet<>();
4✔
342
    CommandletManager commandletManager = this.context.getCommandletManager();
4✔
343
    // installed tools in IDE_HOME/software
344
    List<Path> softwarePaths = this.context.getFileAccess().listChildren(this.context.getSoftwarePath(), Files::isDirectory);
14✔
345
    for (Path softwarePath : softwarePaths) {
10✔
346
      String toolName = softwarePath.getFileName().toString();
4✔
347
      ToolCommandlet toolCommandlet = commandletManager.getToolCommandlet(toolName);
4✔
348
      if (toolCommandlet != null) {
2!
349
        toolCommandlets.add(toolCommandlet);
4✔
350
      }
351
    }
1✔
352

353
    // regular tools in $IDE_TOOLS
354
    List<String> regularTools = IdeVariables.IDE_TOOLS.get(this.context);
6✔
355
    if (regularTools != null) {
2!
356
      for (String regularTool : regularTools) {
10✔
357
        ToolCommandlet toolCommandlet = commandletManager.getToolCommandlet(regularTool);
4✔
358
        if (toolCommandlet == null) {
2!
359
          String displayName = (regularTool == null || regularTool.isBlank()) ? "<empty>" : "'" + regularTool + "'";
×
360
          LOG.error("Cannot install or update tool '{}''. No matching commandlet found. Please check your IDE_TOOLS configuration.", displayName);
×
361
        } else {
×
362
          toolCommandlets.add(toolCommandlet);
4✔
363
        }
364
      }
1✔
365
    }
366

367
    // custom tools in ide-custom-tools.json
368
    for (CustomToolMetadata customTool : this.context.getCustomToolRepository().getTools()) {
9!
369
      CustomToolCommandlet customToolCommandlet = new CustomToolCommandlet(this.context, customTool);
×
370
      toolCommandlets.add(customToolCommandlet);
×
371
    }
×
372

373
    // update/install the toolCommandlets
374
    for (ToolCommandlet toolCommandlet : toolCommandlets) {
10✔
375
      this.context.newStep("Install " + toolCommandlet.getName()).run(() -> toolCommandlet.install(false));
15✔
376
    }
1✔
377

378
    ExtraTools extraTools = ExtraToolsMapper.get().loadJsonFromFolder(this.context.getSettingsPath());
7✔
379
    if (extraTools != null) {
2✔
380
      List<String> toolNames = extraTools.getSortedToolNames();
3✔
381
      LOG.info("Found extra installation of the following tools: {}", toolNames);
4✔
382
      for (String tool : toolNames) {
10✔
383
        List<ExtraToolInstallation> installations = extraTools.getExtraInstallations(tool);
4✔
384
        this.context.newStep("Install extra version(s) of " + tool).run(() -> installExtraToolInstallations(tool, installations));
16✔
385
      }
1✔
386
    }
387
  }
1✔
388

389
  private void installExtraToolInstallations(String tool, List<ExtraToolInstallation> extraInstallations) {
390

391
    CommandletManager commandletManager = this.context.getCommandletManager();
4✔
392
    FileAccess fileAccess = this.context.getFileAccess();
4✔
393
    Path extraPath = this.context.getSoftwareExtraPath();
4✔
394
    LocalToolCommandlet toolCommandlet = commandletManager.getRequiredLocalToolCommandlet(tool);
4✔
395
    for (ExtraToolInstallation extraInstallation : extraInstallations) {
10✔
396
      ToolInstallRequest request = new ToolInstallRequest(false);
5✔
397
      String edition = extraInstallation.edition();
3✔
398
      if (edition == null) {
2✔
399
        edition = toolCommandlet.getConfiguredEdition();
3✔
400
      }
401
      ToolEdition toolEdition = new ToolEdition(tool, edition);
6✔
402
      VersionIdentifier version = extraInstallation.version();
3✔
403
      request.setRequested(new ToolEditionAndVersion(toolEdition, version));
7✔
404
      Path extraToolPath = extraPath.resolve(tool);
4✔
405
      Path toolPath = extraToolPath.resolve(extraInstallation.name());
5✔
406
      request.setToolPathForExtraInstallation(toolPath);
3✔
407
      toolCommandlet.install(request);
4✔
408
    }
1✔
409
  }
1✔
410

411
  private void updateRepositories() {
412

413
    if (this.skipRepositories.isTrue()) {
4!
414
      if (this.forceRepositories.isTrue()) {
×
415
        LOG.warn("Options to skip and force repositories are incompatible and should not be combined. Ignoring --force-repositories to proceed.");
×
416
      }
417
      LOG.info("Skipping setup of repositories as specified by the user.");
×
418
      return;
×
419
    }
420
    RepositoryCommandlet repositoryCommandlet = this.context.getCommandletManager().getCommandlet(RepositoryCommandlet.class);
7✔
421
    repositoryCommandlet.reset();
2✔
422
    repositoryCommandlet.run();
2✔
423
  }
1✔
424

425
  private void createStartScripts() {
426

427
    List<String> ides = IdeVariables.CREATE_START_SCRIPTS.get(this.context);
6✔
428
    if (ides == null) {
2✔
429
      LOG.info("Variable CREATE_START_SCRIPTS is undefined - skipping start script creation.");
3✔
430
      return;
1✔
431
    }
432
    for (String ide : ides) {
10✔
433
      ToolCommandlet tool = this.context.getCommandletManager().getToolCommandlet(ide);
6✔
434
      if (tool == null) {
2!
435
        LOG.error("Undefined IDE '{}' configured in variable CREATE_START_SCRIPTS.", ide);
×
436
      } else {
437
        createStartScript(ide);
3✔
438
      }
439
    }
1✔
440
  }
1✔
441

442
  private void createStartScript(String ide) {
443

444
    LOG.info("Creating start scripts for {}", ide);
4✔
445
    Path workspaces = this.context.getIdeHome().resolve(IdeContext.FOLDER_WORKSPACES);
6✔
446
    try (Stream<Path> childStream = Files.list(workspaces)) {
3✔
447
      Iterator<Path> iterator = childStream.iterator();
3✔
448
      while (iterator.hasNext()) {
3✔
449
        Path child = iterator.next();
4✔
450
        if (Files.isDirectory(child)) {
5!
451
          createStartScript(ide, child.getFileName().toString());
6✔
452
        }
453
      }
1✔
454
    } catch (IOException e) {
×
455
      throw new RuntimeException("Failed to list children of directory " + workspaces, e);
×
456
    }
1✔
457
  }
1✔
458

459
  private void createStartScript(String ide, String workspace) {
460

461
    Path ideHome = this.context.getIdeHome();
4✔
462
    String scriptName = ide + "-" + workspace;
4✔
463
    boolean windows = this.context.getSystemInfo().isWindows();
5✔
464
    if (windows) {
2!
465
      scriptName = scriptName + ".bat";
×
466
    } else {
467
      scriptName = scriptName + ".sh";
3✔
468
    }
469
    Path scriptPath = ideHome.resolve(scriptName);
4✔
470
    if (Files.exists(scriptPath)) {
5!
471
      return;
×
472
    }
473
    String scriptContent;
474
    if (windows) {
2!
475
      scriptContent = "@echo off\r\n" + "pushd %~dp0\r\n" + "cd workspaces/" + workspace + "\r\n" + "call ide " + ide + "\r\n" + "popd\r\n";
×
476
    } else {
477
      scriptContent = "#!/usr/bin/env bash\n" + "cd \"$(dirname \"$0\")\"\n" + "cd workspaces/" + workspace + "\n" + "ideasy " + ide + "\n";
4✔
478
    }
479
    FileAccess fileAccess = this.context.getFileAccess();
4✔
480
    fileAccess.writeFileContent(scriptContent, scriptPath);
4✔
481
    fileAccess.makeExecutable(scriptPath);
3✔
482
  }
1✔
483
}
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