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

devonfw / IDEasy / 15855321673

24 Jun 2025 03:51PM UTC coverage: 68.191% (+0.4%) from 67.783%
15855321673

Pull #1375

github

web-flow
Merge 26e6591cb into 584febaaf
Pull Request #1375: #742: Show warning when git repo name does not meet name convention.

3198 of 5094 branches covered (62.78%)

Branch coverage included in aggregate %.

8213 of 11640 relevant lines covered (70.56%)

3.09 hits per line

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

80.6
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.apache.commons.lang3.NotImplementedException;
13

14
import com.devonfw.tools.ide.context.AbstractIdeContext;
15
import com.devonfw.tools.ide.context.IdeContext;
16
import com.devonfw.tools.ide.git.GitContext;
17
import com.devonfw.tools.ide.git.repository.RepositoryCommandlet;
18
import com.devonfw.tools.ide.io.FileAccess;
19
import com.devonfw.tools.ide.property.FlagProperty;
20
import com.devonfw.tools.ide.property.StringProperty;
21
import com.devonfw.tools.ide.step.Step;
22
import com.devonfw.tools.ide.tool.CustomToolCommandlet;
23
import com.devonfw.tools.ide.tool.ToolCommandlet;
24
import com.devonfw.tools.ide.tool.repository.CustomToolMetadata;
25
import com.devonfw.tools.ide.variable.IdeVariables;
26

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

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

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

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

41
  /** {@link FlagProperty} to force the update of the settings git repository. */
42
  public final FlagProperty forcePull;
43

44
  /** {@link FlagProperty} to force the installation/update of plugins. */
45
  public final FlagProperty forcePlugins;
46

47
  /** {@link FlagProperty} to force the setup of git repositories. */
48
  public final FlagProperty forceRepositories;
49

50
  /**
51
   * The constructor.
52
   *
53
   * @param context the {@link IdeContext}.
54
   */
55
  public AbstractUpdateCommandlet(IdeContext context) {
56

57
    super(context);
3✔
58
    addKeyword(getName());
4✔
59
    this.skipTools = add(new FlagProperty("--skip-tools"));
9✔
60
    this.skipRepositories = add(new FlagProperty("--skip-repositories"));
9✔
61
    this.forcePull = add(new FlagProperty("--force-pull"));
9✔
62
    this.forcePlugins = add(new FlagProperty("--force-plugins"));
9✔
63
    this.forceRepositories = add(new FlagProperty("--force-repositories"));
9✔
64
    this.settingsRepo = new StringProperty("", false, "settingsRepository");
8✔
65
  }
1✔
66

67
  @Override
68
  public void run() {
69

70
    this.context.setForcePull(forcePull.isTrue());
6✔
71
    this.context.setForcePlugins(forcePlugins.isTrue());
6✔
72
    this.context.setForceRepositories(forceRepositories.isTrue());
6✔
73

74
    if (!this.context.isSettingsRepositorySymlinkOrJunction() || this.context.isForceMode() || forcePull.isTrue()) {
4!
75
      updateSettings();
2✔
76
    }
77
    updateConf();
2✔
78
    reloadContext();
2✔
79

80
    updateSoftware();
2✔
81
    updateRepositories();
2✔
82
    createStartScripts();
2✔
83
  }
1✔
84

85
  private void reloadContext() {
86

87
    ((AbstractIdeContext) this.context).reload();
4✔
88
  }
1✔
89

90
  private void updateConf() {
91

92
    Path templatesFolder = this.context.getSettingsPath().resolve(IdeContext.FOLDER_TEMPLATES);
6✔
93
    if (!Files.exists(templatesFolder)) {
5✔
94
      Path legacyTemplatesFolder = this.context.getSettingsPath().resolve(IdeContext.FOLDER_LEGACY_TEMPLATES);
6✔
95
      if (Files.exists(legacyTemplatesFolder)) {
5!
96
        templatesFolder = legacyTemplatesFolder;
×
97
      } else {
98
        this.context.warning("Templates folder is missing in settings repository.");
4✔
99
        return;
1✔
100
      }
101
    }
102

103
    Step step = this.context.newStep("Copy configuration templates", templatesFolder);
11✔
104
    final Path finalTemplatesFolder = templatesFolder;
2✔
105
    step.run(() -> setupConf(finalTemplatesFolder, this.context.getIdeHome()));
13✔
106
  }
