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

devonfw / IDEasy / 20330220431

18 Dec 2025 07:58AM UTC coverage: 70.087% (+0.03%) from 70.061%
20330220431

push

github

hohwille
#1596: Add support for comma-separated workspace values in repository configuration (#1569)

Co-authored-by: Malte Brunnlieb <malte.brunnlieb@capgemini.com>
Co-authored-by: Malte Brunnlieb <maybeec@users.noreply.github.com>

3980 of 6253 branches covered (63.65%)

Branch coverage included in aggregate %.

10186 of 13959 relevant lines covered (72.97%)

3.15 hits per line

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

63.59
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 com.devonfw.tools.ide.commandlet.Commandlet;
9
import com.devonfw.tools.ide.context.IdeContext;
10
import com.devonfw.tools.ide.git.GitContext;
11
import com.devonfw.tools.ide.git.GitUrl;
12
import com.devonfw.tools.ide.io.FileAccess;
13
import com.devonfw.tools.ide.property.RepositoryProperty;
14
import com.devonfw.tools.ide.step.Step;
15
import com.devonfw.tools.ide.tool.ToolCommandlet;
16
import com.devonfw.tools.ide.tool.ide.IdeToolCommandlet;
17

18
/**
19
 * {@link Commandlet} to setup one or multiple GIT repositories for development.
20
 */
21
public class RepositoryCommandlet extends Commandlet {
22

23
  /** the repository to setup. */
24
  public final RepositoryProperty repository;
25

26
  /**
27
   * The constructor.
28
   *
29
   * @param context the {@link IdeContext}.
30
   */
31
  public RepositoryCommandlet(IdeContext context) {
32

33
    super(context);
3✔
34
    addKeyword(getName());
4✔
35
    addKeyword("setup");
3✔
36
    this.repository = add(new RepositoryProperty("", false, "repository"));
11✔
37
  }
1✔
38

39
  @Override
40
  public String getName() {
41

42
    return "repository";
2✔
43
  }
44

45
  @Override
46
  public void run() {
47

48
    Path repositoryFile = this.repository.getValue();
5✔
49

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

69
  private void doImportRepository(Path repositoryFile, boolean forceMode) {
70

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

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

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

144
  private boolean cloneOrPullRepository(Path repositoryPath, GitUrl gitUrl, Path repositoryCreatedStatusFile) {
145

146
    FileAccess fileAccess = this.context.getFileAccess();
4✔
147
    return this.context.newStep("Clone or pull repository").run(() -> {
12✔
148
      fileAccess.mkdirs(repositoryPath);
3✔
149
      this.context.getGitContext().pullOrClone(gitUrl, repositoryPath);
6✔
150
      fileAccess.touch(repositoryCreatedStatusFile);
3✔
151
    });
1✔
152
  }
153

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

183
  private void importRepository(RepositoryConfig repositoryConfig, Path repositoryPath, String repositoryId) {
184

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