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

devonfw / IDEasy / 15251366887

26 May 2025 10:14AM UTC coverage: 67.719% (+0.2%) from 67.562%
15251366887

push

github

web-flow
#1314: add IdeContext.runWithoutLogging to temporary disable logging (#1318)

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

3162 of 5068 branches covered (62.39%)

Branch coverage included in aggregate %.

8082 of 11536 relevant lines covered (70.06%)

3.07 hits per line

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

61.96
cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java
1
package com.devonfw.tools.ide.context;
2

3
import static com.devonfw.tools.ide.variable.IdeVariables.IDE_MIN_VERSION;
4

5
import java.io.BufferedReader;
6
import java.io.InputStreamReader;
7
import java.net.URL;
8
import java.net.URLConnection;
9
import java.nio.file.Files;
10
import java.nio.file.Path;
11
import java.time.LocalDateTime;
12
import java.util.ArrayList;
13
import java.util.HashMap;
14
import java.util.Iterator;
15
import java.util.List;
16
import java.util.Locale;
17
import java.util.Map;
18
import java.util.Objects;
19

20
import com.devonfw.tools.ide.cli.CliAbortException;
21
import com.devonfw.tools.ide.cli.CliArgument;
22
import com.devonfw.tools.ide.cli.CliArguments;
23
import com.devonfw.tools.ide.cli.CliException;
24
import com.devonfw.tools.ide.commandlet.Commandlet;
25
import com.devonfw.tools.ide.commandlet.CommandletManager;
26
import com.devonfw.tools.ide.commandlet.CommandletManagerImpl;
27
import com.devonfw.tools.ide.commandlet.ContextCommandlet;
28
import com.devonfw.tools.ide.commandlet.EnvironmentCommandlet;
29
import com.devonfw.tools.ide.commandlet.HelpCommandlet;
30
import com.devonfw.tools.ide.common.SystemPath;
31
import com.devonfw.tools.ide.completion.CompletionCandidate;
32
import com.devonfw.tools.ide.completion.CompletionCandidateCollector;
33
import com.devonfw.tools.ide.completion.CompletionCandidateCollectorDefault;
34
import com.devonfw.tools.ide.environment.AbstractEnvironmentVariables;
35
import com.devonfw.tools.ide.environment.EnvironmentVariables;
36
import com.devonfw.tools.ide.environment.EnvironmentVariablesType;
37
import com.devonfw.tools.ide.environment.IdeSystem;
38
import com.devonfw.tools.ide.environment.IdeSystemImpl;
39
import com.devonfw.tools.ide.git.GitContext;
40
import com.devonfw.tools.ide.git.GitContextImpl;
41
import com.devonfw.tools.ide.git.GitUrl;
42
import com.devonfw.tools.ide.io.FileAccess;
43
import com.devonfw.tools.ide.io.FileAccessImpl;
44
import com.devonfw.tools.ide.log.IdeLogLevel;
45
import com.devonfw.tools.ide.log.IdeLogger;
46
import com.devonfw.tools.ide.log.IdeSubLogger;
47
import com.devonfw.tools.ide.merge.DirectoryMerger;
48
import com.devonfw.tools.ide.migration.IdeMigrator;
49
import com.devonfw.tools.ide.network.NetworkProxy;
50
import com.devonfw.tools.ide.os.SystemInfo;
51
import com.devonfw.tools.ide.os.SystemInfoImpl;
52
import com.devonfw.tools.ide.os.WindowsHelper;
53
import com.devonfw.tools.ide.os.WindowsHelperImpl;
54
import com.devonfw.tools.ide.os.WindowsPathSyntax;
55
import com.devonfw.tools.ide.process.ProcessContext;
56
import com.devonfw.tools.ide.process.ProcessContextImpl;
57
import com.devonfw.tools.ide.process.ProcessResult;
58
import com.devonfw.tools.ide.property.Property;
59
import com.devonfw.tools.ide.step.Step;
60
import com.devonfw.tools.ide.step.StepImpl;
61
import com.devonfw.tools.ide.tool.repository.CustomToolRepository;
62
import com.devonfw.tools.ide.tool.repository.CustomToolRepositoryImpl;
63
import com.devonfw.tools.ide.tool.repository.DefaultToolRepository;
64
import com.devonfw.tools.ide.tool.repository.MavenRepository;
65
import com.devonfw.tools.ide.tool.repository.ToolRepository;
66
import com.devonfw.tools.ide.url.model.UrlMetadata;
67
import com.devonfw.tools.ide.util.DateTimeUtil;
68
import com.devonfw.tools.ide.validation.ValidationResult;
69
import com.devonfw.tools.ide.validation.ValidationResultValid;
70
import com.devonfw.tools.ide.validation.ValidationState;
71
import com.devonfw.tools.ide.variable.IdeVariables;
72
import com.devonfw.tools.ide.version.IdeVersion;
73
import com.devonfw.tools.ide.version.VersionIdentifier;
74

75
/**
76
 * Abstract base implementation of {@link IdeContext}.
77
 */