1✔
107

108
  private void setupConf(Path template, Path conf) {
109

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

113
      String basename = child.getFileName().toString();
4✔
114
      Path confPath = conf.resolve(basename);
4✔
115

116
      if (Files.isDirectory(child)) {
5✔
117
        if (!Files.isDirectory(confPath)) {
5!
118
          this.context.getFileAccess().mkdirs(confPath);
5✔
119
        }
120
        setupConf(child, confPath);
5✔
121
      } else if (Files.isRegularFile(child)) {
5!
122
        if (Files.isRegularFile(confPath)) {
5!
123
          this.context.debug("Configuration {} already exists - skipping to copy from {}", confPath, child);
×
124
        } else {
125
          if (!basename.equals("settings.xml")) {
4!
126
            this.context.info("Copying template {} to {}.", child, conf);
14✔
127
            this.context.getFileAccess().copy(child, conf);
6✔
128
          }
129
        }
130
      }
131
    }
1✔
132
  }
1✔
133

134
  /**
135
   * Process a repository and returns the created new step.
136
   */
137
  protected void processRepository() {
138
    throw new NotImplementedException("The process Repository is not implemented until it is overwritten.");
×
139
  }
140

141
  /**
142
   * 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
143
   * latest current commit ID in the file ".commit.id".
144
   */
145
  protected void updateSettings() {
146

147
    Path settingsPath = this.context.getSettingsPath();
4✔
148
    GitContext gitContext = this.context.getGitContext();
4✔
149
    Step step = null;
2✔
150
    try {
151
      // here we do not use pullOrClone to prevent asking a pointless question for repository URL...
152
      if (Files.isDirectory(settingsPath) && !this.context.getFileAccess().isEmptyDir(settingsPath)) {
11!
153
        step = this.context.newStep("Pull settings repository");
5✔
154
        gitContext.pull(settingsPath);
3✔
155
        this.context.getGitContext().saveCurrentCommitId(settingsPath, this.context.getSettingsCommitIdPath());
8✔
156
        step.success("Successfully updated settings repository.");
4✔
157
      } else {
158
        processRepository();
2✔
159
      }
160
    } finally {
161
      if (step != null) {
2✔
162
        step.close();
2✔
163
      }
164
    }
165
  }
1✔
166

167
  private void updateSoftware() {
168

169
    if (this.skipTools.isTrue()) {
4✔
170
      this.context.info("Skipping installation/update of tools as specified by the user.");
4✔
171
      return;
1✔
172
    }
173
    Step step = this.context.newStep("Install or update software");
5✔
174
    step.run(() -> doUpdateSoftwareStep(step));
10✔
175
  }
1✔
176

177
  private void doUpdateSoftwareStep(Step step) {
178

179
    Set<ToolCommandlet> toolCommandlets = new HashSet<>();
4✔
180
    // installed tools in IDE_HOME/software
181
    List<Path> softwarePaths = this.context.getFileAccess().listChildren(this.context.getSoftwarePath(), Files::isDirectory);
14✔
182
    for (Path softwarePath : softwarePaths) {
10✔
183
      String toolName = softwarePath.getFileName().toString();
4✔
184
      ToolCommandlet toolCommandlet = this.context.getCommandletManager().getToolCommandlet(toolName);
6✔
185
      if (toolCommandlet != null) {
2!
186
        toolCommandlets.add(toolCommandlet);
4✔
187
      }
188
    }
1✔
189

190
    // regular tools in $IDE_TOOLS
191
    List<String> regularTools = IdeVariables.IDE_TOOLS.get(this.context);
6✔
192
    if (regularTools != null) {
2!
193
      for (String regularTool : regularTools) {
10✔
194
        toolCommandlets.add(this.context.getCommandletManager().getRequiredToolCommandlet(regularTool));
8✔
195
      }
1✔
196
    }
197

198
    // custom tools in ide-custom-tools.json
199
    for (CustomToolMetadata customTool : this.context.getCustomToolRepository().getTools()) {
9!
200
      CustomToolCommandlet customToolCommandlet = new CustomToolCommandlet(this.context, customTool);
×
201
      toolCommandlets.add(customToolCommandlet);
×
202
    }
×
203

204
    // update/install the toolCommandlets
205
    for (ToolCommandlet toolCommandlet : toolCommandlets) {
10✔
206
      this.context.newStep("Install " + toolCommandlet.getName()).run(() -> toolCommandlet.install(false));
15✔
207
    }
1✔
208
  }
