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

devonfw / IDEasy / 13063227142

30 Jan 2025 11:31PM UTC coverage: 68.379% (-0.2%) from 68.557%
13063227142

Pull #990

github

web-flow
Merge dde1c0e39 into aaf4eb4e0
Pull Request #990: #954: improve repository support

2857 of 4597 branches covered (62.15%)

Branch coverage included in aggregate %.

7391 of 10390 relevant lines covered (71.14%)

3.1 hits per line

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

81.5
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 com.devonfw.tools.ide.context.AbstractIdeContext;
13
import com.devonfw.tools.ide.context.IdeContext;
14
import com.devonfw.tools.ide.git.GitContext;
15
import com.devonfw.tools.ide.git.GitUrl;
16
import com.devonfw.tools.ide.git.repository.RepositoryCommandlet;
17
import com.devonfw.tools.ide.io.FileAccess;
18
import com.devonfw.tools.ide.property.FlagProperty;
19
import com.devonfw.tools.ide.property.StringProperty;
20
import com.devonfw.tools.ide.step.Step;
21
import com.devonfw.tools.ide.tool.CustomToolCommandlet;
22
import com.devonfw.tools.ide.tool.ToolCommandlet;
23
import com.devonfw.tools.ide.tool.repository.CustomToolMetadata;
24
import com.devonfw.tools.ide.variable.IdeVariables;
25

26
/**
27
 * Abstract {@link Commandlet} base-class for both {@link UpdateCommandlet} and {@link CreateCommandlet}.
28
 */
29
public abstract class AbstractUpdateCommandlet extends Commandlet {
30

31
  /** {@link StringProperty} for the settings repository URL. */
32
  public final StringProperty settingsRepo;
33

34
  /** {@link FlagProperty} for skipping installation/updating of tools. */
35
  public final FlagProperty skipTools;
36

37
  /** {@link FlagProperty} for skipping the setup of git repositories. */
38
  public final FlagProperty skipRepositories;
39

40
  /**
41
   * The constructor.
42
   *
43
   * @param context the {@link IdeContext}.
44
   */
45
  public AbstractUpdateCommandlet(IdeContext context) {
46

47
    super(context);
3✔
48
    addKeyword(getName());
4✔
49
    this.skipTools = add(new FlagProperty("--skip-tools"));
9✔
50
    this.skipRepositories = add(new FlagProperty("--skip-repositories"));
9✔
51
    this.settingsRepo = new StringProperty("", false, "settingsRepository");
8✔
52
  }
1✔
53

54
  @Override
55
  public void run() {
56

57
    if (!this.context.isSettingsRepositorySymlinkOrJunction() || this.context.isForceMode()) {
4!
58
      updateSettings();
2✔
59
    }
60
    updateConf();
2✔
61
    reloadContext();
2✔
62

63
    updateSoftware();
2✔
64
    updateRepositories();
2✔
65
    createStartScripts();
2✔
66
  }
1✔
67

68
  private void reloadContext() {
69

70
    ((AbstractIdeContext) this.context).reload();
4✔
71
  }
1✔
72

73
  private void updateConf() {
74

75
    Path templatesFolder = this.context.getSettingsPath().resolve(IdeContext.FOLDER_TEMPLATES);
6✔
76
    if (!Files.exists(templatesFolder)) {
5✔
77
      Path legacyTemplatesFolder = this.context.getSettingsPath().resolve(IdeContext.FOLDER_LEGACY_TEMPLATES);
6✔
78
      if (Files.exists(legacyTemplatesFolder)) {
5!
79
        templatesFolder = legacyTemplatesFolder;
×
80
      } else {
81
        this.context.warning("Templates folder is missing in settings repository.");
4✔
82
        return;
1✔
83
      }
84
    }
85

86
    try (Step step = this.context.newStep("Copy configuration templates", templatesFolder)) {
11✔
87
      setupConf(templatesFolder, this.context.getIdeHome());
6✔
88
      step.success();
2✔
89
    }
90
  }
1✔
91

92
  private void setupConf(Path template, Path conf) {
93

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

97
      String basename = child.getFileName().toString();
4✔
98
      Path confPath = conf.resolve(basename);
4✔
99

100
      if (Files.isDirectory(child)) {
5✔
101
        if (!Files.isDirectory(confPath)) {
5!
102
          this.context.getFileAccess().mkdirs(confPath);
5✔
103
        }
104
        setupConf(child, confPath);
5✔
105
      } else if (Files.isRegularFile(child)) {
5!
106
        if (Files.isRegularFile(confPath)) {
5!
107
          this.context.debug("Configuration {} already exists - skipping to copy from {}", confPath, child);
×
108
        } else {
109
          if (!basename.equals("settings.xml")) {
4!
110
            this.context.info("Copying template {} to {}.", child, conf);
14✔
111
            this.context.getFileAccess().copy(child, conf);
6✔
112
          }
113
        }
114
      }
115
    }
1✔
116
  }