78
public abstract class AbstractIdeContext implements IdeContext {
79

80
  private static final GitUrl IDE_URLS_GIT = new GitUrl("https://github.com/devonfw/ide-urls.git", null);
7✔
81

82
  private static final String LICENSE_URL = "https://github.com/devonfw/IDEasy/blob/main/documentation/LICENSE.adoc";
83

84
  private final IdeStartContextImpl startContext;
85

86
  private Path ideHome;
87

88
  private final Path ideRoot;
89

90
  private Path confPath;
91

92
  protected Path settingsPath;
93

94
  private Path settingsCommitIdPath;
95

96
  protected Path pluginsPath;
97

98
  private Path workspacePath;
99

100
  private String workspaceName;
101

102
  private Path cwd;
103

104
  private Path downloadPath;
105

106
  protected Path userHome;
107

108
  private Path userHomeIde;
109

110
  private SystemPath path;
111

112
  private WindowsPathSyntax pathSyntax;
113

114
  private final SystemInfo systemInfo;
115

116
  private EnvironmentVariables variables;
117

118
  private final FileAccess fileAccess;
119

120
  protected CommandletManager commandletManager;
121

122
  protected ToolRepository defaultToolRepository;
123

124
  private CustomToolRepository customToolRepository;
125

126
  private MavenRepository mavenRepository;
127

128
  private DirectoryMerger workspaceMerger;
129

130
  protected UrlMetadata urlMetadata;
131

132
  protected Path defaultExecutionDirectory;
133

134
  private StepImpl currentStep;
135

136
  protected Boolean online;
137

138
  protected IdeSystem system;
139

140
  private NetworkProxy networkProxy;
141

142
  private WindowsHelper windowsHelper;
143

144
  /**
145
   * The constructor.
146
   *
147
   * @param startContext the {@link IdeLogger}.
148
   * @param workingDirectory the optional {@link Path} to current working directory.
149
   */
150
  public AbstractIdeContext(IdeStartContextImpl startContext, Path workingDirectory) {
151

152
    super();
2✔
153
    this.startContext = startContext;
3✔
154
    this.systemInfo = SystemInfoImpl.INSTANCE;
3✔
155
    this.commandletManager = new CommandletManagerImpl(this);
6✔
156
    this.fileAccess = new FileAccessImpl(this);
6✔
157
    String workspace = WORKSPACE_MAIN;
2✔
158
    if (workingDirectory == null) {
2!
159
      workingDirectory = Path.of(System.getProperty("user.dir"));
×
160
    } else {
161
      workingDirectory = workingDirectory.toAbsolutePath();
3✔
162
    }
163
    this.cwd = workingDirectory;
3✔
164
    // detect IDE_HOME and WORKSPACE
165
    Path currentDir = workingDirectory;
2✔
166
    String name1 = "";
2✔
167
    String name2 = "";
2✔
168
    Path ideRootPath = getIdeRootPathFromEnv();
3✔
169
    while (currentDir != null) {
2✔
170
      trace("Looking for IDE_HOME in {}", currentDir);
9✔
171
      if (isIdeHome(currentDir)) {
4✔
172
        if (FOLDER_WORKSPACES.equals(name1) && !name2.isEmpty()) {
7✔
173
          workspace = name2;
3✔
174
        }
175
        break;
176
      }
177
      name2 = name1;
2✔
178
      int nameCount = currentDir.getNameCount();
3✔
179
      if (nameCount >= 1) {
3✔
180
        name1 = currentDir.getName(nameCount - 1).toString();
7✔
181
      }
182
      currentDir = currentDir.getParent();
3✔
183
      if ((ideRootPath != null) && (ideRootPath.equals(currentDir))) {
2!
184
        // prevent that during tests we traverse to the real IDE project of IDEasy developer
185
        currentDir = null;
×
186
      }
187
    }
1✔
188

189
    // detection completed, initializing variables
190
    this.ideRoot = findIdeRoot(currentDir);
5✔
191

192
    setCwd(workingDirectory, workspace, currentDir);
5✔
193

194
    if (this.ideRoot != null) {
3✔
195
      Path tempDownloadPath = getTempDownloadPath();
3✔
196
      if (Files.isDirectory(tempDownloadPath)) {
6✔
197
        // TODO delete all files older than 1 day here...
198
      } else {
199
        this.fileAccess.mkdirs(tempDownloadPath);
4✔
200
      }
201
    }
202

203
    this.defaultToolRepository = new DefaultToolRepository(this);
6✔
204
    this.mavenRepository = new MavenRepository(this);
6✔
205
  }
1✔
206

207
  private Path findIdeRoot(Path ideHomePath) {
208

209
    Path ideRootPath = null;
2✔
210
    if (ideHomePath != null) {
2✔
211
      ideRootPath = ideHomePath.getParent();
4✔
212
    } else if (!isTest()) {
3!
213
      ideRootPath = getIdeRootPathFromEnv();
×
214
    }
215
    return ideRootPath;
2✔
216
  }
217

218
  /**
219
   * @return the {@link #getIdeRoot() IDE_ROOT} from the system environment.
220
   */
221
  protected Path getIdeRootPathFromEnv() {
222

223
    String root = getSystem().getEnv(IdeVariables.IDE_ROOT.getName());
×
224
    if (root != null) {
×
225
      Path rootPath = Path.of(root);
×
226
      if (Files.isDirectory(rootPath)) {
×
227
        return rootPath;
×
228
      }
229
    }
230
    return null;
×
231
  }
232

233
  @Override
234
  public void setCwd(Path userDir, String workspace, Path ideHome) {
235

236
    this.cwd = userDir;
3✔
237
    this.workspaceName = workspace;
3✔
238
    this.ideHome = ideHome;
3✔
239
    if (ideHome == null) {
2✔
240
      this.workspacePath = null;
3✔
241
      this.confPath = null;
3✔
242
      this.settingsPath = null;
3✔
243
      this.pluginsPath = null;
4✔
244
    } else {
245
      this.workspacePath = this.ideHome.resolve(FOLDER_WORKSPACES).resolve(this.workspaceName);
9✔
246
      this.confPath = this.ideHome.resolve(FOLDER_CONF);
6✔
247
      this.settingsPath = this.ideHome.resolve(FOLDER_SETTINGS);
6✔
248
      this.settingsCommitIdPath = this.ideHome.resolve(IdeContext.SETTINGS_COMMIT_ID);
6✔
249
      this.pluginsPath = this.ideHome.resolve(FOLDER_PLUGINS);
6✔
250
    }
251
    if (isTest()) {
3!
252
      // only for testing...
253
      if (this.ideHome == null) {
3✔
254
        this.userHome = Path.of("/non-existing-user-home-for-testing");
7✔
255
      } else {
256
        this.userHome = this.ideHome.resolve("home");
7✔
257
      }
258
    } else {
259
      this.userHome = Path.of(getSystem().getProperty("user.home"));
×
260
    }
261
    this.userHomeIde = this.userHome.resolve(FOLDER_DOT_IDE);
6✔
262
    this.downloadPath = this.userHome.resolve("Downloads/ide");
6✔
263

264
    this.path = computeSystemPath();
4✔
265
  }
1✔
266

267
  private String getMessageIdeHomeFound() {
268

269
    return "IDE environment variables have been set for " + this.ideHome + " in workspace " + this.workspaceName;
7✔
270
  }
271

272
  private String getMessageNotInsideIdeProject() {
273

274
    return "You are not inside an IDE project: " + this.cwd;
5✔
275
  }
276

277
  private String getMessageIdeRootNotFound() {
278

279
    String root = getSystem().getEnv("IDE_ROOT");
5✔
280
    if (root == null) {
2!
281
      return "The environment variable IDE_ROOT is undefined. Please reinstall IDEasy or manually repair IDE_ROOT variable.";
2✔
282
    } else {
283
      return "The environment variable IDE_ROOT is pointing to an invalid path " + root + ". Please reinstall IDEasy or manually repair IDE_ROOT variable.";
×
284
    }
285
  }
286

287
  /**
288
   * @return {@code true} if this is a test context for JUnits, {@code false} otherwise.
289
   */
290
  public boolean isTest() {
291

292
    return false;
×
293
  }
294

295
  protected SystemPath computeSystemPath() {
296

297
    return new SystemPath(this);
×
298
  }
299

300
  private boolean isIdeHome(Path dir) {
301

302
    if (!Files.isDirectory(dir.resolve("workspaces"))) {
7✔
303
      return false;
2✔
304
    } else if (!Files.isDirectory(dir.resolve("settings"))) {
7!
305
      return false;
×
306
    }
307
    return true;
2✔
308
  }
309

310
  private EnvironmentVariables createVariables() {
311

312
    AbstractEnvironmentVariables system = createSystemVariables();
3✔
313
    AbstractEnvironmentVariables user = system.extend(this.userHomeIde, EnvironmentVariablesType.USER);
6✔
314
    AbstractEnvironmentVariables settings = user.extend(this.settingsPath, EnvironmentVariablesType.SETTINGS);
6✔
315
    AbstractEnvironmentVariables workspace = settings.extend(this.workspacePath, EnvironmentVariablesType.WORKSPACE);
6✔
316
    AbstractEnvironmentVariables conf = workspace.extend(this.confPath, EnvironmentVariablesType.CONF);
6✔
317
    return conf.resolved();
3✔
318
  }
319

320
  protected AbstractEnvironmentVariables createSystemVariables() {
321

322
    return EnvironmentVariables.ofSystem(this);
3✔
323
  }
324

325
  @Override
326
  public SystemInfo getSystemInfo() {
327

328
    return this.systemInfo;
3✔
329
  }
330

331
  @Override
332
  public FileAccess getFileAccess() {
333

334
    // currently FileAccess contains download method and requires network proxy to be configured. Maybe download should be moved to its own interface/class
335
    configureNetworkProxy();
2✔
336
    return this.fileAccess;
3✔
337
  }
338

339
  @Override
340
  public CommandletManager getCommandletManager() {
341

342
    return this.commandletManager;
3✔
343
  }
344

345
  @Override
346
  public ToolRepository getDefaultToolRepository() {
347

348
    return this.defaultToolRepository;
3✔
349
  }
350

351
  @Override
352
  public MavenRepository getMavenToolRepository() {
353

354
    return this.mavenRepository;
3✔
355
  }
356

357
  @Override
358
  public CustomToolRepository getCustomToolRepository() {
359

360
    if (this.customToolRepository == null) {
3!
361
      this.customToolRepository = CustomToolRepositoryImpl.of(this);
4✔
362
    }
363
    return this.customToolRepository;
3✔
364
  }
365

366
  @Override
367
  public Path getIdeHome() {
368

369
    return this.ideHome;
3✔
370
  }
371

372
  @Override
373
  public String getProjectName() {
374

375
    if (this.ideHome != null) {
3!
376
      return this.ideHome.getFileName().toString();
5✔
377
    }
378
    return "";
×
379
  }
380

381
  @Override
382
  public VersionIdentifier getProjectVersion() {
383

384
    if (this.ideHome != null) {
3!
385
      Path versionFile = this.ideHome.resolve(IdeContext.FILE_SOFTWARE_VERSION);
5✔
386
      if (Files.exists(versionFile)) {
5✔
387
        String version = this.fileAccess.readFileContent(versionFile).trim();
6✔
388
        return VersionIdentifier.of(version);
3✔
389
      }
390
    }
391
    return IdeMigrator.START_VERSION;
2✔
392
  }
393

394
  @Override
395
  public void setProjectVersion(VersionIdentifier version) {
396

397
    if (this.ideHome == null) {
3!
398
      throw new IllegalStateException("IDE_HOME not available!");
×
399
    }
400
    Objects.requireNonNull(version);
3✔
401
    Path versionFile = this.ideHome.resolve(IdeContext.FILE_SOFTWARE_VERSION);
5✔
402
    this.fileAccess.writeFileContent(version.toString(), versionFile);
6✔
403
  }
1✔
404

405
  @Override
406
  public Path getIdeRoot() {
407

408
    return this.ideRoot;
3✔
409
  }
410

411
  @Override
412
  public Path getIdePath() {
413

414
    Path myIdeRoot = getIdeRoot();
3✔
415
    if (myIdeRoot == null) {
2!
416
      return null;
×
417
    }
418
    return myIdeRoot.resolve(FOLDER_UNDERSCORE_IDE);
4✔
419
  }
420

421
  @Override
422
  public Path getCwd() {
423

424
    return this.cwd;
3✔
425
  }
426

427
  @Override
428
  public Path getTempPath() {
429

430
    Path idePath = getIdePath();
3✔
431
    if (idePath == null) {
2!
432
      return null;
×
433
    }
434
    return idePath.resolve("tmp");
4✔
435
  }
436

437
  @Override
438
  public Path getTempDownloadPath() {
439

440
    Path tmp = getTempPath();
3✔
441
    if (tmp == null) {
2!
442
      return null;
×
443
    }
444
    return tmp.resolve(FOLDER_DOWNLOADS);
4✔
445
  }
446

447
  @Override
448
  public Path getUserHome() {
449

450
    return this.userHome;
3✔
451
  }
452

453
  @Override
454
  public Path getUserHomeIde() {
455

456
    return this.userHomeIde;
3✔
457
  }
458

459
  @Override
460
  public Path getSettingsPath() {
461

462
    return this.settingsPath;
3✔
463
  }
464

465
  @Override
466
  public Path getSettingsGitRepository() {
467

468
    Path settingsPath = getSettingsPath();
3✔
469
    // check whether the settings path has a .git folder only if its not a symbolic link or junction
470
    if ((settingsPath != null) && !Files.exists(settingsPath.resolve(".git")) && !isSettingsRepositorySymlinkOrJunction()) {
12!
471
      error("Settings repository exists but is not a git repository.");
3✔
472
      return null;
2✔
473
    }
474
    return settingsPath;
2✔
475
  }
476

477
  @Override
478
  public boolean isSettingsRepositorySymlinkOrJunction() {
479

480
    Path settingsPath = getSettingsPath();
3✔
481
    if (settingsPath == null) {
2!
482
      return false;
×
483
    }
484
    return Files.isSymbolicLink(settingsPath) || getFileAccess().isJunction(settingsPath);
10!
485
  }
486

487
  @Override
488
  public Path getSettingsCommitIdPath() {
489

490
    return this.settingsCommitIdPath;
3✔
491
  }
492

493
  @Override
494
  public Path getConfPath() {
495

496
    return this.confPath;
3✔
497
  }
498

499
  @Override
500
  public Path getSoftwarePath() {
501

502
    if (this.ideHome == null) {
3✔
503
      return null;
2✔
504
    }
505
    return this.ideHome.resolve(FOLDER_SOFTWARE);
5✔
506
  }
507

508
  @Override
509
  public Path getSoftwareExtraPath() {
510

511
    Path softwarePath = getSoftwarePath();
3✔
512
    if (softwarePath == null) {
2!
513
      return null;
×
514
    }
515
    return softwarePath.resolve(FOLDER_EXTRA);
4✔
516
  }
517

518
  @Override
519
  public Path getSoftwareRepositoryPath() {
520

521
    Path idePath = getIdePath();
3✔
522
    if (idePath == null) {
2!
523
      return null;
×
524
    }
525
    return idePath.resolve(FOLDER_SOFTWARE);
4✔
526
  }
527

528
  @Override
529
  public Path getPluginsPath() {
530

531
    return this.pluginsPath;
3✔
532
  }
533

534
  @Override
535
  public String getWorkspaceName() {
536

537
    return this.workspaceName;
3✔
538
  }
539

540
  @Override
541
  public Path getWorkspacePath() {
542

543
    return this.workspacePath;
3✔
544
  }
545

546
  @Override
547
  public Path getDownloadPath() {
548

549
    return this.downloadPath;
3✔
550
  }
551

552
  @Override
553
  public Path getUrlsPath() {
554

555
    Path idePath = getIdePath();
3✔
556
    if (idePath == null) {
2!
557
      return null;
×
558
    }
559
    return idePath.resolve(FOLDER_URLS);
4✔
560
  }
561

562
  @Override
563
  public Path getToolRepositoryPath() {
564

565
    Path idePath = getIdePath();
3✔
566
    if (idePath == null) {
2!
567
      return null;
×
568
    }
569
    return idePath.resolve(FOLDER_SOFTWARE);
4✔
570
  }
571

572
  @Override
573
  public SystemPath getPath() {
574

575
    return this.path;
3✔
576
  }
577

578
  @Override
579
  public EnvironmentVariables getVariables() {
580

581
    if (this.variables == null) {
3✔
582
      this.variables = createVariables();
4✔
583
    }
584
    return this.variables;
3✔
585
  }
586

587
  @Override
588
  public UrlMetadata getUrls() {
589

590
    if (this.urlMetadata == null) {
3✔
591
      if (!isTest()) {
3!
592
        getGitContext().pullOrCloneAndResetIfNeeded(IDE_URLS_GIT, getUrlsPath(), null);
×
593
      }
594
      this.urlMetadata = new UrlMetadata(this);
6✔
595
    }
596
    return this.urlMetadata;
3✔
597
  }
598

599
  @Override
600
  public boolean isQuietMode() {
601

602
    return this.startContext.isQuietMode();
4✔
603
  }
604

605
  @Override
606
  public boolean isBatchMode() {
607

608
    return this.startContext.isBatchMode();
4✔
609
  }
610

611
  @Override
612
  public boolean isForceMode() {
613

614
    return this.startContext.isForceMode();
4✔
615
  }
616

617
  @Override
618
  public boolean isForcePull() {
619

620
    return this.startContext.isForcePull();
×
621
  }
622

623
  @Override
624
  public boolean isForcePlugins() {
625

626
    return this.startContext.isForcePlugins();
×
627
  }
628

629
  @Override
630
  public boolean isForceRepositories() {
631

632
    return this.startContext.isForceRepositories();
×
633
  }
634

635
  @Override
636
  public boolean isOfflineMode() {
637

638
    return this.startContext.isOfflineMode();
4✔
639
  }
640

641
  @Override
642
  public boolean isSkipUpdatesMode() {
643

644
    return this.startContext.isSkipUpdatesMode();
4✔
645
  }
646

647
  @Override
648
  public boolean isOnline() {
649

650
    if (this.online == null) {
3✔
651
      configureNetworkProxy();
2✔
652
      // we currently assume we have only a CLI process that runs shortly
653
      // therefore we run this check only once to save resources when this method is called many times
654
      try {
655
        int timeout = 1000;
2✔
656
        //open a connection to github.com and try to retrieve data
657
        //getContent fails if there is no connection
658
        URLConnection connection = new URL("https://www.github.com").openConnection();
6✔
659
        connection.setConnectTimeout(timeout);
3✔
660
        connection.getContent();
3✔
661
        this.online = Boolean.TRUE;
3✔
662
      } catch (Exception ignored) {
×
663
        this.online = Boolean.FALSE;
×
664
      }
1✔
665
    }
666
    return this.online.booleanValue();
4✔
667
  }
668

669
  private void configureNetworkProxy() {
670

671
    if (this.networkProxy == null) {
3✔
672
      this.networkProxy = new NetworkProxy(this);
6✔
673
      this.networkProxy.configure();
3✔
674
    }
675
  }
1✔
676

677
  @Override
678
  public Locale getLocale() {
679

680
    Locale locale = this.startContext.getLocale();
4✔
681
    if (locale == null) {
2✔
682
      locale = Locale.getDefault();
2✔
683
    }
684
    return locale;
2✔
685
  }
686

687
  @Override
688
  public DirectoryMerger getWorkspaceMerger() {
689

690
    if (this.workspaceMerger == null) {
3✔
691
      this.workspaceMerger = new DirectoryMerger(this);
6✔
692
    }
693
    return this.workspaceMerger;
3✔
694
  }
695

696
  /**
697
   * @return the {@link #getDefaultExecutionDirectory() default execution directory} in which a command process is executed.
698
   */
699
  @Override
700
  public Path getDefaultExecutionDirectory() {
701

702
    return this.defaultExecutionDirectory;
×
703
  }
704

705
  /**
706
   * @param defaultExecutionDirectory new value of {@link #getDefaultExecutionDirectory()}.
707
   */
708
  public void setDefaultExecutionDirectory(Path defaultExecutionDirectory) {
709

710
    if (defaultExecutionDirectory != null) {
×
711
      this.defaultExecutionDirectory = defaultExecutionDirectory;
×
712
    }
713
  }
×
714

715
  @Override
716
  public GitContext getGitContext() {
717

718
    return new GitContextImpl(this);
×
719
  }
720

721
  @Override
722
  public ProcessContext newProcess() {
723

724
    ProcessContext processContext = createProcessContext();
3✔
725
    if (this.defaultExecutionDirectory != null) {
3!
726
      processContext.directory(this.defaultExecutionDirectory);
×
727
    }
728
    return processContext;
2✔
729
  }
730

731
  @Override
732
  public IdeSystem getSystem() {
733

734
    if (this.system == null) {
×
735
      this.system = new IdeSystemImpl(this);
×
736
    }
737
    return this.system;
×
738
  }
739

740
  /**
741
   * @return a new instance of {@link ProcessContext}.
742
   * @see #newProcess()
743
   */
744
  protected ProcessContext createProcessContext() {
745

746
    return new ProcessContextImpl(this);
5✔
747
  }
748

749
  @Override
750
  public IdeSubLogger level(IdeLogLevel level) {
751

752
    return this.startContext.level(level);
5✔
753
  }
754

755
  @Override
756
  public void logIdeHomeAndRootStatus() {
757

758
    if (this.ideRoot != null) {
3!
759
      success("IDE_ROOT is set to {}", this.ideRoot);
×
760
    }
761
    if (this.ideHome == null) {
3!
762
      warning(getMessageNotInsideIdeProject());
5✔
763
    } else {
764
      success("IDE_HOME is set to {}", this.ideHome);
×
765
    }
766
  }
1✔
767

768
  @Override
769
  public String askForInput(String message, String defaultValue) {
770

771
    if (!message.isBlank()) {
×
772
      info(message);
×
773
    }
774
    if (isBatchMode()) {
×
775
      if (isForceMode() || isForcePull()) {
×
776
        return defaultValue;
×
777
      } else {
778
        throw new CliAbortException();
×
779
      }
780
    }
781
    String input = readLine().trim();
×
782
    return input.isEmpty() ? defaultValue : input;
×
783
  }
784

785
  @Override
786
  public String askForInput(String message) {
787

788
    String input;
789
    do {
790
      info(message);
3✔
791
      input = readLine().trim();
4✔
792
    } while (input.isEmpty());
3!
793

794
    return input;
2✔
795
  }
796

797
  @SuppressWarnings("unchecked")
798
  @Override
799
  public <O> O question(String question, O... options) {
800

801
    assert (options.length >= 2);
×
802
    interaction(question);
×
803
    Map<String, O> mapping = new HashMap<>(options.length);
×
804
    int i = 0;
×
805
    for (O option : options) {
×
806
      i++;
×
807
      String key = "" + option;
×
808
      addMapping(mapping, key, option);
×
809
      String numericKey = Integer.toString(i);
×
810
      if (numericKey.equals(key)) {
×
811
        trace("Options should not be numeric: " + key);
×
812
      } else {
813
        addMapping(mapping, numericKey, option);
×
814
      }
815
      interaction("Option " + numericKey + ": " + key);
×
816
    }
817
    O option = null;
×
818
    if (isBatchMode()) {
×
819
      if (isForceMode() || isForcePull()) {
×
820
        option = options[0];
×
821
        interaction("" + option);
×
822
      }
823
    } else {
824
      while (option == null) {
×
825
        String answer = readLine();
×
826
        option = mapping.get(answer);
×
827
        if (option == null) {
×
828
          warning("Invalid answer: '" + answer + "' - please try again.");
×
829
        }
830
      }
×
831
    }
832
    return option;
×
833
  }
834

835
  /**
836
   * @return the input from the end-user (e.g. read from the console).
837
   */
838
  protected abstract String readLine();
839

840
  private static <O> void addMapping(Map<String, O> mapping, String key, O option) {
841

842
    O duplicate = mapping.put(key, option);
×
843
    if (duplicate != null) {
×
844
      throw new IllegalArgumentException("Duplicated option " + key);
×
845
    }
846
  }
×
847

848
  @Override
849
  public Step getCurrentStep() {
850

851
    return this.currentStep;
×
852
  }
853

854
  @Override
855
  public StepImpl newStep(boolean silent, String name, Object... parameters) {
856

857
    this.currentStep = new StepImpl(this, this.currentStep, name, silent, parameters);
11✔
858
    return this.currentStep;
3✔
859
  }
860

861
  /**
862
   * Internal method to end the running {@link Step}.
863
   *
864
   * @param step the current {@link Step} to end.
865
   */
866
  public void endStep(StepImpl step) {
867

868
    if (step == this.currentStep) {
4!
869
      this.currentStep = this.currentStep.getParent();
6✔
870
    } else {
871
      String currentStepName = "null";
×
872
      if (this.currentStep != null) {
×
873
        currentStepName = this.currentStep.getName();
×
874
      }
875
      warning("endStep called with wrong step '{}' but expected '{}'", step.getName(), currentStepName);
×
876
    }
877
  }
1✔
878

879
  /**
880
   * Finds the matching {@link Commandlet} to run, applies {@link CliArguments} to its {@link Commandlet#getProperties() properties} and will execute it.
881
   *
882
   * @param arguments the {@link CliArgument}.
883
   * @return the return code of the execution.
884
   */
885
  public int run(CliArguments arguments) {
886

887
    CliArgument current = arguments.current();
3✔
888
    assert (this.currentStep == null);
4!
889
    boolean supressStepSuccess = false;
2✔
890
    StepImpl step = newStep(true, "ide", (Object[]) current.asArray());
8✔
891
    Iterator<Commandlet> commandletIterator = this.commandletManager.findCommandlet(arguments, null);
6✔
892
    Commandlet cmd = null;
2✔
893
    ValidationResult result = null;
2✔
894
    try {
895
      while (commandletIterator.hasNext()) {
3✔
896
        cmd = commandletIterator.next();
4✔
897
        result = applyAndRun(arguments.copy(), cmd);
6✔
898
        if (result.isValid()) {
3!
899
          supressStepSuccess = cmd.isSuppressStepSuccess();
3✔
900
          step.success();
2✔
901
          return ProcessResult.SUCCESS;
4✔
902
        }
903
      }
904
      this.startContext.activateLogging();
3✔
905
      verifyIdeMinVersion(false);
3✔
906
      if (result != null) {
2!
907
        error(result.getErrorMessage());
×
908
      }
909
      step.error("Invalid arguments: {}", current.getArgs());
10✔
910
      HelpCommandlet help = this.commandletManager.getCommandlet(HelpCommandlet.class);
6✔
911
      if (cmd != null) {
2!
912
        help.commandlet.setValue(cmd);
×
913
      }
914
      help.run();
2✔
915
      return 1;
4✔
916
    } catch (Throwable t) {
1✔
917
      this.startContext.activateLogging();
3✔
918
      step.error(t, true);
4✔
919
      throw t;
2✔
920
    } finally {
921
      step.close();
2✔
922
      assert (this.currentStep == null);
4!
923
      step.logSummary(supressStepSuccess);
3✔
924
    }
925
  }
926

927
  @Override
928
  public void runWithoutLogging(Runnable lambda, IdeLogLevel threshold) {
929

930
    this.startContext.deactivateLogging(threshold);
4✔
931
    lambda.run();
2✔
932
    this.startContext.activateLogging();
3✔
933
  }
1✔
934

935
  /**
936
   * @param cmd the potential {@link Commandlet} to {@link #apply(CliArguments, Commandlet) apply} and {@link Commandlet#run() run}.
937
   * @return {@code true} if the given {@link Commandlet} matched and did {@link Commandlet#run() run} successfully, {@code false} otherwise (the
938
   *     {@link Commandlet} did not match and we have to try a different candidate).
939
   */
940
  private ValidationResult applyAndRun(CliArguments arguments, Commandlet cmd) {
941

942
    IdeLogLevel previousLogLevel = null;
2✔
943
    cmd.reset();
2✔
944
    ValidationResult result = apply(arguments, cmd);
5✔
945
    if (result.isValid()) {
3!
946
      result = cmd.validate();
3✔
947
    }
948
    if (result.isValid()) {
3!
949
      debug("Running commandlet {}", cmd);
9✔
950
      if (cmd.isIdeHomeRequired() && (this.ideHome == null)) {
6!
951
        throw new CliException(getMessageNotInsideIdeProject(), ProcessResult.NO_IDE_HOME);
×
952
      } else if (cmd.isIdeRootRequired() && (this.ideRoot == null)) {
6!
953
        throw new CliException(getMessageIdeRootNotFound(), ProcessResult.NO_IDE_ROOT);
7✔
954
      }
955
      try {
956
        if (cmd.isProcessableOutput()) {
3!
957
          if (!debug().isEnabled()) {
×
958
            // unless --debug or --trace was supplied, processable output commandlets will disable all log-levels except INFO to prevent other logs interfere
959
            previousLogLevel = this.startContext.setLogLevel(IdeLogLevel.PROCESSABLE);
×
960
          }
961
          this.startContext.activateLogging();
×
962
        } else {
963
          this.startContext.activateLogging();
3✔
964
          verifyIdeRoot();
2✔
965
          if (cmd.isIdeHomeRequired()) {
3!
966
            debug(getMessageIdeHomeFound());
4✔
967
          }
968
          Path settingsRepository = getSettingsGitRepository();
3✔
969
          if (settingsRepository != null) {
2!
970
            if (getGitContext().isRepositoryUpdateAvailable(settingsRepository, getSettingsCommitIdPath()) || (
×
971
                getGitContext().fetchIfNeeded(settingsRepository) && getGitContext().isRepositoryUpdateAvailable(
×
972
                    settingsRepository, getSettingsCommitIdPath()))) {
×
973
              if (isSettingsRepositorySymlinkOrJunction()) {
×
974
                interaction(
×
975
                    "Updates are available for the settings repository. Please pull the latest changes by yourself or by calling \"ide -f update\" to apply them.");
976

977
              } else {
978
                interaction(
×
979
                    "Updates are available for the settings repository. If you want to apply the latest changes, call \"ide update\"");
980
              }
981
            }
982
          }
983
        }
984
        boolean success = ensureLicenseAgreement(cmd);
4✔
985
        if (!success) {
2!
986
          return ValidationResultValid.get();
×
987
        }
988
        cmd.run();
2✔
989
      } finally {
990
        if (previousLogLevel != null) {
2!
991
          this.startContext.setLogLevel(previousLogLevel);
×
992
        }
993
      }
1✔
994
    } else {
995
      trace("Commandlet did not match");
×
996
    }
997
    return result;
2✔
998
  }
999

1000
  private boolean ensureLicenseAgreement(Commandlet cmd) {
1001

1002
    if (isTest()) {
3!
1003
      return true; // ignore for tests
2✔
1004
    }
1005
    getFileAccess().mkdirs(this.userHomeIde);
×
1006
    Path licenseAgreement = this.userHomeIde.resolve(FILE_LICENSE_AGREEMENT);
×
1007
    if (Files.isRegularFile(licenseAgreement)) {
×
1008
      return true; // success, license already accepted
×
1009
    }
1010
    if (cmd instanceof EnvironmentCommandlet) {
×
1011
      // if the license was not accepted, "$(ideasy env --bash)" that is written into a variable prevents the user from seeing the question he is asked
1012
      // in such situation the user could not open a bash terminal anymore and gets blocked what would really annoy the user so we exit here without doing or
1013
      // printing anything anymore in such case.
1014
      return false;
×
1015
    }
1016
    boolean logLevelInfoDisabled = !this.startContext.info().isEnabled();
×
1017
    if (logLevelInfoDisabled) {
×
1018
      this.startContext.setLogLevel(IdeLogLevel.INFO, true);
×
1019
    }
1020
    boolean logLevelInteractionDisabled = !this.startContext.interaction().isEnabled();
×
1021
    if (logLevelInteractionDisabled) {
×
1022
      this.startContext.setLogLevel(IdeLogLevel.INTERACTION, true);
×
1023
    }
1024
    StringBuilder sb = new StringBuilder(1180);
×
1025
    sb.append(LOGO).append("""
×
1026
        Welcome to IDEasy!
1027
        This product (with its included 3rd party components) is open-source software and can be used free (also commercially).
1028
        It supports automatic download and installation of arbitrary 3rd party tools.
1029
        By default only open-source 3rd party tools are used (downloaded, installed, executed).
1030
        But if explicitly configured, also commercial software that requires an additional license may be used.
1031
        This happens e.g. if you configure "ultimate" edition of IntelliJ or "docker" edition of Docker (Docker Desktop).
1032
        You are solely responsible for all risks implied by using this software.
1033
        Before using IDEasy you need to read and accept the license agreement with all involved licenses.
1034
        You will be able to find it online under the following URL:
1035
        """).append(LICENSE_URL);
×
1036
    if (this.ideRoot != null) {
×
1037
      sb.append("\n\nAlso it is included in the documentation that you can find here:\n").
×
1038
          append(getIdePath().resolve("IDEasy.pdf").toString()).append("\n");
×
1039
    }
1040
    info(sb.toString());
×
1041
    askToContinue("Do you accept these terms of use and all license agreements?");
×
1042

1043
    sb.setLength(0);
×
1044
    LocalDateTime now = LocalDateTime.now();
×
1045
    sb.append("On ").append(DateTimeUtil.formatDate(now, false)).append(" at ").append(DateTimeUtil.formatTime(now))
×
1046
        .append(" you accepted the IDEasy license.\n").append(LICENSE_URL);
×
1047
    try {
1048
      Files.writeString(licenseAgreement, sb);
×
1049
    } catch (Exception e) {
×
1050
      throw new RuntimeException("Failed to save license agreement!", e);
×
1051
    }
×
1052
    if (logLevelInfoDisabled) {
×
1053
      this.startContext.setLogLevel(IdeLogLevel.INFO, false);
×
1054
    }
1055
    if (logLevelInteractionDisabled) {
×
1056
      this.startContext.setLogLevel(IdeLogLevel.INTERACTION, false);
×
1057
    }
1058
    return true;
×
1059
  }
1060

1061
  private void verifyIdeRoot() {
1062

1063
    if (!isTest()) {
3!
1064
      if (this.ideRoot == null) {
×
1065
        warning("Variable IDE_ROOT is undefined. Please check your installation or run setup script again.");
×
1066
      } else if (this.ideHome != null) {
×
1067
        Path ideRootPath = getIdeRootPathFromEnv();
×
1068
        if (!this.ideRoot.equals(ideRootPath)) {
×
1069
          warning("Variable IDE_ROOT is set to '{}' but for your project '{}' the path '{}' would have been expected.", ideRootPath,
×
1070
              this.ideHome.getFileName(), this.ideRoot);
×
1071
        }
1072
      }
1073
    }
1074
  }
1✔
1075

1076
  @Override
1077
  public void verifyIdeMinVersion(boolean throwException) {
1078
    VersionIdentifier minVersion = IDE_MIN_VERSION.get(this);
5✔
1079
    if (minVersion == null) {
2✔
1080
      return;
1✔
1081
    }
1082
    if (IdeVersion.getVersionIdentifier().compareVersion(minVersion).isLess()) {
5✔
1083
      String message = String.format("Your version of IDEasy is currently %s\n"
7✔
1084
          + "However, this is too old as your project requires at latest version %s\n"
1085
          + "Please run the following command to update to the latest version of IDEasy and fix the problem:\n"
1086
          + "ide upgrade", IdeVersion.getVersionIdentifier().toString(), minVersion.toString());
8✔
1087
      if (throwException) {
2✔
1088
        throw new CliException(message);
5✔
1089
      } else {
1090
        warning(message);
3✔
1091
      }
1092
    }
1093
  }
1✔
1094

1095
  /**
1096
   * @param arguments the {@link CliArguments#ofCompletion(String...) completion arguments}.
1097
   * @param includeContextOptions to include the options of {@link ContextCommandlet}.
1098
   * @return the {@link List} of {@link CompletionCandidate}s to suggest.
1099
   */
1100
  public List<CompletionCandidate> complete(CliArguments arguments, boolean includeContextOptions) {
1101

1102
    CompletionCandidateCollector collector = new CompletionCandidateCollectorDefault(this);
5✔
1103
    if (arguments.current().isStart()) {
4✔
1104
      arguments.next();
3✔
1105
    }
1106
    if (includeContextOptions) {
2✔
1107
      ContextCommandlet cc = new ContextCommandlet();
4✔
1108
      for (Property<?> property : cc.getProperties()) {
11✔
1109
        assert (property.isOption());
4!
1110
        property.apply(arguments, this, cc, collector);
7✔
1111
      }
1✔
1112
    }
1113
    Iterator<Commandlet> commandletIterator = this.commandletManager.findCommandlet(arguments, collector);
6✔
1114
    CliArgument current = arguments.current();
3✔
1115
    if (current.isCompletion() && current.isCombinedShortOption()) {
6✔
1116
      collector.add(current.get(), null, null, null);
7✔
1117
    }
1118
    arguments.next();
3✔
1119
    while (commandletIterator.hasNext()) {
3✔
1120
      Commandlet cmd = commandletIterator.next();
4✔
1121
      if (!arguments.current().isEnd()) {
4✔
1122
        completeCommandlet(arguments.copy(), cmd, collector);
6✔
1123
      }
1124
    }
1✔
1125
    return collector.getSortedCandidates();
3✔
1126
  }
1127

1128
  private void completeCommandlet(CliArguments arguments, Commandlet cmd, CompletionCandidateCollector collector) {
1129

1130
    trace("Trying to match arguments for auto-completion for commandlet {}", cmd.getName());
10✔
1131
    Iterator<Property<?>> valueIterator = cmd.getValues().iterator();
4✔
1132
    valueIterator.next(); // skip first property since this is the keyword property that already matched to find the commandlet
3✔
1133
    List<Property<?>> properties = cmd.getProperties();
3✔
1134
    // we are creating our own list of options and remove them when matched to avoid duplicate suggestions
1135
    List<Property<?>> optionProperties = new ArrayList<>(properties.size());
6✔
1136
    for (Property<?> property : properties) {
10✔
1137
      if (property.isOption()) {
3✔
1138
        optionProperties.add(property);
4✔
1139
      }
1140
    }
1✔
1141
    CliArgument currentArgument = arguments.current();
3✔
1142
    while (!currentArgument.isEnd()) {
3✔
1143
      trace("Trying to match argument '{}'", currentArgument);
9✔
1144
      if (currentArgument.isOption() && !arguments.isEndOptions()) {
6!
1145
        if (currentArgument.isCompletion()) {
3✔
1146
          Iterator<Property<?>> optionIterator = optionProperties.iterator();
3✔
1147
          while (optionIterator.hasNext()) {
3✔
1148
            Property<?> option = optionIterator.next();
4✔
1149
            boolean success = option.apply(arguments, this, cmd, collector);
7✔
1150
            if (success) {
2✔
1151
              optionIterator.remove();
2✔
1152
              arguments.next();
3✔
1153
            }
1154
          }
1✔
1155
        } else {
1✔
1156
          Property<?> option = cmd.getOption(currentArgument.get());
5✔
1157
          if (option != null) {
2✔
1158
            arguments.next();
3✔
1159
            boolean removed = optionProperties.remove(option);
4✔
1160
            if (!removed) {
2!
1161
              option = null;
×
1162
            }
1163
          }
1164
          if (option == null) {
2✔
1165
            trace("No such option was found.");
3✔
1166
            return;
1✔
1167
          }
1168
        }
1✔
1169
      } else {
1170
        if (valueIterator.hasNext()) {
3✔
1171
          Property<?> valueProperty = valueIterator.next();
4✔
1172
          boolean success = valueProperty.apply(arguments, this, cmd, collector);
7✔
1173
          if (!success) {
2✔
1174
            trace("Completion cannot match any further.");
3✔
1175
            return;
1✔
1176
          }
1177
        } else {
1✔
1178
          trace("No value left for completion.");
3✔
1179
          return;
1✔
1180
        }
1181
      }
1182
      currentArgument = arguments.current();
4✔
1183
    }
1184
  }
1✔
1185

1186
  /**
1187
   * @param arguments the {@link CliArguments} to apply. Will be {@link CliArguments#next() consumed} as they are matched. Consider passing a
1188
   *     {@link CliArguments#copy() copy} as needed.
1189
   * @param cmd the potential {@link Commandlet} to match.
1190
   * @return the {@link ValidationResult} telling if the {@link CliArguments} can be applied successfully or if validation errors ocurred.
1191
   */
1192
  public ValidationResult apply(CliArguments arguments, Commandlet cmd) {
1193

1194
    trace("Trying to match arguments to commandlet {}", cmd.getName());
10✔
1195
    CliArgument currentArgument = arguments.current();
3✔
1196
    Iterator<Property<?>> propertyIterator = cmd.getValues().iterator();
4✔
1197
    Property<?> property = null;
2✔
1198
    if (propertyIterator.hasNext()) {
3!
1199
      property = propertyIterator.next();
4✔
1200
    }
1201
    while (!currentArgument.isEnd()) {
3✔
1202
      trace("Trying to match argument '{}'", currentArgument);
9✔
1203
      Property<?> currentProperty = property;
2✔
1204
      if (!arguments.isEndOptions()) {
3!
1205
        Property<?> option = cmd.getOption(currentArgument.getKey());
5✔
1206
        if (option != null) {
2!
1207
          currentProperty = option;
×
1208
        }
1209
      }
1210
      if (currentProperty == null) {
2!
1211
        trace("No option or next value found");
×
1212
        ValidationState state = new ValidationState(null);
×
1213
        state.addErrorMessage("No matching property found");
×
1214
        return state;
×
1215
      }
1216
      trace("Next property candidate to match argument is {}", currentProperty);
9✔
1217
      if (currentProperty == property) {
3!
1218
        if (!property.isMultiValued()) {
3✔
1219
          if (propertyIterator.hasNext()) {
3✔
1220
            property = propertyIterator.next();
5✔
1221
          } else {
1222
            property = null;
2✔
1223
          }
1224
        }
1225
        if ((property != null) && property.isValue() && property.isMultiValued()) {
8!
1226
          arguments.stopSplitShortOptions();
2✔
1227
        }
1228
      }
1229
      boolean matches = currentProperty.apply(arguments, this, cmd, null);
7✔
1230
      if (!matches) {
2!
1231
        ValidationState state = new ValidationState(null);
×
1232
        state.addErrorMessage("No matching property found");
×
1233
        return state;
×
1234
      }
1235
      currentArgument = arguments.current();
3✔
1236
    }
1✔
1237
    return ValidationResultValid.get();
2✔
1238
  }
1239

1240
  @Override
1241
  public String findBash() {
1242

1243
    String bash = "bash";
2✔
1244
    if (SystemInfoImpl.INSTANCE.isWindows()) {
3!
1245
      bash = findBashOnWindows();
×
1246
    }
1247

1248
    return bash;
2✔
1249
  }
1250

1251
  private String findBashOnWindows() {
1252

1253
    // Check if Git Bash exists in the default location
1254
    Path defaultPath = Path.of("C:\\Program Files\\Git\\bin\\bash.exe");
×
1255
    if (Files.exists(defaultPath)) {
×
1256
      return defaultPath.toString();
×
1257
    }
1258

1259
    // If not found in the default location, try the registry query
1260
    String[] bashVariants = { "GitForWindows", "Cygwin\\setup" };
×
1261
    String[] registryKeys = { "HKEY_LOCAL_MACHINE", "HKEY_CURRENT_USER" };
×
1262
    String regQueryResult;
1263
    for (String bashVariant : bashVariants) {
×
1264
      for (String registryKey : registryKeys) {
×
1265
        String toolValueName = ("GitForWindows".equals(bashVariant)) ? "InstallPath" : "rootdir";
×
1266
        String command = "reg query " + registryKey + "\\Software\\" + bashVariant + "  /v " + toolValueName + " 2>nul";
×
1267

1268
        try {
1269
          Process process = new ProcessBuilder("cmd.exe", "/c", command).start();
×
1270
          try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
×
1271
            StringBuilder output = new StringBuilder();
×
1272
            String line;
1273

1274
            while ((line = reader.readLine()) != null) {
×
1275
              output.append(line);
×
1276
            }
1277

1278
            int exitCode = process.waitFor();
×
1279
            if (exitCode != 0) {
×
1280
              return null;
×
1281
            }
1282

1283
            regQueryResult = output.toString();
×
1284
            if (regQueryResult != null) {
×
1285
              int index = regQueryResult.indexOf("REG_SZ");
×
1286
              if (index != -1) {
×
1287
                String path = regQueryResult.substring(index + "REG_SZ".length()).trim();
×
1288
                return path + "\\bin\\bash.exe";
×
1289
              }
1290
            }
1291

1292
          }
×
1293
        } catch (Exception e) {
×
1294
          return null;
×
1295
        }
×
1296
      }
1297
    }
1298
    // no bash found
1299
    return null;
×
1300
  }
