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

devonfw / IDEasy / 22241505980

20 Feb 2026 09:16PM UTC coverage: 70.656% (+0.2%) from 70.474%
22241505980

Pull #1710

github

web-flow
Merge 04e4bdacd into 379acdc9d
Pull Request #1710: #404: allow logging via SLF4J

4121 of 6440 branches covered (63.99%)

Branch coverage included in aggregate %.

10704 of 14542 relevant lines covered (73.61%)

3.13 hits per line

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

63.78
cli/src/main/java/com/devonfw/tools/ide/git/repository/RepositoryCommandlet.java
1
package com.devonfw.tools.ide.git.repository;
2

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

8
import org.slf4j.Logger;
9
import org.slf4j.LoggerFactory;
10

11
import com.devonfw.tools.ide.commandlet.Commandlet;
12
import com.devonfw.tools.ide.context.IdeContext;
13
import com.devonfw.tools.ide.git.GitContext;
14
import com.devonfw.tools.ide.git.GitUrl;
15
import com.devonfw.tools.ide.io.FileAccess;
16
import com.devonfw.tools.ide.property.RepositoryProperty;
17
import com.devonfw.tools.ide.step.Step;
18
import com.devonfw.tools.ide.tool.ToolCommandlet;
19
import com.devonfw.tools.ide.tool.ide.IdeToolCommandlet;
20

21
/**
22
 * {@link Commandlet} to setup one or multiple GIT repositories for development.
23
 */
