Skip to content

Commit

Permalink
#100: implement repository commandlet (#143)
Browse files Browse the repository at this point in the history
  • Loading branch information
salimbouch authored Mar 19, 2024
1 parent 810c616 commit e122f21
Show file tree
Hide file tree
Showing 16 changed files with 473 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ public CommandletManagerImpl(IdeContext context) {
add(new EditionSetCommandlet(context));
add(new EditionListCommandlet(context));
add(new VersionCommandlet(context));
add(new RepositoryCommandlet(context));
add(new Gh(context));
add(new Helm(context));
add(new Java(context));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package com.devonfw.tools.ide.commandlet;

import com.devonfw.tools.ide.context.IdeContext;
import com.devonfw.tools.ide.property.PathProperty;
import com.devonfw.tools.ide.property.RepositoryProperty;
import com.devonfw.tools.ide.tool.ToolCommandlet;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import static com.devonfw.tools.ide.commandlet.RepositoryConfig.loadProperties;

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

/** the repository to setup. */
public final RepositoryProperty repository;

/**
* The constructor.
*
* @param context the {@link IdeContext}.
*/
public RepositoryCommandlet(IdeContext context) {

super(context);
addKeyword(getName());
addKeyword("setup");
this.repository = add(new RepositoryProperty("", false, "repository"));
}

@Override
public String getName() {

return "repository";
}

@Override
public void run() {

Path repositoryFile = repository.getValue();

if (repositoryFile != null) {
// Handle the case when a specific repository is provided
doImportRepository(repositoryFile, true);
} else {
// If no specific repository is provided, check for repositories folder
Path repositoriesPath = this.context.getSettingsPath().resolve(IdeContext.FOLDER_REPOSITORIES);
Path legacyRepositoriesPath = this.context.getSettingsPath().resolve(IdeContext.FOLDER_LEGACY_REPOSITORIES);
Path repositories;
if (Files.exists(repositoriesPath)) {
repositories = repositoriesPath;
} else if (Files.exists(legacyRepositoriesPath)) {
repositories = legacyRepositoriesPath;
} else {
this.context.warning("Cannot find repositories folder nor projects folder.");
return;
}

List <Path> propertiesFiles = this.context.getFileAccess().listChildren(repositories,
path -> path.getFileName().toString().endsWith(".properties"));

boolean forceMode = this.context.isForceMode();
for (Path propertiesFile : propertiesFiles) {
doImportRepository(propertiesFile, forceMode);
}
}
}

private void doImportRepository(Path repositoryFile, boolean forceMode) {

this.context.info("Importing repository from {} ...", repositoryFile.getFileName().toString());
RepositoryConfig repositoryConfig = loadProperties(repositoryFile);

if (!repositoryConfig.active()) {
this.context.info("Repository is not active by default.");
if (forceMode) {
this.context.info("Repository setup is forced, hence proceeding ...");
} else {
this.context.info("Skipping repository - use force (-f) to setup all repositories ...");
return;
}
}

String repository = repositoryConfig.path();
String gitUrl = repositoryConfig.gitUrl();
if (repository == null || "".equals(repository) || gitUrl == null || "".equals(gitUrl)) {
this.context.warning("Invalid repository configuration {} - both 'path' and 'git-url' have to be defined."
, repositoryFile.getFileName().toString());
return;
}

this.context.debug(repositoryConfig.toString());

String workspace = repositoryConfig.workspace() != null ? repositoryConfig.workspace() : "main";
Path workspacePath = this.context.getIdeHome().resolve("workspaces").resolve(workspace);
this.context.getFileAccess().mkdirs(workspacePath);

Path repositoryPath = workspacePath.resolve(repository);
this.context.getGitContext().pullOrClone(gitUrl, repositoryConfig.gitBranch(), repositoryPath);

String buildCmd = repositoryConfig.buildCmd();
this.context.debug("Building repository with ide command: {}", buildCmd);
if (buildCmd != null && !buildCmd.isEmpty()) {
String[] command = buildCmd.split("\\s+");
ToolCommandlet commandlet = this.context.getCommandletManager().getToolCommandlet(command[0]);
List<String> args = new ArrayList<>(command.length - 1);
for (int i = 1; i < command.length; i++) {
args.add(command[i]);
}
commandlet.arguments.setValue(args);
commandlet.run();
} else {
this.context.info("Build command not set. Skipping build for repository.");
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.devonfw.tools.ide.commandlet;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.Collections;
import java.util.Properties;
import java.util.Set;

/**
* Represents the configuration of a repository to be used by the {@link RepositoryCommandlet}.
*
* @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 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.
* @param buildCmd The command to invoke to build the repository after clone or pull. If omitted no build is triggered.
* @param imports list of IDEs where the repository will be imported to.
* @param active {@code true} to setup the repository during setup, {@code false} to skip.
*/
public record RepositoryConfig(
String path,
String workingSets,
String workspace,
String gitUrl,
String gitBranch,
String buildPath,
String buildCmd,
Set<String> imports,
boolean active) {
public static RepositoryConfig loadProperties(Path filePath) {

Properties properties = new Properties();
try (InputStream input = new FileInputStream(filePath.toString())) {
properties.load(input);
} catch (IOException e) {
throw new IllegalStateException("Failed to read file: " + filePath, e);
}

Set<String> importsSet = getImports(properties);

return new RepositoryConfig(properties.getProperty("path"), properties.getProperty("workingsets"),
properties.getProperty("workspace"), properties.getProperty("git_url"), properties.getProperty("git_branch"),
properties.getProperty(("build_path")), properties.getProperty("build_cmd"), importsSet,
Boolean.parseBoolean(properties.getProperty("active").trim()));
}

private static Set<String> getImports(Properties properties) {

String importProperty = properties.getProperty("import");
if (importProperty != null && !importProperty.isEmpty()) {
return Set.of(importProperty.split("\\s*,\\s*"));
}

String legacyImportProperty = properties.getProperty("eclipse");
if ("import".equals(legacyImportProperty)) {
return Set.of("eclipse");
} else {
return Collections.emptySet();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,7 @@ public UrlMetadata getUrls() {

if (this.urlMetadata == null) {
if (!isTest()) {
this.getGitContext().pullOrFetchAndResetIfNeeded(IDE_URLS_GIT, this.urlsPath, "origin", "master");
this.getGitContext().pullOrFetchAndResetIfNeeded(IDE_URLS_GIT, "master", this.urlsPath, "origin");
}
this.urlMetadata = new UrlMetadata(this);
}
Expand Down
54 changes: 31 additions & 23 deletions cli/src/main/java/com/devonfw/tools/ide/context/GitContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,63 +13,71 @@ public interface GitContext extends IdeLogger {
* Checks if the Git repository in the specified target folder needs an update by inspecting the modification time of
* a magic file.
*
* @param repoUrl the git remote URL to clone from. May be suffixed with a hash-sign ('#') followed by the branch name
* to check-out.
* @param repoUrl the git remote URL to clone from.
* @param targetRepository the {@link Path} to the target folder where the git repository should be cloned or pulled.
* It is not the parent directory where git will by default create a sub-folder by default on clone but the *
* final folder that will contain the ".git" subfolder.
* It is not the parent directory where git will by default create a sub-folder by default on clone but the * final
* folder that will contain the ".git" subfolder.
*/
void pullOrCloneIfNeeded(String repoUrl, Path targetRepository);
void pullOrCloneIfNeeded(String repoUrl, String branch, Path targetRepository);

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

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

/**
* Runs a git pull or a git clone.
*
* @param gitRepoUrl the git remote URL to clone from.
* @param branch the branch name e.g. master.
* @param targetRepository the {@link Path} to the target folder where the git repository should be cloned or pulled.
* It is not the parent directory where git will by default create a sub-folder by default on clone but the * final
* folder that will contain the ".git" subfolder.
*/
void pullOrClone(String gitRepoUrl, String branch, Path targetRepository);

/**
* Runs a git clone. Throws a CliException if in offline mode.
*
* @param gitRepoUrl the {@link GitUrl} to use for the repository URL.
* @param targetRepository the {@link Path} to the target folder where the git repository should be cloned or pulled.
* It is not the parent directory where git will by default create a sub-folder by default on clone but the *
* final folder that will contain the ".git" subfolder.
* It is not the parent directory where git will by default create a sub-folder by default on clone but the * final
* folder that will contain the ".git" subfolder.
*/
void clone(GitUrl gitRepoUrl, Path targetRepository);

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

/**
* Runs a git reset if files were modified.
*
* @param targetRepository the {@link Path} to the target folder where the git repository should be cloned or pulled.
* It is not the parent directory where git will by default create a sub-folder by default on clone but the *
* final folder that will contain the ".git" subfolder.
* It is not the parent directory where git will by default create a sub-folder by default on clone but the * final
* folder that will contain the ".git" subfolder.
* @param remoteName the remote server name.
* @param branchName the name of the branch.
*/
Expand All @@ -79,8 +87,8 @@ public interface GitContext extends IdeLogger {
* Runs a git cleanup if untracked files were found.
*
* @param targetRepository the {@link Path} to the target folder where the git repository should be cloned or pulled.
* It is not the parent directory where git will by default create a sub-folder by default on clone but the *
* final folder that will contain the ".git" subfolder.
* It is not the parent directory where git will by default create a sub-folder by default on clone but the * final
* folder that will contain the ".git" subfolder.
*/
void cleanup(Path targetRepository);

Expand Down
Loading

0 comments on commit e122f21

Please sign in to comment.