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

devonfw / IDEasy / 19338149779

13 Nov 2025 04:17PM UTC coverage: 69.032% (+0.1%) from 68.927%
19338149779

Pull #1570

github

web-flow
Merge 5e67f12db into 669ec08b9
Pull Request #1570: Handle unsupported IDE import gracefully

3502 of 5557 branches covered (63.02%)

Branch coverage included in aggregate %.

9173 of 12804 relevant lines covered (71.64%)

3.15 hits per line

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

68.79
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
    Path repositoryPath = getRepositoryPath(repositoryConfig, repositoryId);
5✔
100
    if (Files.isDirectory(repositoryPath.resolve(GitContext.GIT_FOLDER))) {
7!
101
      this.context.info("Repository {} already exists at {}", repositoryId, repositoryPath);
×
102
      if (!(this.context.isForceMode() || this.context.isForceRepositories())) {
×
103
        this.context.info("Ignoring repository {} - use --force or --force-repositories to rerun setup.", repositoryId);
×
104
        return;
×
105
      }
106
    }
107
    Path ideStatusDir = this.context.getIdeHome().resolve(IdeContext.FOLDER_DOT_IDE);
6✔
108
    this.context.getFileAccess().mkdirs(ideStatusDir);
5✔
109
    Path repositoryCreatedStatusFile = ideStatusDir.resolve("repository." + repositoryId);
5✔
110
    if (Files.exists(repositoryCreatedStatusFile)) {
5!
111
      if (!(this.context.isForceMode() || this.context.isForceRepositories())) {
×
112
        this.context.info("Ignoring repository {} because it was already setup before - use --force or --force-repositories for recreation.", repository);
×
113
        return;
×
114
      }
115
    }
116
    boolean success = cloneOrPullRepository(repositoryPath, gitUrl, repositoryCreatedStatusFile);
6✔
117
    if (success) {
2!
118
      buildRepository(repositoryConfig, repositoryPath);
5✔
119
      importRepository(repositoryConfig, repositoryPath, repositoryId);
5✔
120
    }
121
  }
1✔
122

123
  private boolean cloneOrPullRepository(Path repositoryPath, GitUrl gitUrl, Path repositoryCreatedStatusFile) {
124

125
    FileAccess fileAccess = this.context.getFileAccess();
4✔
126
    return this.context.newStep("Clone or pull repository").run(() -> {
12✔
127
      fileAccess.mkdirs(repositoryPath);
3✔
128
      this.context.getGitContext().pullOrClone(gitUrl, repositoryPath);
6✔
129
      fileAccess.touch(repositoryCreatedStatusFile);
3✔
130
    });
1✔
131
  }
132

133
  private Path getRepositoryPath(RepositoryConfig repositoryConfig, String repositoryId) {
134
    String workspace = repositoryConfig.workspace();
3✔
135
    if (workspace == null) {
2!
136
      workspace = IdeContext.WORKSPACE_MAIN;
×
137
    }
138
    Path workspacePath = this.context.getIdeHome().resolve(IdeContext.FOLDER_WORKSPACES).resolve(workspace);
8✔
139
    String repositoryRelativePath = repositoryConfig.path();
3✔
140
    if (repositoryRelativePath == null) {
2✔
141
      repositoryRelativePath = repositoryId;
2✔
142
    }
143
    return workspacePath.resolve(repositoryRelativePath);
4✔
144
  }
145

146
  private boolean buildRepository(RepositoryConfig repositoryConfig, Path repositoryPath) {
147
    String buildCmd = repositoryConfig.buildCmd();
3✔
148
    if (buildCmd != null && !buildCmd.isEmpty()) {
2!
149
      return this.context.newStep("Build repository via: " + buildCmd).run(() -> {
×
150
        String[] command = buildCmd.split("\\s+");
×
151
        ToolCommandlet commandlet = this.context.getCommandletManager().getRequiredToolCommandlet(command[0]);
×
152
        commandlet.reset();
×
153
        for (int i = 1; i < command.length; i++) {
×
154
          commandlet.arguments.addValue(command[i]);
×
155
        }
156
        Path executionDirectory = repositoryPath;
×
157
        String path = repositoryConfig.buildPath();
×
158
        if (path != null) {
×
159
          executionDirectory = executionDirectory.resolve(path);
×
160
        }
161
        commandlet.setExecutionDirectory(executionDirectory);
×
162
        commandlet.run();
×
163
      });
×
164
    } else {
165
      this.context.debug("Build command not set. Skipping build for repository.");
4✔
166
      return true;
2✔
167
    }
168
  }
169

170
  private void importRepository(RepositoryConfig repositoryConfig, Path repositoryPath, String repositoryId) {
171

172
    Set<String> imports = repositoryConfig.imports();
3✔
173
    if ((imports == null) || imports.isEmpty()) {
5!
174
      this.context.debug("Repository {} has no IDE configured for import.", repositoryId);
10✔
175
      return;
1✔
176
    }
177
    for (String ide : imports) {
10✔
178
      Step step = this.context.newStep("Importing repository " + repositoryId + " into " + ide);
7✔
179
      step.run(() -> {
9✔
180
        ToolCommandlet commandlet = this.context.getCommandletManager().getRequiredToolCommandlet(ide);
6✔
181
        if (commandlet instanceof IdeToolCommandlet ideCommandlet) {
6!
182
          try {
183
            ideCommandlet.importRepository(repositoryPath);
×
184
          } catch (UnsupportedOperationException e) {
1✔
185
            this.context.warning("Repository import is not supported for IDE {}. Skipping automatic import of repository {}.", ide, repositoryId);
14✔
186
            step.success();
2✔
187
          }
1✔
188
        } else {
189
          step.error("Repository {} has import {} configured that is not an IDE!", repositoryId, ide);
×
190
        }
191
      });
1✔
192
    }
1✔
193
  }
1✔
194
}
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