Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ This file documents all notable changes to https://github.com/devonfw/IDEasy[IDE

Release with new features and bugfixes:

* https://github.com/devonfw/IDEasy/pull/1569[#1569] Support comma-separated list of workspaces to clone repositories
* https://github.com/devonfw/IDEasy/issues/536[#536]: IDEasy complete tries to match commandlets twice
* https://github.com/devonfw/IDEasy/issues/1510[#1510]: Make icd more usable
* https://github.com/devonfw/IDEasy/issues/1169[#1169]: mklink fails if link already exists
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,27 +96,31 @@ private void doImportRepository(Path repositoryFile, boolean forceMode, String r
return;
}
this.context.debug("Repository configuration: {}", repositoryConfig);
Path repositoryPath = getRepositoryPath(repositoryConfig, repositoryId);
if (Files.isDirectory(repositoryPath.resolve(GitContext.GIT_FOLDER))) {
this.context.info("Repository {} already exists at {}", repositoryId, repositoryPath);
if (!(this.context.isForceMode() || this.context.isForceRepositories())) {
this.context.info("Ignoring repository {} - use --force or --force-repositories to rerun setup.", repositoryId);
return;
}
}
List<Path> repositoryPaths = getRepositoryPaths(repositoryConfig, repositoryId);
Path ideStatusDir = this.context.getIdeHome().resolve(IdeContext.FOLDER_DOT_IDE);
this.context.getFileAccess().mkdirs(ideStatusDir);
Path repositoryCreatedStatusFile = ideStatusDir.resolve("repository." + repositoryId);
if (Files.exists(repositoryCreatedStatusFile)) {
if (!(this.context.isForceMode() || this.context.isForceRepositories())) {
this.context.info("Ignoring repository {} because it was already setup before - use --force or --force-repositories for recreation.", repository);
return;

for (Path repositoryPath : repositoryPaths) {
String workspaceName = getWorkspaceName(repositoryPath);
if (Files.isDirectory(repositoryPath.resolve(GitContext.GIT_FOLDER))) {
this.context.info("Repository {} already exists in workspace {} at {}", repositoryId, workspaceName, repositoryPath);
if (!(this.context.isForceMode() || this.context.isForceRepositories())) {
this.context.info("Ignoring repository {} in workspace {} - use --force or --force-repositories to rerun setup.", repositoryId, workspaceName);
continue;
}
}
Path repositoryCreatedStatusFile = ideStatusDir.resolve("repository." + repositoryId + "." + workspaceName);
if (Files.exists(repositoryCreatedStatusFile)) {
if (!(this.context.isForceMode() || this.context.isForceRepositories())) {
this.context.info("Ignoring repository {} in workspace {} because it was already setup before - use --force or --force-repositories for recreation.", repositoryId, workspaceName);
continue;
}
}
boolean success = cloneOrPullRepository(repositoryPath, gitUrl, repositoryCreatedStatusFile);
if (success) {
buildRepository(repositoryConfig, repositoryPath);
importRepository(repositoryConfig, repositoryPath, repositoryId);
}
}
boolean success = cloneOrPullRepository(repositoryPath, gitUrl, repositoryCreatedStatusFile);
if (success) {
buildRepository(repositoryConfig, repositoryPath);
importRepository(repositoryConfig, repositoryPath, repositoryId);
}
}

Expand All @@ -130,17 +134,27 @@ private boolean cloneOrPullRepository(Path repositoryPath, GitUrl gitUrl, Path r
});
}