1301

1302
  @Override
1303
  public WindowsPathSyntax getPathSyntax() {
1304

1305
    return this.pathSyntax;
3✔
1306
  }
1307

1308
  /**
1309
   * @param pathSyntax new value of {@link #getPathSyntax()}.
1310
   */
1311
  public void setPathSyntax(WindowsPathSyntax pathSyntax) {
1312

1313
    this.pathSyntax = pathSyntax;
3✔
1314
  }
1✔
1315

1316
  /**
1317
   * @return the {@link IdeStartContextImpl}.
1318
   */
1319
  public IdeStartContextImpl getStartContext() {
1320

1321
    return startContext;
3✔
1322
  }
1323

1324
  /**
1325
   * @return the {@link WindowsHelper}.
1326
   */
1327
  public final WindowsHelper getWindowsHelper() {
1328

1329
    if (this.windowsHelper == null) {
3✔
1330
      this.windowsHelper = createWindowsHelper();
4✔
1331
    }
1332
    return this.windowsHelper;
3✔
1333
  }
1334

1335
  /**
1336
   * @return the new {@link WindowsHelper} instance.
1337
   */
1338
  protected WindowsHelper createWindowsHelper() {
1339

1340
    return new WindowsHelperImpl(this);
×
1341
  }
1342

1343
  /**
1344
   * Reloads this context and re-initializes the {@link #getVariables() variables}.
1345
   */
1346
  public void reload() {
1347

1348
    this.variables = null;
3✔
1349
    this.customToolRepository = null;
3✔
1350
  }
1✔
1351

1352
  @Override
1353
  public void writeVersionFile(VersionIdentifier version, Path installationPath) {
1354

1355
    assert (Files.isDirectory(installationPath));
6!
1356
    Path versionFile = installationPath.resolve(FILE_SOFTWARE_VERSION);
4✔
1357
    getFileAccess().writeFileContent(version.toString(), versionFile);
6✔
1358
  }
1✔
1359

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