1✔
117

118
  /**
119
   * 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
120
   * latest current commit ID in the file ".commit.id".
121
   */
122
  protected void updateSettings() {
123

124
    Path settingsPath = this.context.getSettingsPath();
4✔
125
    GitContext gitContext = this.context.getGitContext();
4✔
126
    Step step = null;
2✔
127
    try {
128
      // here we do not use pullOrClone to prevent asking a pointless question for repository URL...
129
      if (Files.isDirectory(settingsPath) && !this.context.getFileAccess().isEmptyDir(settingsPath)) {
11!
130
        step = this.context.newStep("Pull settings repository");
5✔
131
        gitContext.pull(settingsPath);
4✔
132
      } else {
133
        step = this.context.newStep("Clone settings repository");
5✔
134
        // check if a settings repository is given, otherwise prompt user for a repository.
135
        String repository = this.settingsRepo.getValue();
5✔
136
        if (repository == null) {
2!
137
          String message = "Missing your settings at " + settingsPath + " and no SETTINGS_URL is defined.\n"
×
138
              + "Further details can be found here: https://github.com/devonfw/IDEasy/blob/main/documentation/settings.asciidoc\n"
139
              + "Please contact the technical lead of your project to get the SETTINGS_URL for your project.\n"
140
              + "In case you just want to test IDEasy you may simply hit return to install the default settings.\n" + "Settings URL ["
141
              + IdeContext.DEFAULT_SETTINGS_REPO_URL + "]:";
142
          repository = this.context.askForInput(message, IdeContext.DEFAULT_SETTINGS_REPO_URL);
×
143
        } else if ("-".equals(repository)) {
4!
144
          repository = IdeContext.DEFAULT_SETTINGS_REPO_URL;
×
145
        }
146
        gitContext.pullOrClone(GitUrl.of(repository), settingsPath);
5✔
147
      }
148
      this.context.getGitContext().saveCurrentCommitId(settingsPath, this.context.getSettingsCommitIdPath());
8✔
149
      step.success("Successfully updated settings repository.");
3✔
150
    } finally {
151
      if (step != null) {
2!
152
        step.close();
2✔
153
      }
154
    }
155
  }
1✔
156

157
  private void updateSoftware() {
158

159
    if (this.skipTools.isTrue()) {
4✔
160
      this.context.info("Skipping installation/update of tools as specified by the user.");
4✔
161
      return;
1✔
162
    }
163
    try (Step step = this.context.newStep("Install or update software")) {
5✔
164
      Set<ToolCommandlet> toolCommandlets = new HashSet<>();
4✔
165
      // installed tools in IDE_HOME/software
166
      List<Path> softwarePaths = this.context.getFileAccess().listChildren(this.context.getSoftwarePath(), Files::isDirectory);
14✔
167
      for (Path softwarePath : softwarePaths) {
10✔
168
        String toolName = softwarePath.getFileName().toString();
4✔
169
        ToolCommandlet toolCommandlet = this.context.getCommandletManager().getToolCommandlet(toolName);
6✔
170
        if (toolCommandlet != null) {
2!
171
          toolCommandlets.add(toolCommandlet);
4✔
172
        }
173
      }
1✔
174

175
      // regular tools in $IDE_TOOLS
176
      List<String> regularTools = IdeVariables.IDE_TOOLS.get(this.context);
6✔
177
      if (regularTools != null) {
2!
178
        for (String regularTool : regularTools) {
10✔
179
          toolCommandlets.add(this.context.getCommandletManager().getRequiredToolCommandlet(regularTool));
8✔
180
        }
1✔
181
      }
182

183
      // custom tools in ide-custom-tools.json
184
      for (CustomToolMetadata customTool : this.context.getCustomToolRepository().getTools()) {
9!
185
        CustomToolCommandlet customToolCommandlet = new CustomToolCommandlet(this.context, customTool);
×
186
        toolCommandlets.add(customToolCommandlet);
×
187
      }
×
188

189
      // update/install the toolCommandlets
190
      for (ToolCommandlet toolCommandlet : toolCommandlets) {
10✔
191
        try {
192
          toolCommandlet.install(false);
4✔
193
        } catch (Exception e) {
1✔
194
          step.error(e, "Installation of {} failed!", toolCommandlet.getName());
11✔
195
        }
1✔
196
      }
1✔
197
      step.success();
2✔
198
    }
199
  }
