Skip to content

Commit e122f21

Browse files
authored
#100: implement repository commandlet (#143)
1 parent 810c616 commit e122f21

16 files changed

+473
-52
lines changed

cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ public CommandletManagerImpl(IdeContext context) {
6767
add(new EditionSetCommandlet(context));
6868
add(new EditionListCommandlet(context));
6969
add(new VersionCommandlet(context));
70+
add(new RepositoryCommandlet(context));
7071
add(new Gh(context));
7172
add(new Helm(context));
7273
add(new Java(context));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
package com.devonfw.tools.ide.commandlet;
2+
3+
import com.devonfw.tools.ide.context.IdeContext;
4+
import com.devonfw.tools.ide.property.PathProperty;
5+
import com.devonfw.tools.ide.property.RepositoryProperty;
6+
import com.devonfw.tools.ide.tool.ToolCommandlet;
7+
8+
import java.io.FileInputStream;
9+
import java.io.IOException;
10+
import java.io.InputStream;
11+
import java.nio.file.Files;
12+
import java.nio.file.Path;
13+
import java.util.ArrayList;
14+
import java.util.List;
15+
import java.util.Properties;
16+
17+
import static com.devonfw.tools.ide.commandlet.RepositoryConfig.loadProperties;
18+
19+
/**
20+
* {@link Commandlet} to setup one or multiple GIT repositories for development.
21+
*/
22+
public class RepositoryCommandlet extends Commandlet {
23+
24+
/** the repository to setup. */
25+
public final RepositoryProperty repository;
26+
27+
/**
28+
* The constructor.
29+
*
30+
* @param context the {@link IdeContext}.
31+
*/
32+
public RepositoryCommandlet(IdeContext context) {
33+
34+
super(context);
35+
addKeyword(getName());
36+
addKeyword("setup");
37+
this.repository = add(new RepositoryProperty("", false, "repository"));
38+
}
39+
40+
@Override
41+
public String getName() {
42+
43+
return "repository";
44+
}
45+
46+
@Override
47+
public void run() {
48+
49+
Path repositoryFile = repository.getValue();
50+
51+
if (repositoryFile != null) {
52+
// Handle the case when a specific repository is provided
53+
doImportRepository(repositoryFile, true);
54+
} else {
55+
// If no specific repository is provided, check for repositories folder
56+
Path repositoriesPath = this.context.getSettingsPath().resolve(IdeContext.FOLDER_REPOSITORIES);
57+
Path legacyRepositoriesPath = this.context.getSettingsPath().resolve(IdeContext.FOLDER_LEGACY_REPOSITORIES);
58+
Path repositories;
59+
if (Files.exists(repositoriesPath)) {
60+
repositories = repositoriesPath;
61+
} else if (Files.exists(legacyRepositoriesPath)) {
62+
repositories = legacyRepositoriesPath;
63+
} else {
64+
this.context.warning("Cannot find repositories folder nor projects folder.");
65+
return;
66+
}
67+
68+
List <Path> propertiesFiles = this.context.getFileAccess().listChildren(repositories,
69+
path -> path.getFileName().toString().endsWith(".properties"));
70+
71+
boolean forceMode = this.context.isForceMode();
72+
for (Path propertiesFile : propertiesFiles) {
73+
doImportRepository(propertiesFile, forceMode);
74+
}
75+
}
76+
}
77+
78+
private void doImportRepository(Path repositoryFile, boolean forceMode) {
79+
80+
this.context.info("Importing repository from {} ...", repositoryFile.getFileName().toString());
81+
RepositoryConfig repositoryConfig = loadProperties(repositoryFile);
82+
83+
if (!repositoryConfig.active()) {
84+
this.context.info("Repository is not active by default.");
85+
if (forceMode) {
86+
this.context.info("Repository setup is forced, hence proceeding ...");
87+
} else {
88+
this.context.info("Skipping repository - use force (-f) to setup all repositories ...");
89+
return;
90+
}
91+
}
92+
93+
String repository = repositoryConfig.path();
94+
String gitUrl = repositoryConfig.gitUrl();
95+
if (repository == null || "".equals(repository) || gitUrl == null || "".equals(gitUrl)) {
96+
this.context.warning("Invalid repository configuration {} - both 'path' and 'git-url' have to be defined."
97+
, repositoryFile.getFileName().toString());
98+
return;
99+
}
100+
101+
this.context.debug(repositoryConfig.toString());
102+
103+
String workspace = repositoryConfig.workspace() != null ? repositoryConfig.workspace() : "main";
104+
Path workspacePath = this.context.getIdeHome().resolve("workspaces").resolve(workspace);
105+
this.context.getFileAccess().mkdirs(workspacePath);
106+
107+
Path repositoryPath = workspacePath.resolve(repository);
108+
this.context.getGitContext().pullOrClone(gitUrl, repositoryConfig.gitBranch(), repositoryPath);
109+
110+
String buildCmd = repositoryConfig.buildCmd();
111+
this.context.debug("Building repository with ide command: {}", buildCmd);
112+
if (buildCmd != null && !buildCmd.isEmpty()) {
113+
String[] command = buildCmd.split("\\s+");
114+
ToolCommandlet commandlet = this.context.getCommandletManager().getToolCommandlet(command[0]);
115+
List<String> args = new ArrayList<>(command.length - 1);
116+
for (int i = 1; i < command.length; i++) {
117+
args.add(command[i]);
118+
}
119+
commandlet.arguments.setValue(args);
120+
commandlet.run();
121+
} else {
122+
this.context.info("Build command not set. Skipping build for repository.");
123+
}
124+
125+
}
126+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package com.devonfw.tools.ide.commandlet;
2+
3+
import java.io.FileInputStream;
4+
import java.io.IOException;
5+
import java.io.InputStream;
6+
import java.nio.file.Path;
7+
import java.util.Collections;
8+
import java.util.Properties;
9+
import java.util.Set;
10+
11+
/**
12+
* Represents the configuration of a repository to be used by the {@link RepositoryCommandlet}.
13+
*
14+
* @param path Path into which the project is cloned. This path is relative to the workspace.
15+
* @param workingSets The working sets associated with the repository.
16+
* @param workspace Workspace to use for checkout and import. Default is main.
17+
* @param gitUrl Git URL to use for cloning the project.
18+
* @param gitBranch Git branch to checkout. Git default branch is default.
19+
* @param buildPath The build path for the repository.
20+
* @param buildCmd The command to invoke to build the repository after clone or pull. If omitted no build is triggered.
21+
* @param imports list of IDEs where the repository will be imported to.
22+
* @param active {@code true} to setup the repository during setup, {@code false} to skip.
23+
*/
24+
public record RepositoryConfig(
25+
String path,
26+
String workingSets,
27+
String workspace,
28+
String gitUrl,
29+
String gitBranch,
30+
String buildPath,
31+
String buildCmd,
32+
Set<String> imports,
33+
boolean active) {
34+
public static RepositoryConfig loadProperties(Path filePath) {
35+
36+
Properties properties = new Properties();
37+
try (InputStream input = new FileInputStream(filePath.toString())) {
38+
properties.load(input);
39+
} catch (IOException e) {
40+
throw new IllegalStateException("Failed to read file: " + filePath, e);
41+
}
42+
43+
Set<String> importsSet = getImports(properties);
44+
45+
return new RepositoryConfig(properties.getProperty("path"), properties.getProperty("workingsets"),
46+
properties.getProperty("workspace"), properties.getProperty("git_url"), properties.getProperty("git_branch"),
47+
properties.getProperty(("build_path")), properties.getProperty("build_cmd"), importsSet,
48+
Boolean.parseBoolean(properties.getProperty("active").trim()));
49+
}
50+
51+
private static Set<String> getImports(Properties properties) {
52+
53+
String importProperty = properties.getProperty("import");
54+
if (importProperty != null && !importProperty.isEmpty()) {
55+
return Set.of(importProperty.split("\\s*,\\s*"));
56+
}
57+
58+
String legacyImportProperty = properties.getProperty("eclipse");
59+
if ("import".equals(legacyImportProperty)) {
60+
return Set.of("eclipse");
61+
} else {
62+
return Collections.emptySet();
63+
}
64+
}
65+
}

cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -497,7 +497,7 @@ public UrlMetadata getUrls() {
497497

498498
if (this.urlMetadata == null) {
499499
if (!isTest()) {
500-
this.getGitContext().pullOrFetchAndResetIfNeeded(IDE_URLS_GIT, this.urlsPath, "origin", "master");
500+
this.getGitContext().pullOrFetchAndResetIfNeeded(IDE_URLS_GIT, "master", this.urlsPath, "origin");
501501
}
502502
this.urlMetadata = new UrlMetadata(this);
503503
}

cli/src/main/java/com/devonfw/tools/ide/context/GitContext.java

+31-23
Original file line numberDiff line numberDiff line change
@@ -13,63 +13,71 @@ public interface GitContext extends IdeLogger {
1313
* Checks if the Git repository in the specified target folder needs an update by inspecting the modification time of
1414
* a magic file.
1515
*
16-
* @param repoUrl the git remote URL to clone from. May be suffixed with a hash-sign ('#') followed by the branch name
17-
* to check-out.
16+
* @param repoUrl the git remote URL to clone from.
1817
* @param targetRepository the {@link Path} to the target folder where the git repository should be cloned or pulled.
19-
* It is not the parent directory where git will by default create a sub-folder by default on clone but the *
20-
* final folder that will contain the ".git" subfolder.
18+
* It is not the parent directory where git will by default create a sub-folder by default on clone but the * final
19+
* folder that will contain the ".git" subfolder.
2120
*/
22-
void pullOrCloneIfNeeded(String repoUrl, Path targetRepository);
21+
void pullOrCloneIfNeeded(String repoUrl, String branch, Path targetRepository);
2322

2423
/**
2524
* Attempts a git pull and reset if required.
2625
*
27-
* @param repoUrl the git remote URL to clone from. May be suffixed with a hash-sign ('#') followed by the branch name
28-
* to check-out.
26+
* @param repoUrl the git remote URL to clone from.
27+
* @param branch the branch name e.g. master.
2928
* @param targetRepository the {@link Path} to the target folder where the git repository should be cloned or pulled.
30-
* It is not the parent directory where git will by default create a sub-folder by default on clone but the *
31-
* final folder that will contain the ".git" subfolder.
29+
* It is not the parent directory where git will by default create a sub-folder by default on clone but the * final
30+
* folder that will contain the ".git" subfolder.
3231
* @param remoteName the remote name e.g. origin.
33-
* @param branchName the branch name e.g. master.
3432
*/
35-
void pullOrFetchAndResetIfNeeded(String repoUrl, Path targetRepository, String remoteName, String branchName);
33+
void pullOrFetchAndResetIfNeeded(String repoUrl, String branch, Path targetRepository, String remoteName);
3634

3735
/**
3836
* Runs a git pull or a git clone.
3937
*
40-
* @param gitRepoUrl the git remote URL to clone from. May be suffixed with a hash-sign ('#') followed by the branch
41-
* name to check-out.
38+
* @param gitRepoUrl the git remote URL to clone from.
4239
* @param targetRepository the {@link Path} to the target folder where the git repository should be cloned or pulled.
43-
* It is not the parent directory where git will by default create a sub-folder by default on clone but the *
44-
* final folder that will contain the ".git" subfolder.
40+
* It is not the parent directory where git will by default create a sub-folder by default on clone but the * final
41+
* folder that will contain the ".git" subfolder.
4542
*/
4643
void pullOrClone(String gitRepoUrl, Path targetRepository);
4744

45+
/**
46+
* Runs a git pull or a git clone.
47+
*
48+
* @param gitRepoUrl the git remote URL to clone from.
49+
* @param branch the branch name e.g. master.
50+
* @param targetRepository the {@link Path} to the target folder where the git repository should be cloned or pulled.
51+
* It is not the parent directory where git will by default create a sub-folder by default on clone but the * final
52+
* folder that will contain the ".git" subfolder.
53+
*/
54+
void pullOrClone(String gitRepoUrl, String branch, Path targetRepository);
55+
4856
/**
4957
* Runs a git clone. Throws a CliException if in offline mode.
5058
*
5159
* @param gitRepoUrl the {@link GitUrl} to use for the repository URL.
5260
* @param targetRepository the {@link Path} to the target folder where the git repository should be cloned or pulled.
53-
* It is not the parent directory where git will by default create a sub-folder by default on clone but the *
54-
* final folder that will contain the ".git" subfolder.
61+
* It is not the parent directory where git will by default create a sub-folder by default on clone but the * final
62+
* folder that will contain the ".git" subfolder.
5563
*/
5664
void clone(GitUrl gitRepoUrl, Path targetRepository);
5765

5866
/**
5967
* Runs a git pull.
6068
*
6169
* @param targetRepository the {@link Path} to the target folder where the git repository should be cloned or pulled.
62-
* It is not the parent directory where git will by default create a sub-folder by default on clone but the *
63-
* final folder that will contain the ".git" subfolder.
70+
* It is not the parent directory where git will by default create a sub-folder by default on clone but the * final
71+
* folder that will contain the ".git" subfolder.
6472
*/
6573
void pull(Path targetRepository);
6674

6775
/**
6876
* Runs a git reset if files were modified.
6977
*
7078
* @param targetRepository the {@link Path} to the target folder where the git repository should be cloned or pulled.
71-
* It is not the parent directory where git will by default create a sub-folder by default on clone but the *
72-
* final folder that will contain the ".git" subfolder.
79+
* It is not the parent directory where git will by default create a sub-folder by default on clone but the * final
80+
* folder that will contain the ".git" subfolder.
7381
* @param remoteName the remote server name.
7482
* @param branchName the name of the branch.
7583
*/
@@ -79,8 +87,8 @@ public interface GitContext extends IdeLogger {
7987
* Runs a git cleanup if untracked files were found.
8088
*
8189
* @param targetRepository the {@link Path} to the target folder where the git repository should be cloned or pulled.
82-
* It is not the parent directory where git will by default create a sub-folder by default on clone but the *
83-
* final folder that will contain the ".git" subfolder.
90+
* It is not the parent directory where git will by default create a sub-folder by default on clone but the * final
91+
* folder that will contain the ".git" subfolder.
8492
*/
8593
void cleanup(Path targetRepository);
8694

0 commit comments

Comments
 (0)