24
public class RepositoryCommandlet extends Commandlet {
25

26
  private static final Logger LOG = LoggerFactory.getLogger(RepositoryCommandlet.class);
4✔
27

28
  /** the repository to setup. */
29
  public final RepositoryProperty repository;
30

31
  /**
32
   * The constructor.
33
   *
34
   * @param context the {@link IdeContext}.
35
   */
36
  public RepositoryCommandlet(IdeContext context) {
37

38
    super(context);
3✔
39
    addKeyword(getName());
4✔
40
    addKeyword("setup");
3✔
41
    this.repository = add(new RepositoryProperty("", false, "repository"));
11✔
42
  }
1✔
43

44
  @Override
45
  public String getName() {
46

47
    return "repository";
2✔
48
  }
49

50
  @Override
51
  protected void doRun() {
52

53
    Path repositoryFile = this.repository.getValue();
5✔
54

55
    if (repositoryFile != null) {
2✔
56
      // Handle the case when a specific repository is provided
57
      doImportRepository(repositoryFile, true);
5✔
58
    } else {
59
      // If no specific repository is provided, check for repositories folder
60
      Path repositoriesPath = this.context.getRepositoriesPath();
4✔
61
      if (repositoriesPath == null) {
2✔
62
        LOG.warn("Cannot find folder 'repositories' nor 'projects' in your settings.");
3✔
63
        return;
1✔
64
      }
65
      List<Path> propertiesFiles = this.context.getFileAccess()
5✔
66
          .listChildren(repositoriesPath, path -> path.getFileName().toString().endsWith(".properties"));
8✔
67
      boolean forceMode = this.context.isForceMode() || this.context.isForceRepositories();
12!
68
      for (Path propertiesFile : propertiesFiles) {
10✔
69
        doImportRepository(propertiesFile, forceMode);
4✔
70
      }
1✔
71
    }
72
  }
1✔
73

74
  private void doImportRepository(Path repositoryFile, boolean forceMode) {
75

76
    String repositoryFilename = repositoryFile.getFileName().toString();
4✔
77
    final String repositoryId;
78
    if (repositoryFilename.endsWith(IdeContext.EXT_PROPERTIES)) {
4!
79
      repositoryId = repositoryFilename.substring(0, repositoryFilename.length() - IdeContext.EXT_PROPERTIES.length());
10✔
80
    } else {
81
      repositoryId = repositoryFilename;
×
82
    }
83
    this.context.newStep("Setup of repository " + repositoryId, repositoryFile).run(() -> {
18✔
84
      doImportRepository(repositoryFile, forceMode, repositoryId);
5✔
85
    });
1✔
86
  }
1✔
87

88
  private void doImportRepository(Path repositoryFile, boolean forceMode, String repositoryId) {
89
    RepositoryConfig repositoryConfig = RepositoryConfig.loadProperties(repositoryFile, this.context);
5✔
90
    if (!repositoryConfig.active()) {
3✔
91
      if (forceMode) {
2✔
92
        LOG.info("Setup of repository {} is forced, hence proceeding ...", repositoryId);
5✔
93
      } else {
94
        LOG.info("Skipping repository {} because it is not active - use --force to setup all repositories ...", repositoryId);
4✔
95
        return;
1✔
96
      }
97
    }
98
    GitUrl gitUrl = repositoryConfig.asGitUrl();
3✔
99
    if (gitUrl == null) {
2!
100
      // error was already logged.
101
      return;
×
102
    }
103
    LOG.debug("Repository configuration: {}", repositoryConfig);
4✔
104
    List<String> workspaces = repositoryConfig.workspaces();
3✔
105
    String repositoryRelativePath = repositoryConfig.path();
3✔
106
    if (repositoryRelativePath == null) {
2✔
107
      repositoryRelativePath = repositoryId;
2✔
108
    }
109
    Path ideStatusDir = this.context.getIdeHome().resolve(IdeContext.FOLDER_DOT_IDE);
6✔
110
    FileAccess fileAccess = this.context.getFileAccess();
4✔
111
    fileAccess.mkdirs(ideStatusDir);
3✔
112

113
    Path firstRepository = null;
2✔
114
    for (String workspaceName : workspaces) {
10✔
115
      Path workspacePath = this.context.getIdeHome().resolve(IdeContext.FOLDER_WORKSPACES).resolve(workspaceName);
8✔
116
      Path repositoryPath = workspacePath.resolve(repositoryRelativePath);
4✔
117
      if (Files.isDirectory(repositoryPath.resolve(GitContext.GIT_FOLDER))) {
7!
118
        if (firstRepository == null) {
×
119
          firstRepository = repositoryPath;
×
120
        }
121
        LOG.info("Repository {} already exists in workspace {} at {}", repositoryId, workspaceName, repositoryPath);
×
122
        if (!(this.context.isForceMode() || this.context.isForceRepositories())) {
×
123
          LOG.info("Ignoring repository {} in workspace {} - use --force or --force-repositories to rerun setup.", repositoryId, workspaceName);
×
124
          continue;
×
125
        }
126
      }
127
      Path repositoryCreatedStatusFile = ideStatusDir.resolve("repository." + repositoryId + "." + workspaceName);
6✔
128
      if (Files.exists(repositoryCreatedStatusFile)) {
5!
129
        if (!(this.context.isForceMode() || this.context.isForceRepositories())) {
×
130
          LOG.info("Ignoring repository {} in workspace {} because it was already setup before - use --force or --force-repositories for recreation.",
×
131
              repositoryId, workspaceName);
132
          continue;
×
133
        }
134
      }
135
      if (firstRepository == null) {
2✔
136
        boolean success = cloneOrPullRepository(repositoryPath, gitUrl, repositoryCreatedStatusFile);
6✔
137
        if (success) {
2!
138
          firstRepository = repositoryPath;
2✔
139
          buildRepository(repositoryConfig, repositoryPath);
5✔
140
          importRepository(repositoryConfig, repositoryPath, repositoryId);
5✔
141
        }
142
      } else {
1✔
143
        fileAccess.mkdirs(repositoryPath.getParent());
4✔
144
        fileAccess.symlink(firstRepository, repositoryPath);
4✔
145
      }
146
    }
1✔
147
  }
1✔
148

149
  private boolean cloneOrPullRepository(Path repositoryPath, GitUrl gitUrl, Path repositoryCreatedStatusFile) {
150

151
    FileAccess fileAccess = this.context.getFileAccess();
4✔
152
    return this.context.newStep("Clone or pull repository").run(() -> {
12✔
153
      fileAccess.mkdirs(repositoryPath);
3✔
154
      this.context.getGitContext().pullOrClone(gitUrl, repositoryPath);
6✔
155
      fileAccess.touch(repositoryCreatedStatusFile);
3✔
156
    });
1✔
157
  }
158

159
  private boolean buildRepository(RepositoryConfig repositoryConfig, Path repositoryPath) {
160
    String buildCmd = repositoryConfig.buildCmd();
3✔
161
    if (buildCmd != null && !buildCmd.isEmpty()) {
2!
162
      return this.context.newStep("Build repository via: " + buildCmd).run(() -> {
×
163
        String[] command = buildCmd.split("\\s+");
×
164
        ToolCommandlet commandlet = this.context.getCommandletManager().getToolCommandlet(command[0]);
×
165
        if (commandlet == null) {
×
166
          String displayName = (command[0] == null || command[0].isBlank()) ? "<empty>" : "'" + command[0] + "'";
×
167
          LOG.error("Cannot build repository. Required tool '{}' not found. Please check your repository's build_cmd configuration value.",
×
168
              displayName);
169
          return;
×
170
        }
171
        commandlet.reset();
×
172
        for (int i = 1; i < command.length; i++) {
×
173
          commandlet.arguments.addValue(command[i]);
×
174
        }
175
        Path executionDirectory = repositoryPath;
×
176
        String path = repositoryConfig.buildPath();
×
177
        if (path != null) {
×
178
          executionDirectory = executionDirectory.resolve(path);
×
179
        }
180
        commandlet.setExecutionDirectory(executionDirectory);
×
181
        commandlet.run();
×
182
      });
×
183
    } else {
184
      LOG.debug("Build command not set. Skipping build for repository.");
3✔
185
      return true;
2✔
186
    }
187
  }
188

189
  private void importRepository(RepositoryConfig repositoryConfig, Path repositoryPath, String repositoryId) {
190

191
    Set<String> imports = repositoryConfig.imports();
3✔
192
    if ((imports == null) || imports.isEmpty()) {
5!
193
      LOG.debug("Repository {} has no IDE configured for import.", repositoryId);
4✔
194
      return;
1✔
195
    }
196
    for (String ide : imports) {
10✔
197
      Step step = this.context.newStep("Importing repository " + repositoryId + " into " + ide);
7✔
198
      step.run(() -> {
9✔
199
        ToolCommandlet commandlet = this.context.getCommandletManager().getToolCommandlet(ide);
6✔
200
        if (commandlet == null) {
2!
201
          String displayName = (ide == null || ide.isBlank()) ? "<empty>" : "'" + ide + "'";
×
202
          step.error("Cannot import repository '{}'. Required IDE '{}' not found. Please check your repository's imports configuration.", repositoryId,
×
203
              displayName);
204
        } else if (commandlet instanceof IdeToolCommandlet ideCommandlet) {
6!
205
          ideCommandlet.importRepository(repositoryPath);
4✔
206
        } else {
207
          step.error("Repository {} has import {} configured that is not an IDE!", repositoryId, ide);
×
208
        }
209
      });
1✔
210
    }
1✔
211
  }
1✔
212
}
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