1✔
200

201
  private void updateRepositories() {
202

203
    if (this.skipRepositories.isTrue()) {
4!
204
      this.context.info("Skipping setup of repositories as specified by the user.");
×
205
      return;
×
206
    }
207
    RepositoryCommandlet repositoryCommandlet = this.context.getCommandletManager().getCommandlet(RepositoryCommandlet.class);
7✔
208
    repositoryCommandlet.reset();
2✔
209
    repositoryCommandlet.run();
2✔
210
  }
1✔
211

212
  private void createStartScripts() {
213

214
    List<String> ides = IdeVariables.CREATE_START_SCRIPTS.get(this.context);
6✔
215
    if (ides == null) {
2✔
216
      this.context.info("Variable CREATE_START_SCRIPTS is undefined - skipping start script creation.");
4✔
217
      return;
1✔
218
    }
219
    for (String ide : ides) {
10✔
220
      ToolCommandlet tool = this.context.getCommandletManager().getToolCommandlet(ide);
6✔
221
      if (tool == null) {
2!
222
        this.context.error("Undefined IDE '{}' configured in variable CREATE_START_SCRIPTS.");
×
223
      } else {
224
        createStartScript(ide);
3✔
225
      }
226
    }
1✔
227
  }
1✔
228

229
  private void createStartScript(String ide) {
230

231
    this.context.info("Creating start scripts for {}", ide);
10✔
232
    Path workspaces = this.context.getIdeHome().resolve(IdeContext.FOLDER_WORKSPACES);
6✔
233
    try (Stream<Path> childStream = Files.list(workspaces)) {
3✔
234
      Iterator<Path> iterator = childStream.iterator();
3✔
235
      while (iterator.hasNext()) {
3✔
236
        Path child = iterator.next();
4✔
237
        if (Files.isDirectory(child)) {
5!
238
          createStartScript(ide, child.getFileName().toString());
6✔
239
        }
240
      }
1✔
241
    } catch (IOException e) {
×
242
      throw new RuntimeException("Failed to list children of directory " + workspaces, e);
×
243
    }
1✔
244
  }
1✔
245

246
  private void createStartScript(String ide, String workspace) {
247

248
    Path ideHome = this.context.getIdeHome();
4✔
249
    String scriptName = ide + "-" + workspace;
4✔
250
    boolean windows = this.context.getSystemInfo().isWindows();
5✔
251
    if (windows) {
2!
252
      scriptName = scriptName + ".bat";
×
253
    } else {
254
      scriptName = scriptName + ".sh";
3✔
255
    }
256
    Path scriptPath = ideHome.resolve(scriptName);
4✔
257
    if (Files.exists(scriptPath)) {
5!
258
      return;
×
259
    }
260
    String scriptContent;
261
    if (windows) {
2!
262
      scriptContent = "@echo off\r\n"
×
263
          + "pushd %~dp0\r\n"
264
          + "cd workspaces/" + workspace + "\r\n"
265
          + "call ide " + ide + "\r\n"
266
          + "popd\r\n";
267
    } else {
268
      scriptContent = "#!/usr/bin/env bash\n"
4✔
269
          + "cd \"$(dirname \"$0\")\"\n"
270
          + "cd workspaces/" + workspace + "\n"
271
          + "ide " + ide + "\n";
272
    }
273
    FileAccess fileAccess = this.context.getFileAccess();
4✔
274
    fileAccess.writeFileContent(scriptContent, scriptPath);
4✔
275
    fileAccess.makeExecutable(scriptPath);
3✔
276
  }
1✔
277

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

© 2025 Coveralls, Inc