private Path getRepositoryPath(RepositoryConfig repositoryConfig, String repositoryId) {
String workspace = repositoryConfig.workspace();
if (workspace == null) {
workspace = IdeContext.WORKSPACE_MAIN;
private List<Path> getRepositoryPaths(RepositoryConfig repositoryConfig, String repositoryId) {
Set<String> workspaces = repositoryConfig.workspaces();
if (workspaces == null || workspaces.isEmpty()) {
workspaces = Set.of(IdeContext.WORKSPACE_MAIN);
}
Path workspacePath = this.context.getIdeHome().resolve(IdeContext.FOLDER_WORKSPACES).resolve(workspace);
String repositoryRelativePath = repositoryConfig.path();
if (repositoryRelativePath == null) {
repositoryRelativePath = repositoryId;
}
return workspacePath.resolve(repositoryRelativePath);
List<Path> repositoryPaths = new java.util.ArrayList<>();
for (String workspace : workspaces) {
Path workspacePath = this.context.getIdeHome().resolve(IdeContext.FOLDER_WORKSPACES).resolve(workspace);
repositoryPaths.add(workspacePath.resolve(repositoryRelativePath));
}
return repositoryPaths;
}

private String getWorkspaceName(Path repositoryPath) {
Path workspacesFolder = this.context.getIdeHome().resolve(IdeContext.FOLDER_WORKSPACES);
Path relativePath = workspacesFolder.relativize(repositoryPath);
return relativePath.getName(0).toString();
}

private boolean buildRepository(RepositoryConfig repositoryConfig, Path repositoryPath) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
*
* @param path Path into which the project is cloned. This path is relative to the workspace.
* @param workingSets The working sets associated with the repository.
* @param workspace Workspace to use for checkout and import. Default is main.
* @param workspaces Workspaces to use for checkout and import. Supports comma-separated values. Default is main.
* @param gitUrl Git URL to use for cloning the project.
* @param gitBranch Git branch to checkout. Git default branch is default.
* @param buildPath The build path for the repository.
Expand All @@ -23,7 +23,7 @@
public record RepositoryConfig(
String path,
String workingSets,
String workspace,
Set<String> workspaces,
String gitUrl,
String gitBranch,
String buildPath,
Expand All @@ -37,7 +37,7 @@ public record RepositoryConfig(
/** {@link RepositoryProperties#getProperty(String) Property name} for {@link #workingSets()}. */
public static final String PROPERTY_WORKING_SETS = "workingsets";

/** {@link RepositoryProperties#getProperty(String) Property name} for {@link #workspace()}. */
/** {@link RepositoryProperties#getProperty(String) Property name} for {@link #workspaces()}. */
public static final String PROPERTY_WORKSPACE = "workspace";

/** {@link RepositoryProperties#getProperty(String) Property name} for {@link #gitUrl()}. */
Expand Down Expand Up @@ -82,9 +82,10 @@ public static RepositoryConfig loadProperties(Path filePath, IdeContext context)
RepositoryProperties properties = new RepositoryProperties(filePath, context);

Set<String> importsSet = properties.getImports();
Set<String> workspacesSet = properties.getWorkspaces();

return new RepositoryConfig(properties.getProperty(PROPERTY_PATH), properties.getProperty(PROPERTY_WORKING_SETS),
properties.getProperty(PROPERTY_WORKSPACE), properties.getProperty(PROPERTY_GIT_URL, true), properties.getProperty(PROPERTY_GIT_BRANCH),
workspacesSet, properties.getProperty(PROPERTY_GIT_URL, true), properties.getProperty(PROPERTY_GIT_BRANCH),
properties.getProperty(PROPERTY_BUILD_PATH), properties.getProperty(PROPERTY_BUILD_CMD), importsSet,
parseBoolean(properties.getProperty(PROPERTY_ACTIVE).trim()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,19 @@ public Set<String> getImports() {
}
}

/**
* @return the workspaces where to clone the repository. Returns a set containing "main" as default if not specified.
*/
public Set<String> getWorkspaces() {

String workspaceProperty = this.properties.getProperty(RepositoryConfig.PROPERTY_WORKSPACE);
if (workspaceProperty != null) {
if (workspaceProperty.isEmpty()) {
return Set.of(IdeContext.WORKSPACE_MAIN);
}
return Arrays.stream(workspaceProperty.split(",")).map(String::trim).collect(Collectors.toUnmodifiableSet());
}
return Set.of(IdeContext.WORKSPACE_MAIN);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,65 @@ public void testSetupSpecificRepositoryWithForceOption() {
assertThat(this.context).logAtSuccess().hasMessage("Successfully ended step 'Setup of repository test'.");
}

@Test
public void testSetupRepositoryWithMultipleWorkspaces() {

// arrange
String workspace1 = "workspace1";
String workspace2 = "workspace2";
RepositoryCommandlet rc = this.context.getCommandletManager().getCommandlet(RepositoryCommandlet.class);
this.properties.setProperty("workspace", workspace1 + "," + workspace2);
saveProperties();
rc.repository.setValueAsString("test", this.context);

// act
rc.run();

// assert
assertThat(context.getIdeHome().resolve(IdeContext.FOLDER_WORKSPACES).resolve(workspace1).resolve(TEST_REPO)).isDirectory();
assertThat(context.getIdeHome().resolve(IdeContext.FOLDER_WORKSPACES).resolve(workspace2).resolve(TEST_REPO)).isDirectory();
assertThat(this.context).logAtSuccess().hasMessage("Successfully ended step 'Setup of repository test'.");
}

@Test
public void testSetupRepositoryWithMultipleWorkspacesWithSpaces() {

// arrange
String workspace1 = "workspace1";
String workspace2 = "workspace2";
String workspace3 = "workspace3";
RepositoryCommandlet rc = this.context.getCommandletManager().getCommandlet(RepositoryCommandlet.class);
this.properties.setProperty("workspace", workspace1 + " , " + workspace2 + ", " + workspace3);
saveProperties();
rc.repository.setValueAsString("test", this.context);

// act
rc.run();

// assert
assertThat(context.getIdeHome().resolve(IdeContext.FOLDER_WORKSPACES).resolve(workspace1).resolve(TEST_REPO)).isDirectory();
assertThat(context.getIdeHome().resolve(IdeContext.FOLDER_WORKSPACES).resolve(workspace2).resolve(TEST_REPO)).isDirectory();
assertThat(context.getIdeHome().resolve(IdeContext.FOLDER_WORKSPACES).resolve(workspace3).resolve(TEST_REPO)).isDirectory();
assertThat(this.context).logAtSuccess().hasMessage("Successfully ended step 'Setup of repository test'.");
}

@Test
public void testSetupRepositoryWithEmptyWorkspaceDefaultsToMain() {

// arrange
RepositoryCommandlet rc = this.context.getCommandletManager().getCommandlet(RepositoryCommandlet.class);
this.properties.setProperty("workspace", "");
saveProperties();
rc.repository.setValueAsString("test", this.context);

// act
rc.run();

// assert
assertThat(context.getIdeHome().resolve(IdeContext.FOLDER_WORKSPACES).resolve(IdeContext.WORKSPACE_MAIN).resolve(TEST_REPO)).isDirectory();
assertThat(this.context).logAtSuccess().hasMessage("Successfully ended step 'Setup of repository test'.");
}

private void saveProperties() {

FileAccess fileAccess = this.context.getFileAccess();
Expand Down
6 changes: 5 additions & 1 deletion documentation/repository.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ import=eclipse
active=true
```

NOTE: To clone a repository into multiple workspaces, simply provide comma-separated workspace names like `workspace=main,test,feature`.
This will clone the repository into each specified workspace during project setup.

.Variables of repository import
[options="header"]
|===
Expand All @@ -32,7 +35,8 @@ This path is relative to the workspace.
|`working sets`|e.g. `ws1,ws2`|(optional) This will create working sets (in eclipse).
Each module (eclipse project) of this project will be part of all these working sets.
Working sets will be automatically created if necessary.
|`workspace`|`main`|Workspace to use for checkout and import.
|`workspace`|`main`|Workspace(s) to use for checkout and import.
Supports comma-separated values to clone the repository into multiple workspaces (e.g., `main,test,feature`).
Default is `main`.
|`git_url`|e.g. `http://github.com/someorg/someproject`|(required) Git URL to use for cloning the project.
|`git_branch`|e.g. `develop`|(optional) Git branch to checkout.
Expand Down