1✔
209

210
  private void updateRepositories() {
211

212
    if (this.skipRepositories.isTrue()) {
4!
213
      if (this.forceRepositories.isTrue()) {
×
214
        this.context.warning("Options to skip and force repositories are incompatible and should not be combined. Ignoring --force-repositories to proceed.");
×
215
      }
216
      this.context.info("Skipping setup of repositories as specified by the user.");
×
217
      return;
×
218
    }
219
    RepositoryCommandlet repositoryCommandlet = this.context.getCommandletManager().getCommandlet(RepositoryCommandlet.class);
7✔
220
    repositoryCommandlet.reset();
2✔
221
    repositoryCommandlet.run();
2✔
222
  }
1✔
223

224
  private void createStartScripts() {
225

226
    List<String> ides = IdeVariables.CREATE_START_SCRIPTS.get(this.context);
6✔
227
    if (ides == null) {
2✔
228
      this.context.info("Variable CREATE_START_SCRIPTS is undefined - skipping start script creation.");
4✔
229
      return;
1✔
230
    }
231
    for (String ide : ides) {
10✔
232
      ToolCommandlet tool = this.context.getCommandletManager().getToolCommandlet(ide);
6✔
233
      if (tool == null) {
2!
234
        this.context.error("Undefined IDE '{}' configured in variable CREATE_START_SCRIPTS.");
×
235
      } else {
236
        createStartScript(ide);
3✔
237
      }
238
    }
1✔
239
  }
1✔
240

241
  private void createStartScript(String ide) {
242

243
    this.context.info("Creating start scripts for {}", ide);
10✔
244
    Path workspaces = this.context.getIdeHome().resolve(IdeContext.FOLDER_WORKSPACES);
6✔
245
    try (Stream<Path> childStream = Files.list(workspaces)) {
3✔
246
      Iterator<Path> iterator = childStream.iterator();
3✔
247
      while (iterator.hasNext()) {
3✔
248
        Path child = iterator.next();
4✔
249
        if (Files.isDirectory(child)) {
5!
250
          createStartScript(ide, child.getFileName().toString());
6✔
251
        }
252
      }
1✔
253
    } catch (IOException e) {
×
254
      throw new RuntimeException("Failed to list children of directory " + workspaces, e);
×
255
    }
1✔
256
  }
1✔
257

258
  private void createStartScript(String ide, String workspace) {
259

260
    Path ideHome = this.context.getIdeHome();
4✔
261
    String scriptName = ide + "-" + workspace;
4✔
262
    boolean windows = this.context.getSystemInfo().isWindows();
5✔
263
    if (windows) {
2!
264
      scriptName = scriptName + ".bat";
×
265
    } else {
266
      scriptName = scriptName + ".sh";
3✔
267
    }
268
    Path scriptPath = ideHome.resolve(scriptName);
4✔
269
    if (Files.exists(scriptPath)) {
5!
270
      return;
×
271
    }
272
    String scriptContent;
273
    if (windows) {
2!
274
      scriptContent = "@echo off\r\n"
×
275
          + "pushd %~dp0\r\n"
276
          + "cd workspaces/" + workspace + "\r\n"
277
          + "call ide " + ide + "\r\n"
278
          + "popd\r\n";
279
    } else {
280
      scriptContent = "#!/usr/bin/env bash\n"
4✔
281
          + "cd \"$(dirname \"$0\")\"\n"
282
          + "cd workspaces/" + workspace + "\n"
283
          + "ideasy " + ide + "\n";
284
    }
285
    FileAccess fileAccess = this.context.getFileAccess();
4✔
286
    fileAccess.writeFileContent(scriptContent, scriptPath);
4✔
287
    fileAccess.makeExecutable(scriptPath);
3✔
288
  }
1✔
289

290
  protected boolean isCodeRepository() {
291
    return false;
×
292
  }
293
}
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