From e9d574f613bfc16e245e8654936b3f8804a8e094 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Wed, 8 Nov 2023 15:11:07 +0100 Subject: [PATCH 01/93] added class RepositoryCommandlet and its help function --- .../ide/commandlet/CommandletManagerImpl.java | 1 + .../ide/commandlet/RepositoryCommandlet.java | 38 +++++++++++++++++++ cli/src/main/resources/nls/Ide.properties | 2 + 3 files changed, 41 insertions(+) create mode 100644 cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java index 089f909ba..80c42e49e 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java @@ -58,6 +58,7 @@ private CommandletManagerImpl(IdeContext context) { add(new VersionSetCommandlet(context)); add(new VersionGetCommandlet(context)); add(new VersionListCommandlet(context)); + add(new RepositoryCommandlet(context)); add(new Gh(context)); add(new Helm(context)); add(new Java(context)); diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java new file mode 100644 index 000000000..dbc03edcf --- /dev/null +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java @@ -0,0 +1,38 @@ +package com.devonfw.tools.ide.commandlet; + +import com.devonfw.tools.ide.context.IdeContext; +import com.devonfw.tools.ide.property.StringProperty; + +public class RepositoryCommandlet extends Commandlet { + + + private StringProperty setup; + + private StringProperty project; + + /** + * The constructor. + * + * @param context the {@link IdeContext}. + */ + public RepositoryCommandlet(IdeContext context) { + + super(context); + addKeyword(getName()); + this.setup = add(new StringProperty("setup", true, "setup")); + this.project = add(new StringProperty("", false, "project")); + + + } + + @Override + public String getName() { + + return "repository"; + } + + @Override + public void run() { + + } +} diff --git a/cli/src/main/resources/nls/Ide.properties b/cli/src/main/resources/nls/Ide.properties index 7f1344f9d..3892df2af 100644 --- a/cli/src/main/resources/nls/Ide.properties +++ b/cli/src/main/resources/nls/Ide.properties @@ -5,6 +5,7 @@ options=Options: cmd-az=Tool commandlet for Azure CLI. cmd-env=Print the environment variables to set and export. cmd-get-version=Get the version of the selected tool. +cmd-repository=setup the pre-configured project. cmd-gh=Tool commandlet for Github CLI. cmd-gradle=Tool commandlet for Gradle (Build-Tool) cmd-helm=Tool commandlet for Helm (Kubernetes Package Manager) @@ -25,6 +26,7 @@ val-args=The commandline arguments to pass to the tool. val-tool=The tool commandlet to select. val-version=The tool version. val-set-version-version=The tool version to set. +val-repository-project=The pre-configured project to setup version-banner=Current version of IDE is {} opt--batch=enable batch mode (non-interactive) opt--debug=enable debug logging From 76eba655ef3cebc1d1d142340a7257258489e919 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Thu, 9 Nov 2023 20:24:50 +0100 Subject: [PATCH 02/93] implemented run method when project is given as argument --- .../ide/commandlet/RepositoryCommandlet.java | 44 ++++++++++++++++--- cli/src/main/resources/nls/Ide.properties | 2 +- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java index dbc03edcf..d7bf2f58b 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java @@ -1,14 +1,19 @@ package com.devonfw.tools.ide.commandlet; import com.devonfw.tools.ide.context.IdeContext; +import com.devonfw.tools.ide.property.FolderProperty; +import com.devonfw.tools.ide.property.PathProperty; import com.devonfw.tools.ide.property.StringProperty; +import java.nio.file.Files; +import java.nio.file.Path; + public class RepositoryCommandlet extends Commandlet { private StringProperty setup; - private StringProperty project; + private PathProperty project; /** * The constructor. @@ -19,10 +24,8 @@ public RepositoryCommandlet(IdeContext context) { super(context); addKeyword(getName()); - this.setup = add(new StringProperty("setup", true, "setup")); - this.project = add(new StringProperty("", false, "project")); - - + this.setup = add(new StringProperty("setup", true, null)); + this.project = add(new PathProperty("", false, "project")); } @Override @@ -34,5 +37,36 @@ public String getName() { @Override public void run() { + Path repositoriesPath = this.context.getSettingsPath().resolve("repositories"); + Path legacyRepositoriesPath = this.context.getSettingsPath().resolve("projects"); + + if (project != null) { + Path projectFile = project.getValue(); + if (!Files.exists(projectFile)) { + projectFile = repositoriesPath.resolve(projectFile); + } + if (!Files.exists(projectFile)) { + Path legacyProjectFile = legacyRepositoriesPath.resolve(projectFile); + if (Files.exists(legacyProjectFile)) { + projectFile = legacyProjectFile; + } else { + this.context.warning("Could not find " + projectFile); + return; + } + } + doImportProject(projectFile, true); + } else { + //if no project was given, check whether repositoriesPath exists, if not check the legacy repositoriesPath, if not return. + Path repositories = Files.exists(repositoriesPath) ? repositoriesPath : Files.exists(legacyRepositoriesPath) ? legacyRepositoriesPath : null; + if (repositories == null) return; + + //iterate through repositories and import all active projects + } + } + + private void doImportProject(Path projectFile, boolean force) { + + + } } diff --git a/cli/src/main/resources/nls/Ide.properties b/cli/src/main/resources/nls/Ide.properties index 3892df2af..5cb298590 100644 --- a/cli/src/main/resources/nls/Ide.properties +++ b/cli/src/main/resources/nls/Ide.properties @@ -26,7 +26,7 @@ val-args=The commandline arguments to pass to the tool. val-tool=The tool commandlet to select. val-version=The tool version. val-set-version-version=The tool version to set. -val-repository-project=The pre-configured project to setup +val-repository-project=The pre-configured project to setup, omit to setup all active projects version-banner=Current version of IDE is {} opt--batch=enable batch mode (non-interactive) opt--debug=enable debug logging From 0e16de61ebd2f9e2ffb0e001a52d5843c3a132e1 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Fri, 10 Nov 2023 05:13:51 +0100 Subject: [PATCH 03/93] implemented loadProperties --- .../ide/commandlet/RepositoryCommandlet.java | 129 +++++++++++++++--- .../ide/commandlet/RepositoryConfig.java | 12 ++ .../com/devonfw/tools/ide/io/FileAccess.java | 10 ++ .../devonfw/tools/ide/io/FileAccessImpl.java | 20 +++ cli/src/main/resources/nls/Ide.properties | 2 +- cli/src/main/resources/nls/Ide_de.properties | 3 + 6 files changed, 154 insertions(+), 22 deletions(-) create mode 100644 cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryConfig.java diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java index d7bf2f58b..09fadac33 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java @@ -1,19 +1,23 @@ package com.devonfw.tools.ide.commandlet; import com.devonfw.tools.ide.context.IdeContext; -import com.devonfw.tools.ide.property.FolderProperty; import com.devonfw.tools.ide.property.PathProperty; import com.devonfw.tools.ide.property.StringProperty; +import com.devonfw.tools.ide.tool.ToolCommandlet; +import com.devonfw.tools.ide.tool.mvn.Mvn; +import com.devonfw.tools.ide.util.FilenameUtil; +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.List; +import java.util.Properties; public class RepositoryCommandlet extends Commandlet { - - private StringProperty setup; - - private PathProperty project; + private PathProperty repository; /** * The constructor. @@ -24,8 +28,8 @@ public RepositoryCommandlet(IdeContext context) { super(context); addKeyword(getName()); - this.setup = add(new StringProperty("setup", true, null)); - this.project = add(new PathProperty("", false, "project")); + add(new StringProperty("setup", true, null)); + this.repository = add(new PathProperty("", false, "repository")); } @Override @@ -40,33 +44,116 @@ public void run() { Path repositoriesPath = this.context.getSettingsPath().resolve("repositories"); Path legacyRepositoriesPath = this.context.getSettingsPath().resolve("projects"); - if (project != null) { - Path projectFile = project.getValue(); - if (!Files.exists(projectFile)) { - projectFile = repositoriesPath.resolve(projectFile); + if (repository != null) { + // Handle the case when a specific repository is provided + Path repositoryFile = repository.getValue(); + if (!Files.exists(repositoryFile)) { + repositoryFile = repositoriesPath.resolve(repositoryFile); } - if (!Files.exists(projectFile)) { - Path legacyProjectFile = legacyRepositoriesPath.resolve(projectFile); - if (Files.exists(legacyProjectFile)) { - projectFile = legacyProjectFile; + // + repositoryFile = Path.of(repositoryFile.toString() + ".properties"); + if (!Files.exists(repositoryFile)) { + Path legacyRepositoryFile = Path.of(legacyRepositoriesPath.resolve(repositoryFile.getFileName()).toString()); + if (Files.exists(legacyRepositoryFile)) { + repositoryFile = legacyRepositoryFile; } else { - this.context.warning("Could not find " + projectFile); + this.context.warning("Could not find " + repositoryFile); return; } } - doImportProject(projectFile, true); + doImportRepository(repositoryFile, true); } else { - //if no project was given, check whether repositoriesPath exists, if not check the legacy repositoriesPath, if not return. - Path repositories = Files.exists(repositoriesPath) ? repositoriesPath : Files.exists(legacyRepositoriesPath) ? legacyRepositoriesPath : null; + // If no specific repository is provided, check for repositories folder + Path repositories = Files.exists(repositoriesPath) ? repositoriesPath : + Files.exists(legacyRepositoriesPath) ? legacyRepositoriesPath : null; + if (repositories == null) return; - //iterate through repositories and import all active projects + List propertiesFiles = this.context.getFileAccess().getFilesInDir(repositories, + path -> "properties".equals(FilenameUtil.getExtension(path.getFileName().toString()))); + + if (propertiesFiles != null) { + boolean forceMode = this.context.isForceMode(); + for (Path propertiesFile : propertiesFiles) { + doImportRepository(propertiesFile, forceMode); + } + } + } + } + + private void doImportRepository(Path repositoryFile, boolean forceMode) { + + //TODO: unnecessary? + if (!Files.exists(repositoryFile)) { + return; + } + + this.context.info("Importing from {} ...", repositoryFile.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 ..."); + } + } + + String repository = repositoryConfig.path(); + String gitUrl = repositoryConfig.gitUrl(); + if (repository == null || ( repository != null && repository.isEmpty()) || gitUrl == null || (gitUrl != null && gitUrl.isEmpty())) { + this.context.warning("Invalid repository configuration {} - both 'path' and 'git-url' have to be defined." + , repositoryFile); + return; + } + + this.context.debug(repositoryConfig.toString()); + this.context.debug("Pull or clone git repository {} ...", repository); + + String workspace = repositoryConfig.workspace() != null ? repositoryConfig.workspace() : "main"; + Path repositoryWorkspacePath = this.context.getIdeHome().resolve("workspaces").resolve(workspace); + this.context.getFileAccess().mkdirs(repositoryWorkspacePath); + + String targetGitUrl = repositoryConfig.gitUrl(); + if (repositoryConfig.gitBranch() != null && !repositoryConfig.gitBranch().isEmpty()) { + targetGitUrl = targetGitUrl + "#" + repositoryConfig.gitBranch(); + } + + Path repositoryPath = repositoryWorkspacePath.resolve(repository); + this.context.gitPullOrClone(repositoryPath, targetGitUrl); + + String buildCmd = repositoryConfig.buildCmd(); + this.context.debug("Building project with ide command: {}", buildCmd); + if (buildCmd != null && !buildCmd.isEmpty()) { + List command = List.of(buildCmd.split("\\s+")); + String commandlet = command.get(0); + command.remove(0); + ToolCommandlet tc = (ToolCommandlet) this.context.getCommandletManager().getCommandlet(commandlet); + tc.runTool(null, command.toArray(new String[0])); } } - private void doImportProject(Path projectFile, boolean force) { + private 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); + } + return new RepositoryConfig( + properties.getProperty("path"), + properties.getProperty("workingsets"), + properties.getProperty("workspace"), + properties.getProperty("git_url"), + properties.getProperty("git_branch"), + properties.getProperty("build_cmd"), + properties.getProperty("eclipse"), + Boolean.parseBoolean(properties.getProperty("active")) + ); } + } diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryConfig.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryConfig.java new file mode 100644 index 000000000..5ac3b0391 --- /dev/null +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryConfig.java @@ -0,0 +1,12 @@ +package com.devonfw.tools.ide.commandlet; + +public record RepositoryConfig( + String path, + String workingSets, + String workspace, + String gitUrl, + String gitBranch, + String buildCmd, + String eclipse, + boolean active) { +} diff --git a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java index 82fe25090..d685facdf 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java +++ b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java @@ -1,6 +1,8 @@ package com.devonfw.tools.ide.io; +import java.io.IOException; import java.nio.file.Path; +import java.util.List; import java.util.function.Predicate; /** @@ -124,4 +126,12 @@ default void copy(Path source, Path target) { */ Path findFirst(Path dir, Predicate filter, boolean recursive); + /** + * Retrieves a list of file paths from the specified directory that satisfy the given predicate. + * @param dir the folder to iterate through + * @param filter the {@link Predicate} that determines whether a file should be included in the list. + * @return a list of paths that satisfy the provided {@link Predicate} or {@code null} if no match was found. + */ + public List getFilesInDir(Path dir, Predicate filter); + } diff --git a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java index 3a916fcd1..b59306df5 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java @@ -451,4 +451,24 @@ private Path findFirstRecursive(Path dir, Predicate filter, boolean recurs return null; } + @Override + public List getFilesInDir(Path dir, Predicate filter) { + List files = null; + try (Stream childStream = Files.list(dir)) { + Iterator iterator = childStream.iterator(); + while (iterator.hasNext()) { + Path child = iterator.next(); + if (filter.test(child)) { + if (files == null) { + files = new ArrayList<>(); + } + files.add(child); + } + } + } catch (IOException e) { + throw new IllegalStateException("Failed to iterate through " + dir, e); + } + return files; + } + } diff --git a/cli/src/main/resources/nls/Ide.properties b/cli/src/main/resources/nls/Ide.properties index 5cb298590..7b74ec4bf 100644 --- a/cli/src/main/resources/nls/Ide.properties +++ b/cli/src/main/resources/nls/Ide.properties @@ -26,7 +26,7 @@ val-args=The commandline arguments to pass to the tool. val-tool=The tool commandlet to select. val-version=The tool version. val-set-version-version=The tool version to set. -val-repository-project=The pre-configured project to setup, omit to setup all active projects +val-repository-project=The pre-configured project to setup, omit to setup all active projects. version-banner=Current version of IDE is {} opt--batch=enable batch mode (non-interactive) opt--debug=enable debug logging diff --git a/cli/src/main/resources/nls/Ide_de.properties b/cli/src/main/resources/nls/Ide_de.properties index 37815d7a1..b1178f925 100644 --- a/cli/src/main/resources/nls/Ide_de.properties +++ b/cli/src/main/resources/nls/Ide_de.properties @@ -5,6 +5,7 @@ options=Optionen: cmd-az=Werkzeug Kommando fuer Azure Kommandoschnittstelle. cmd-env=Gibt die zu setztenden und exportierenden Umgebungsvariablen aus. cmd-get-version=Zeigt die Version des selektierten Werkzeugs an. +cmd-repository=Richtet das vorkonfigurierte Projekt ein. cmd-gh=Werkzeug Kommando für die Github Kommandoschnittstelle. cmd-helm=Werkzeug Kommando für Helm (Kubernetes Package Manager) cmd-help=Zeigt diese Hilfe an. @@ -24,6 +25,8 @@ val-args=Die Kommandozeilen-Argumente zur Übergabe an das Werkzeug. val-tool=Das zu selektierende Werkzeug Kommando. val-version=Die Werkzeug Version. val-set-version-version=Die zu setztende Werkzeug Version. +val-repository-project= Das vorkonfigurierte Projekt zum Einrichten. + version-banner=Die aktuelle Version der IDE ist {} opt--batch=Aktiviert den Batch-Modus (nicht-interaktive Stapelverarbeitung) opt--debug=Aktiviert Debug-Ausgaben (Fehleranalyse) From 69835e338e5f071091d20575e8fe26333b7f6ee7 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Sat, 11 Nov 2023 04:40:12 +0100 Subject: [PATCH 04/93] added record RepositoryConfig, implemented run method, implemented loadProperties --- .../java/com/devonfw/tools/ide/cli/Ide.java | 2 +- .../ide/commandlet/RepositoryCommandlet.java | 30 ++++++++----------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/cli/Ide.java b/cli/src/main/java/com/devonfw/tools/ide/cli/Ide.java index df6e040c4..451c435d0 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/cli/Ide.java +++ b/cli/src/main/java/com/devonfw/tools/ide/cli/Ide.java @@ -155,7 +155,7 @@ private CliArgument initContext(CliArgument first) { } /** - * @param argument the current {@link CliArgument} (position) to match. + * @param current the current {@link CliArgument} (position) to match. * @param commandlet the potential {@link Commandlet} to {@link #apply(CliArgument, Commandlet) apply} and * {@link Commandlet#run() run}. * @return {@code true} if the given {@link Commandlet} matched and did {@link Commandlet#run() run} successfully, diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java index 09fadac33..9e585490f 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java @@ -4,7 +4,6 @@ import com.devonfw.tools.ide.property.PathProperty; import com.devonfw.tools.ide.property.StringProperty; import com.devonfw.tools.ide.tool.ToolCommandlet; -import com.devonfw.tools.ide.tool.mvn.Mvn; import com.devonfw.tools.ide.util.FilenameUtil; import java.io.FileInputStream; @@ -17,6 +16,7 @@ public class RepositoryCommandlet extends Commandlet { + private PathProperty repository; /** @@ -43,17 +43,15 @@ public void run() { Path repositoriesPath = this.context.getSettingsPath().resolve("repositories"); Path legacyRepositoriesPath = this.context.getSettingsPath().resolve("projects"); + Path repositoryFile = repository.getValue(); - if (repository != null) { + if (repositoryFile != null) { // Handle the case when a specific repository is provided - Path repositoryFile = repository.getValue(); if (!Files.exists(repositoryFile)) { - repositoryFile = repositoriesPath.resolve(repositoryFile); + repositoryFile = repositoriesPath.resolve(repositoryFile.toString() + ".properties"); } - // - repositoryFile = Path.of(repositoryFile.toString() + ".properties"); if (!Files.exists(repositoryFile)) { - Path legacyRepositoryFile = Path.of(legacyRepositoriesPath.resolve(repositoryFile.getFileName()).toString()); + Path legacyRepositoryFile = legacyRepositoriesPath.resolve(repositoryFile.getFileName().toString()); if (Files.exists(legacyRepositoryFile)) { repositoryFile = legacyRepositoryFile; } else { @@ -83,11 +81,6 @@ public void run() { private void doImportRepository(Path repositoryFile, boolean forceMode) { - //TODO: unnecessary? - if (!Files.exists(repositoryFile)) { - return; - } - this.context.info("Importing from {} ...", repositoryFile.toString()); RepositoryConfig repositoryConfig = loadProperties(repositoryFile); @@ -97,6 +90,7 @@ private void doImportRepository(Path repositoryFile, boolean forceMode) { this.context.info("Repository setup is forced, hence proceeding ..."); } else { this.context.info("Skipping repository - use force (-f) to setup all repositories ..."); + return; } } @@ -126,11 +120,13 @@ private void doImportRepository(Path repositoryFile, boolean forceMode) { String buildCmd = repositoryConfig.buildCmd(); this.context.debug("Building project with ide command: {}", buildCmd); if (buildCmd != null && !buildCmd.isEmpty()) { - List command = List.of(buildCmd.split("\\s+")); - String commandlet = command.get(0); - command.remove(0); - ToolCommandlet tc = (ToolCommandlet) this.context.getCommandletManager().getCommandlet(commandlet); - tc.runTool(null, command.toArray(new String[0])); + //TODO: build repository build path + } else { + this.context.info("Build command not set. Skipping build for repository."); + } + + if ("import".equals(repositoryConfig.eclipse()) && !Files.exists(repositoryPath.resolve(".project"))) { + //TODO: import repository to eclipse } } From 90145a6b1ee46b44659d517851957110e0a05ee8 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Mon, 13 Nov 2023 05:39:10 +0100 Subject: [PATCH 05/93] implemented build functionality, import missing --- .../ide/commandlet/RepositoryCommandlet.java | 21 ++++++++++++++++--- .../ide/commandlet/RepositoryConfig.java | 1 + .../tools/ide/tool/eclipse/Eclipse.java | 7 +++++++ 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java index 9e585490f..04771eb91 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java @@ -1,9 +1,11 @@ package com.devonfw.tools.ide.commandlet; import com.devonfw.tools.ide.context.IdeContext; +import com.devonfw.tools.ide.process.ProcessContext; import com.devonfw.tools.ide.property.PathProperty; import com.devonfw.tools.ide.property.StringProperty; import com.devonfw.tools.ide.tool.ToolCommandlet; +import com.devonfw.tools.ide.tool.eclipse.Eclipse; import com.devonfw.tools.ide.util.FilenameUtil; import java.io.FileInputStream; @@ -11,6 +13,7 @@ import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Arrays; import java.util.List; import java.util.Properties; @@ -96,7 +99,7 @@ private void doImportRepository(Path repositoryFile, boolean forceMode) { String repository = repositoryConfig.path(); String gitUrl = repositoryConfig.gitUrl(); - if (repository == null || ( repository != null && repository.isEmpty()) || gitUrl == null || (gitUrl != null && gitUrl.isEmpty())) { + 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); return; @@ -109,7 +112,7 @@ private void doImportRepository(Path repositoryFile, boolean forceMode) { Path repositoryWorkspacePath = this.context.getIdeHome().resolve("workspaces").resolve(workspace); this.context.getFileAccess().mkdirs(repositoryWorkspacePath); - String targetGitUrl = repositoryConfig.gitUrl(); + String targetGitUrl = gitUrl; if (repositoryConfig.gitBranch() != null && !repositoryConfig.gitBranch().isEmpty()) { targetGitUrl = targetGitUrl + "#" + repositoryConfig.gitBranch(); } @@ -120,13 +123,24 @@ private void doImportRepository(Path repositoryFile, boolean forceMode) { String buildCmd = repositoryConfig.buildCmd(); this.context.debug("Building project with ide command: {}", buildCmd); if (buildCmd != null && !buildCmd.isEmpty()) { - //TODO: build repository build path + String[] command = buildCmd.split("\\s+"); + ProcessContext pc = this.context.newProcess(); + pc.executable(command[0]); + pc.addArgs(Arrays.copyOfRange(command, 1, command.length)); + Path buildPath = repositoryPath; + if (repositoryConfig.buildPath() != null) { + buildPath = buildPath.resolve(repositoryConfig.buildPath()); + } + pc.directory(buildPath); + this.context.getCommandletManager().getToolCommandlet(command[0]).install(false); + pc.run(); } else { this.context.info("Build command not set. Skipping build for repository."); } if ("import".equals(repositoryConfig.eclipse()) && !Files.exists(repositoryPath.resolve(".project"))) { //TODO: import repository to eclipse + this.context.getCommandletManager().getCommandlet(Eclipse.class).importRepository(repositoryPath, repositoryWorkspacePath, repositoryConfig.workingSets()); } } @@ -146,6 +160,7 @@ private RepositoryConfig loadProperties(Path filePath) { properties.getProperty("workspace"), properties.getProperty("git_url"), properties.getProperty("git_branch"), + properties.getProperty(("build_path")), properties.getProperty("build_cmd"), properties.getProperty("eclipse"), Boolean.parseBoolean(properties.getProperty("active")) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryConfig.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryConfig.java index 5ac3b0391..474c7b461 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryConfig.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryConfig.java @@ -6,6 +6,7 @@ public record RepositoryConfig( String workspace, String gitUrl, String gitBranch, + String buildPath, String buildCmd, String eclipse, boolean active) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/eclipse/Eclipse.java b/cli/src/main/java/com/devonfw/tools/ide/tool/eclipse/Eclipse.java index 1d0adf78c..e28342ef5 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/eclipse/Eclipse.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/eclipse/Eclipse.java @@ -104,4 +104,11 @@ private void log(IdeLogLevel level, List lines) { } } + public void importRepository(Path importPath, Path workspacePath, String workingSets) { + + //configureEclipse(); + //checkAndInstallGroovy(); + + + } } From c738a0edc3f1a00ca1c252504ff3e8819835c176 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Mon, 13 Nov 2023 05:47:53 +0100 Subject: [PATCH 06/93] minor improvements --- .../devonfw/tools/ide/commandlet/RepositoryCommandlet.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java index 04771eb91..d9beececa 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java @@ -51,7 +51,7 @@ public void run() { if (repositoryFile != null) { // Handle the case when a specific repository is provided if (!Files.exists(repositoryFile)) { - repositoryFile = repositoriesPath.resolve(repositoryFile.toString() + ".properties"); + repositoryFile = repositoriesPath.resolve(repositoryFile.getFileName().toString() + ".properties"); } if (!Files.exists(repositoryFile)) { Path legacyRepositoryFile = legacyRepositoriesPath.resolve(repositoryFile.getFileName().toString()); @@ -112,13 +112,12 @@ private void doImportRepository(Path repositoryFile, boolean forceMode) { Path repositoryWorkspacePath = this.context.getIdeHome().resolve("workspaces").resolve(workspace); this.context.getFileAccess().mkdirs(repositoryWorkspacePath); - String targetGitUrl = gitUrl; if (repositoryConfig.gitBranch() != null && !repositoryConfig.gitBranch().isEmpty()) { - targetGitUrl = targetGitUrl + "#" + repositoryConfig.gitBranch(); + gitUrl = gitUrl + "#" + repositoryConfig.gitBranch(); } Path repositoryPath = repositoryWorkspacePath.resolve(repository); - this.context.gitPullOrClone(repositoryPath, targetGitUrl); + this.context.gitPullOrClone(repositoryPath, gitUrl); String buildCmd = repositoryConfig.buildCmd(); this.context.debug("Building project with ide command: {}", buildCmd); From c3de09e703b6884d037017fe14cbe59a121b6c28 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Tue, 14 Nov 2023 12:22:01 +0100 Subject: [PATCH 07/93] javadoc --- .../ide/commandlet/RepositoryCommandlet.java | 18 +++++++++--------- .../tools/ide/commandlet/RepositoryConfig.java | 13 +++++++++++++ .../tools/ide/tool/eclipse/Eclipse.java | 7 ------- cli/src/main/resources/nls/Ide.properties | 4 ++-- 4 files changed, 24 insertions(+), 18 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java index d9beececa..a45aa2556 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java @@ -4,8 +4,6 @@ import com.devonfw.tools.ide.process.ProcessContext; import com.devonfw.tools.ide.property.PathProperty; import com.devonfw.tools.ide.property.StringProperty; -import com.devonfw.tools.ide.tool.ToolCommandlet; -import com.devonfw.tools.ide.tool.eclipse.Eclipse; import com.devonfw.tools.ide.util.FilenameUtil; import java.io.FileInputStream; @@ -17,10 +15,13 @@ import java.util.List; import java.util.Properties; +/** + * {@link Commandlet} to setup a repository + */ public class RepositoryCommandlet extends Commandlet { - - private PathProperty repository; + /** the repository to setup. */ + public final PathProperty repository; /** * The constructor. @@ -109,20 +110,21 @@ private void doImportRepository(Path repositoryFile, boolean forceMode) { this.context.debug("Pull or clone git repository {} ...", repository); String workspace = repositoryConfig.workspace() != null ? repositoryConfig.workspace() : "main"; - Path repositoryWorkspacePath = this.context.getIdeHome().resolve("workspaces").resolve(workspace); - this.context.getFileAccess().mkdirs(repositoryWorkspacePath); + Path workspacePath = this.context.getIdeHome().resolve("workspaces").resolve(workspace); + this.context.getFileAccess().mkdirs(workspacePath); if (repositoryConfig.gitBranch() != null && !repositoryConfig.gitBranch().isEmpty()) { gitUrl = gitUrl + "#" + repositoryConfig.gitBranch(); } - Path repositoryPath = repositoryWorkspacePath.resolve(repository); + Path repositoryPath = workspacePath.resolve(repository); this.context.gitPullOrClone(repositoryPath, gitUrl); String buildCmd = repositoryConfig.buildCmd(); this.context.debug("Building project with ide command: {}", buildCmd); if (buildCmd != null && !buildCmd.isEmpty()) { String[] command = buildCmd.split("\\s+"); + this.context.getCommandletManager().getToolCommandlet(command[0]).install(true); ProcessContext pc = this.context.newProcess(); pc.executable(command[0]); pc.addArgs(Arrays.copyOfRange(command, 1, command.length)); @@ -131,7 +133,6 @@ private void doImportRepository(Path repositoryFile, boolean forceMode) { buildPath = buildPath.resolve(repositoryConfig.buildPath()); } pc.directory(buildPath); - this.context.getCommandletManager().getToolCommandlet(command[0]).install(false); pc.run(); } else { this.context.info("Build command not set. Skipping build for repository."); @@ -139,7 +140,6 @@ private void doImportRepository(Path repositoryFile, boolean forceMode) { if ("import".equals(repositoryConfig.eclipse()) && !Files.exists(repositoryPath.resolve(".project"))) { //TODO: import repository to eclipse - this.context.getCommandletManager().getCommandlet(Eclipse.class).importRepository(repositoryPath, repositoryWorkspacePath, repositoryConfig.workingSets()); } } diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryConfig.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryConfig.java index 474c7b461..e03d2765a 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryConfig.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryConfig.java @@ -1,5 +1,18 @@ package com.devonfw.tools.ide.commandlet; +/** + * 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 eclipse Desired action for eclipse IDE. If equals to "import" all modules will be imported into eclipse. + * @param active {@code true} to setup the repository during setup, {@code false} to skip. + */ public record RepositoryConfig( String path, String workingSets, diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/eclipse/Eclipse.java b/cli/src/main/java/com/devonfw/tools/ide/tool/eclipse/Eclipse.java index e28342ef5..1d0adf78c 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/eclipse/Eclipse.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/eclipse/Eclipse.java @@ -104,11 +104,4 @@ private void log(IdeLogLevel level, List lines) { } } - public void importRepository(Path importPath, Path workspacePath, String workingSets) { - - //configureEclipse(); - //checkAndInstallGroovy(); - - - } } diff --git a/cli/src/main/resources/nls/Ide.properties b/cli/src/main/resources/nls/Ide.properties index 7b74ec4bf..fe6032054 100644 --- a/cli/src/main/resources/nls/Ide.properties +++ b/cli/src/main/resources/nls/Ide.properties @@ -5,7 +5,7 @@ options=Options: cmd-az=Tool commandlet for Azure CLI. cmd-env=Print the environment variables to set and export. cmd-get-version=Get the version of the selected tool. -cmd-repository=setup the pre-configured project. +cmd-repository=setup the pre-configured repository. cmd-gh=Tool commandlet for Github CLI. cmd-gradle=Tool commandlet for Gradle (Build-Tool) cmd-helm=Tool commandlet for Helm (Kubernetes Package Manager) @@ -26,7 +26,7 @@ val-args=The commandline arguments to pass to the tool. val-tool=The tool commandlet to select. val-version=The tool version. val-set-version-version=The tool version to set. -val-repository-project=The pre-configured project to setup, omit to setup all active projects. +val-repository-repository=The pre-configured repository to setup, omit to setup all active repositories. version-banner=Current version of IDE is {} opt--batch=enable batch mode (non-interactive) opt--debug=enable debug logging From c0fbd26e7caa3c30f3185ca9d1199884b67e5d70 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Thu, 16 Nov 2023 11:16:51 +0100 Subject: [PATCH 08/93] fixed bug: tool installed but not found --- cli/src/main/java/com/devonfw/tools/ide/common/SystemPath.java | 1 + 1 file changed, 1 insertion(+) diff --git a/cli/src/main/java/com/devonfw/tools/ide/common/SystemPath.java b/cli/src/main/java/com/devonfw/tools/ide/common/SystemPath.java index ac24d3a99..9219eea93 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/common/SystemPath.java +++ b/cli/src/main/java/com/devonfw/tools/ide/common/SystemPath.java @@ -166,6 +166,7 @@ public Path getPath(String tool) { */ public void setPath(String tool, Path path) { + this.paths.add(path); this.tool2pathMap.put(tool, path); } From edc7b1dab3e1646b379a12277b4d4d5b016a1c90 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Thu, 23 Nov 2023 02:59:20 +0100 Subject: [PATCH 09/93] created class UpdateCommandlet, implemented updateSettings method --- .../ide/commandlet/CommandletManagerImpl.java | 1 + .../ide/commandlet/UpdateCommandlet.java | 67 +++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java index 089f909ba..d92068f25 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java @@ -58,6 +58,7 @@ private CommandletManagerImpl(IdeContext context) { add(new VersionSetCommandlet(context)); add(new VersionGetCommandlet(context)); add(new VersionListCommandlet(context)); + add(new UpdateCommandlet(context)); add(new Gh(context)); add(new Helm(context)); add(new Java(context)); diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java new file mode 100644 index 000000000..9c2729297 --- /dev/null +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java @@ -0,0 +1,67 @@ +package com.devonfw.tools.ide.commandlet; + +import com.devonfw.tools.ide.context.IdeContext; +import com.devonfw.tools.ide.property.StringProperty; + +import java.nio.file.Files; +import java.nio.file.Path; + +public class UpdateCommandlet extends Commandlet { + + public final StringProperty settingsURL; + + /** + * The constructor. + * + * @param context the {@link IdeContext}. + */ + public UpdateCommandlet(IdeContext context) { + + super(context); + addKeyword(getName()); + settingsURL = add(new StringProperty("", false, "settingsURL")); + + } + + @Override + public String getName() { + + return "update"; + } + + @Override + public void run() { + + updateSettings(); + updateSoftware(); + updateRepositories(); + + } + + private void updateRepositories() { + + } + + private void updateSoftware() { + + } + + private void updateSettings() { + + Path settingsPath = this.context.getSettingsPath(); + if (Files.isDirectory(settingsPath)) { + //pull remote settings repo + this.context.gitPullOrClone(settingsPath, "http"); + } else { + //clone given settingsUrl + String settingsUrl = this.settingsURL.getValue(); + if (settingsUrl != null ) { + + } + + } + + + + } +} From 7b43974811e319476a93b3a61fe010ea3eae1ba1 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Thu, 23 Nov 2023 02:59:37 +0100 Subject: [PATCH 10/93] created class UpdateCommandlet, implemented updateSettings method --- .../ide/commandlet/UpdateCommandlet.java | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java index 9c2729297..55da44335 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java @@ -10,6 +10,8 @@ public class UpdateCommandlet extends Commandlet { public final StringProperty settingsURL; + private static final String defaultSettingsUrl = "https://github.com/devonfw/ide-settings.git"; + /** * The constructor. * @@ -49,15 +51,30 @@ private void updateSoftware() { private void updateSettings() { Path settingsPath = this.context.getSettingsPath(); + if (Files.isDirectory(settingsPath)) { //pull remote settings repo this.context.gitPullOrClone(settingsPath, "http"); } else { //clone given settingsUrl String settingsUrl = this.settingsURL.getValue(); - if (settingsUrl != null ) { + if (settingsUrl == null) { + if (this.context.isBatchMode()) { + settingsUrl = ""; + } else { + this.context.info("Missing your settings at {} and no Settings URL is defined."); + this.context.info("Further details can be found here:"); + this.context.info("https://github.com/devonfw/IDEasy/blob/main/documentation/settings.asciidoc"); + this.context.info("Please contact the technical lead of your project to get the SETTINGS_URL for your project."); + this.context.info("In case you just want to test IDEasy you may simply hit return to install default settings."); + //read line settingsUrl = this.context.readLine(); + } + } + if (settingsUrl.isEmpty()) { + settingsUrl = defaultSettingsUrl; } + this.context.gitPullOrClone(settingsPath, settingsUrl); } From 15bdb62c6b62f607081a03abd526d4ecf52c39ba Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Sun, 26 Nov 2023 03:36:25 +0100 Subject: [PATCH 11/93] implemented repository update --- .../ide/commandlet/UpdateCommandlet.java | 61 +++++++++---------- 1 file changed, 28 insertions(+), 33 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java index 55da44335..84618857a 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java @@ -1,17 +1,16 @@ package com.devonfw.tools.ide.commandlet; import com.devonfw.tools.ide.context.IdeContext; -import com.devonfw.tools.ide.property.StringProperty; +import com.devonfw.tools.ide.tool.ToolCommandlet; import java.nio.file.Files; import java.nio.file.Path; +import java.util.HashSet; +import java.util.List; +import java.util.Set; public class UpdateCommandlet extends Commandlet { - public final StringProperty settingsURL; - - private static final String defaultSettingsUrl = "https://github.com/devonfw/ide-settings.git"; - /** * The constructor. * @@ -21,8 +20,6 @@ public UpdateCommandlet(IdeContext context) { super(context); addKeyword(getName()); - settingsURL = add(new StringProperty("", false, "settingsURL")); - } @Override @@ -34,51 +31,49 @@ public String getName() { @Override public void run() { - updateSettings(); + // updateSettings(); updateSoftware(); updateRepositories(); } private void updateRepositories() { + this.context.getCommandletManager().getCommandlet(RepositoryCommandlet.class).run(); } private void updateSoftware() { + // Retrieve all installed software + List softwares = this.context.getFileAccess().getFilesInDir(this.context.getSoftwarePath(), p -> true); + Set toolCommandlets = new HashSet<>(); + for (Path software : softwares) { + toolCommandlets.add(this.context.getCommandletManager().getToolCommandlet(software.getFileName().toString())); + } + + String regularToolsV = this.context.getVariables().get("IDE_TOOLS"); + // Split the string based on comma delimiter + String[] regularTools = regularToolsV.split(",\\s"); + for (String regularTool : regularTools) { + toolCommandlets.add(this.context.getCommandletManager().getToolCommandlet(regularTool)); + } + + // update/install the toolCommandlets + for (ToolCommandlet toolCommandlet : toolCommandlets) { + toolCommandlet.install(); + } + + } private void updateSettings() { Path settingsPath = this.context.getSettingsPath(); - if (Files.isDirectory(settingsPath)) { - //pull remote settings repo + // perform git pull on the settings repo this.context.gitPullOrClone(settingsPath, "http"); } else { - //clone given settingsUrl - String settingsUrl = this.settingsURL.getValue(); - if (settingsUrl == null) { - if (this.context.isBatchMode()) { - settingsUrl = ""; - } else { - this.context.info("Missing your settings at {} and no Settings URL is defined."); - this.context.info("Further details can be found here:"); - this.context.info("https://github.com/devonfw/IDEasy/blob/main/documentation/settings.asciidoc"); - this.context.info("Please contact the technical lead of your project to get the SETTINGS_URL for your project."); - this.context.info("In case you just want to test IDEasy you may simply hit return to install default settings."); - //read line settingsUrl = this.context.readLine(); - } - } - - if (settingsUrl.isEmpty()) { - settingsUrl = defaultSettingsUrl; - } - this.context.gitPullOrClone(settingsPath, settingsUrl); - + throw new IllegalStateException("Cannot find settings repository."); } - - - } } From 8063580516c1ff4c96f051b80108b86de496f77a Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Tue, 28 Nov 2023 19:06:20 +0100 Subject: [PATCH 12/93] solved bug in CustomToolRepositoryImpl, created class CustomToolCommandlet --- .../ide/commandlet/CommandletManagerImpl.java | 2 + .../ide/commandlet/UpdateCommandlet.java | 77 ++++++++++++++----- .../ide/repo/CustomToolRepositoryImpl.java | 6 +- .../tools/ide/tool/CustomToolCommandlet.java | 29 +++++++ 4 files changed, 90 insertions(+), 24 deletions(-) create mode 100644 cli/src/main/java/com/devonfw/tools/ide/tool/CustomToolCommandlet.java diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java index b860d27bd..9f0164e9f 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java @@ -15,6 +15,7 @@ import com.devonfw.tools.ide.tool.gh.Gh; import com.devonfw.tools.ide.tool.gradle.Gradle; import com.devonfw.tools.ide.tool.helm.Helm; +import com.devonfw.tools.ide.tool.intellij.Intellij; import com.devonfw.tools.ide.tool.java.Java; import com.devonfw.tools.ide.tool.kotlinc.Kotlinc; import com.devonfw.tools.ide.tool.kotlinc.KotlincNative; @@ -74,6 +75,7 @@ private CommandletManagerImpl(IdeContext context) { add(new KotlincNative(context)); add(new Vscode(context)); add(new Azure(context)); + add(new Intellij(context)); } private void add(Commandlet commandlet) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java index 84618857a..7ee0244a8 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java @@ -1,6 +1,8 @@ package com.devonfw.tools.ide.commandlet; import com.devonfw.tools.ide.context.IdeContext; +import com.devonfw.tools.ide.repo.CustomTool; +import com.devonfw.tools.ide.tool.CustomToolCommandlet; import com.devonfw.tools.ide.tool.ToolCommandlet; import java.nio.file.Files; @@ -31,49 +33,82 @@ public String getName() { @Override public void run() { - // updateSettings(); - updateSoftware(); - updateRepositories(); + //updateSettings(); + //updateSoftware(); + //updateRepositories(); + testMethod(); } - private void updateRepositories() { - this.context.getCommandletManager().getCommandlet(RepositoryCommandlet.class).run(); + private void testMethod() { + + for (CustomTool ct : this.context.getCustomToolRepository().getTools()) { + this.context.info("Tool: {}, Version: {}, URL: {}", ct.getTool(), ct.getVersion(), ct.getUrl()); + } } + private void updateSettings() { + + this.context.info("Updating settings repository ..."); + Path settingsPath = this.context.getSettingsPath(); + if (Files.isDirectory(settingsPath)) { + // perform git pull on the settings repo + this.context.gitPullOrClone(settingsPath, "https://github.com/devonfw/ide-settings"); + } else { + throw new IllegalStateException("Cannot find settings repository."); + } + } + private void updateSoftware() { - // Retrieve all installed software - List softwares = this.context.getFileAccess().getFilesInDir(this.context.getSoftwarePath(), p -> true); Set toolCommandlets = new HashSet<>(); + Set failedInstalls = new HashSet<>(); + + // installed tools + List softwares = this.context.getFileAccess().getFilesInDir(this.context.getSoftwarePath(), p -> true); for (Path software : softwares) { - toolCommandlets.add(this.context.getCommandletManager().getToolCommandlet(software.getFileName().toString())); + String toolName = software.getFileName().toString(); + toolCommandlets.add(this.context.getCommandletManager().getToolCommandlet(toolName)); } - String regularToolsV = this.context.getVariables().get("IDE_TOOLS"); - // Split the string based on comma delimiter - String[] regularTools = regularToolsV.split(",\\s"); + // regular tools in $IDE_TOOLS + String regularToolsString = this.context.getVariables().get("IDE_TOOLS"); + String[] regularTools = regularToolsString.split(",\\s"); for (String regularTool : regularTools) { toolCommandlets.add(this.context.getCommandletManager().getToolCommandlet(regularTool)); } + // custom tools + for (CustomTool customTool : this.context.getCustomToolRepository().getTools()) { + CustomToolCommandlet customToolCommandlet = new CustomToolCommandlet(this.context, customTool); + toolCommandlets.add(customToolCommandlet); + } + // update/install the toolCommandlets for (ToolCommandlet toolCommandlet : toolCommandlets) { - toolCommandlet.install(); + try { + this.context.step("Setting up {}", toolCommandlet.getName()); + toolCommandlet.install(false); + } catch (Exception e) { + failedInstalls.add(toolCommandlet.getName()); + } } - + if (failedInstalls.isEmpty()) { + this.context.success("All tools were installed successfully."); + } else { + this.context.warning("Following tools could not be installed: {}", String.join(", ", failedInstalls)); + } } - private void updateSettings() { + private void downloadAndInstall(CustomTool customTool) { - Path settingsPath = this.context.getSettingsPath(); - if (Files.isDirectory(settingsPath)) { - // perform git pull on the settings repo - this.context.gitPullOrClone(settingsPath, "http"); - } else { - throw new IllegalStateException("Cannot find settings repository."); - } + this.context.getCustomToolRepository().download(customTool.getTool(), customTool.getEdition(), customTool.getVersion()); + + } + + private void updateRepositories() { + this.context.getCommandletManager().getCommandlet(RepositoryCommandlet.class).run(); } } diff --git a/cli/src/main/java/com/devonfw/tools/ide/repo/CustomToolRepositoryImpl.java b/cli/src/main/java/com/devonfw/tools/ide/repo/CustomToolRepositoryImpl.java index 29fb8172d..2172c9b58 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/repo/CustomToolRepositoryImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/repo/CustomToolRepositoryImpl.java @@ -189,7 +189,7 @@ private static boolean getBoolean(JsonObject json, String property, Boolean defa } return defaultValue.booleanValue(); } - ValueType valueType = json.getValueType(); + ValueType valueType = value.getValueType(); if (valueType == ValueType.TRUE) { return true; } else if (valueType == ValueType.FALSE) { @@ -213,8 +213,8 @@ private static String getString(JsonObject json, String property, String default } return defaultValue; } - require(json, ValueType.STRING); - return ((JsonString) json).getString(); + require(value, ValueType.STRING); + return ((JsonString) value).getString(); } /** diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/CustomToolCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/tool/CustomToolCommandlet.java new file mode 100644 index 000000000..247773b48 --- /dev/null +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/CustomToolCommandlet.java @@ -0,0 +1,29 @@ +package com.devonfw.tools.ide.tool; + +import com.devonfw.tools.ide.context.IdeContext; +import com.devonfw.tools.ide.repo.CustomTool; +import com.devonfw.tools.ide.version.VersionIdentifier; + + +public class CustomToolCommandlet extends LocalToolCommandlet { + + private CustomTool customTool; + + public CustomToolCommandlet(IdeContext context, CustomTool customTool) { + + super(context, customTool.getTool(), null); + this.customTool = customTool; + } + + @Override + public ToolInstallation installInRepo(VersionIdentifier version) { + + return installInRepo(version, this.customTool.getEdition()); + } + + @Override + public ToolInstallation installInRepo(VersionIdentifier version, String edition) { + + return installInRepo(version, edition, this.context.getCustomToolRepository()); + } +} From 1c729aeb7dbdf226a3a9430d830ad1acd37a0a70 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Tue, 28 Nov 2023 19:14:16 +0100 Subject: [PATCH 13/93] removed useless improt --- .../com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java | 3 +++ cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java | 3 +-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java index a45aa2556..5faac1273 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java @@ -23,6 +23,9 @@ public class RepositoryCommandlet extends Commandlet { /** the repository to setup. */ public final PathProperty repository; + public final Path repositoriesPath = context.getSettingsPath().resolve("repositories"); + Path legacyRepositoriesPath = this.context.getSettingsPath().resolve("projects"); + /** * The constructor. * diff --git a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java index c6b9cf329..237fb8c6b 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java +++ b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java @@ -1,6 +1,5 @@ package com.devonfw.tools.ide.io; -import java.io.IOException; import java.nio.file.Path; import java.util.List; import java.util.function.Predicate; @@ -139,6 +138,6 @@ default void copy(Path source, Path target) { * @param filter the {@link Predicate} that determines whether a file should be included in the list. * @return a list of paths that satisfy the provided {@link Predicate} or {@code null} if no match was found. */ - public List getFilesInDir(Path dir, Predicate filter); + List getFilesInDir(Path dir, Predicate filter); } From 60e72bc3ac275bce76c8e67ad177b3da6461231f Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Tue, 28 Nov 2023 20:23:40 +0100 Subject: [PATCH 14/93] implemented install/update of custom tools --- .../ide/commandlet/UpdateCommandlet.java | 41 ++++++++----------- .../devonfw/tools/ide/repo/CustomTool.java | 3 +- .../tools/ide/tool/CustomToolCommandlet.java | 6 +++ 3 files changed, 24 insertions(+), 26 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java index 7ee0244a8..097356c6f 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java @@ -7,9 +7,7 @@ import java.nio.file.Files; import java.nio.file.Path; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; public class UpdateCommandlet extends Commandlet { @@ -33,19 +31,9 @@ public String getName() { @Override public void run() { - //updateSettings(); - //updateSoftware(); - //updateRepositories(); - testMethod(); - - } - - private void testMethod() { - - for (CustomTool ct : this.context.getCustomToolRepository().getTools()) { - this.context.info("Tool: {}, Version: {}, URL: {}", ct.getTool(), ct.getVersion(), ct.getUrl()); - } - + updateSettings(); + updateSoftware(); + updateRepositories(); } private void updateSettings() { @@ -55,6 +43,7 @@ private void updateSettings() { if (Files.isDirectory(settingsPath)) { // perform git pull on the settings repo this.context.gitPullOrClone(settingsPath, "https://github.com/devonfw/ide-settings"); + this.context.success("Successfully updated settings repository."); } else { throw new IllegalStateException("Cannot find settings repository."); } @@ -64,12 +53,17 @@ private void updateSoftware() { Set toolCommandlets = new HashSet<>(); Set failedInstalls = new HashSet<>(); + Map tool2Exception = new HashMap<>(); // installed tools List softwares = this.context.getFileAccess().getFilesInDir(this.context.getSoftwarePath(), p -> true); for (Path software : softwares) { String toolName = software.getFileName().toString(); - toolCommandlets.add(this.context.getCommandletManager().getToolCommandlet(toolName)); + try { + toolCommandlets.add(this.context.getCommandletManager().getToolCommandlet(toolName)); + } catch (IllegalArgumentException e) { + //tool is a custom tool, ignore + } } // regular tools in $IDE_TOOLS @@ -91,23 +85,20 @@ private void updateSoftware() { this.context.step("Setting up {}", toolCommandlet.getName()); toolCommandlet.install(false); } catch (Exception e) { - failedInstalls.add(toolCommandlet.getName()); + tool2Exception.put(toolCommandlet.getName(), e.getMessage()); } } + if (failedInstalls.isEmpty()) { this.context.success("All tools were installed successfully."); } else { - this.context.warning("Following tools could not be installed: {}", String.join(", ", failedInstalls)); + this.context.warning("Following tools could not be installed:"); + for (String toolName : tool2Exception.keySet()) + this.context.warning("{}: {}", toolName, tool2Exception.get(toolName)); } } - private void downloadAndInstall(CustomTool customTool) { - - this.context.getCustomToolRepository().download(customTool.getTool(), customTool.getEdition(), customTool.getVersion()); - - } - private void updateRepositories() { this.context.getCommandletManager().getCommandlet(RepositoryCommandlet.class).run(); } diff --git a/cli/src/main/java/com/devonfw/tools/ide/repo/CustomTool.java b/cli/src/main/java/com/devonfw/tools/ide/repo/CustomTool.java index 474c6bf25..88692a5d2 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/repo/CustomTool.java +++ b/cli/src/main/java/com/devonfw/tools/ide/repo/CustomTool.java @@ -88,7 +88,8 @@ public CustomTool(String tool, VersionIdentifier versionIdentifier, boolean osAg sb.append(this.arch); } sb.append(".tgz"); - this.url = sb.toString(); + //this.url = sb.toString(); + this.url = repositoryUrl; } @Override diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/CustomToolCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/tool/CustomToolCommandlet.java index 247773b48..1e363cc55 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/CustomToolCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/CustomToolCommandlet.java @@ -26,4 +26,10 @@ public ToolInstallation installInRepo(VersionIdentifier version, String edition) return installInRepo(version, edition, this.context.getCustomToolRepository()); } + + @Override + public VersionIdentifier getConfiguredVersion() { + + return this.customTool.getVersion(); + } } From 01cb2b94b28b198d6899d879f4dcdecdc570c882 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Tue, 28 Nov 2023 20:28:38 +0100 Subject: [PATCH 15/93] minor change --- .../devonfw/tools/ide/commandlet/RepositoryCommandlet.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java index 5faac1273..1be3a0a9a 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java @@ -22,10 +22,7 @@ public class RepositoryCommandlet extends Commandlet { /** the repository to setup. */ public final PathProperty repository; - - public final Path repositoriesPath = context.getSettingsPath().resolve("repositories"); - Path legacyRepositoriesPath = this.context.getSettingsPath().resolve("projects"); - + /** * The constructor. * From db8008a07cd2482163bd57a9e3bbb9ccb6cb383e Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Tue, 28 Nov 2023 20:30:57 +0100 Subject: [PATCH 16/93] Update RepositoryCommandlet.java --- .../com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java index 1be3a0a9a..a45aa2556 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java @@ -22,7 +22,7 @@ public class RepositoryCommandlet extends Commandlet { /** the repository to setup. */ public final PathProperty repository; - + /** * The constructor. * From a64b959e0b0626d350f08f017082b9c81cc4565a Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Tue, 28 Nov 2023 23:56:39 +0100 Subject: [PATCH 17/93] made variable constant --- .../tools/ide/commandlet/UpdateCommandlet.java | 17 +++++++++++------ .../com/devonfw/tools/ide/repo/CustomTool.java | 3 +-- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java index 097356c6f..2165d7d7d 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java @@ -9,8 +9,13 @@ import java.nio.file.Path; import java.util.*; +/** + * {@link Commandlet} to update settings, software and repositories + */ public class UpdateCommandlet extends Commandlet { + private static final String SETTINGS_REPO_URL = "https://github.com/devonfw/ide-settings"; + /** * The constructor. * @@ -36,13 +41,14 @@ public void run() { updateRepositories(); } + private void updateSettings() { this.context.info("Updating settings repository ..."); Path settingsPath = this.context.getSettingsPath(); if (Files.isDirectory(settingsPath)) { // perform git pull on the settings repo - this.context.gitPullOrClone(settingsPath, "https://github.com/devonfw/ide-settings"); + this.context.gitPullOrClone(settingsPath, SETTINGS_REPO_URL); this.context.success("Successfully updated settings repository."); } else { throw new IllegalStateException("Cannot find settings repository."); @@ -52,17 +58,16 @@ private void updateSettings() { private void updateSoftware() { Set toolCommandlets = new HashSet<>(); - Set failedInstalls = new HashSet<>(); Map tool2Exception = new HashMap<>(); - // installed tools + // installed tools in IDE_HOME/software List softwares = this.context.getFileAccess().getFilesInDir(this.context.getSoftwarePath(), p -> true); for (Path software : softwares) { String toolName = software.getFileName().toString(); try { toolCommandlets.add(this.context.getCommandletManager().getToolCommandlet(toolName)); } catch (IllegalArgumentException e) { - //tool is a custom tool, ignore + //tool is a custom tool, ignore ...s } } @@ -90,8 +95,8 @@ private void updateSoftware() { } - if (failedInstalls.isEmpty()) { - this.context.success("All tools were installed successfully."); + if (tool2Exception.isEmpty()) { + this.context.success("All tools were successfully installed."); } else { this.context.warning("Following tools could not be installed:"); for (String toolName : tool2Exception.keySet()) diff --git a/cli/src/main/java/com/devonfw/tools/ide/repo/CustomTool.java b/cli/src/main/java/com/devonfw/tools/ide/repo/CustomTool.java index 88692a5d2..474c6bf25 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/repo/CustomTool.java +++ b/cli/src/main/java/com/devonfw/tools/ide/repo/CustomTool.java @@ -88,8 +88,7 @@ public CustomTool(String tool, VersionIdentifier versionIdentifier, boolean osAg sb.append(this.arch); } sb.append(".tgz"); - //this.url = sb.toString(); - this.url = repositoryUrl; + this.url = sb.toString(); } @Override From 6f265e18820848bb04d01b1fdcae14c38a119666 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Wed, 29 Nov 2023 00:02:55 +0100 Subject: [PATCH 18/93] added the help function --- cli/src/main/resources/nls/Ide.properties | 1 + cli/src/main/resources/nls/Ide_de.properties | 1 + 2 files changed, 2 insertions(+) diff --git a/cli/src/main/resources/nls/Ide.properties b/cli/src/main/resources/nls/Ide.properties index fe6032054..cce49ae56 100644 --- a/cli/src/main/resources/nls/Ide.properties +++ b/cli/src/main/resources/nls/Ide.properties @@ -11,6 +11,7 @@ cmd-gradle=Tool commandlet for Gradle (Build-Tool) cmd-helm=Tool commandlet for Helm (Kubernetes Package Manager) cmd-help=Prints this help. cmd-install=Install the selected tool. +cmd-update=update settings, software and repositories. cmd-java=Tool commandlet for Java (OpenJDK) cmd-kotlinc=Tool commandlet for Kotlin. cmd-kotlincnative=Tool commandlet for Kotlin-Native. diff --git a/cli/src/main/resources/nls/Ide_de.properties b/cli/src/main/resources/nls/Ide_de.properties index b1178f925..0fbbb5abf 100644 --- a/cli/src/main/resources/nls/Ide_de.properties +++ b/cli/src/main/resources/nls/Ide_de.properties @@ -10,6 +10,7 @@ cmd-gh=Werkzeug Kommando für die Github Kommandoschnittstelle. cmd-helm=Werkzeug Kommando für Helm (Kubernetes Package Manager) cmd-help=Zeigt diese Hilfe an. cmd-install=Installiert das selektierte Werkzeug. +cmd-update=Updatet die Settings, Software und Repositories. cmd-java=Werkzeug Kommando für Java (OpenJDK) cmd-kotlinc=Werkzeug Kommando für Kotlin. cmd-kotlincnative=Werkzeug Kommando für Kotlin-Native. From 77fd65100f1d8c5bc56cabb20c6c776f23eddad7 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Fri, 1 Dec 2023 00:35:39 +0100 Subject: [PATCH 19/93] changes after review --- .../tools/ide/commandlet/UpdateCommandlet.java | 14 +++++++------- .../com/devonfw/tools/ide/io/FileAccessImpl.java | 5 +---- .../tools/ide/tool/LocalToolCommandlet.java | 2 +- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java index 2165d7d7d..7c7fffa11 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java @@ -58,16 +58,16 @@ private void updateSettings() { private void updateSoftware() { Set toolCommandlets = new HashSet<>(); - Map tool2Exception = new HashMap<>(); + Map tool2exception = new HashMap<>(); // installed tools in IDE_HOME/software - List softwares = this.context.getFileAccess().getFilesInDir(this.context.getSoftwarePath(), p -> true); + List softwares = this.context.getFileAccess().getFilesInDir(this.context.getSoftwarePath(), path -> true); for (Path software : softwares) { String toolName = software.getFileName().toString(); try { toolCommandlets.add(this.context.getCommandletManager().getToolCommandlet(toolName)); } catch (IllegalArgumentException e) { - //tool is a custom tool, ignore ...s + //tool is a custom tool, ignore ... } } @@ -90,17 +90,17 @@ private void updateSoftware() { this.context.step("Setting up {}", toolCommandlet.getName()); toolCommandlet.install(false); } catch (Exception e) { - tool2Exception.put(toolCommandlet.getName(), e.getMessage()); + tool2exception.put(toolCommandlet.getName(), e.getMessage()); } } - if (tool2Exception.isEmpty()) { + if (tool2exception.isEmpty()) { this.context.success("All tools were successfully installed."); } else { this.context.warning("Following tools could not be installed:"); - for (String toolName : tool2Exception.keySet()) - this.context.warning("{}: {}", toolName, tool2Exception.get(toolName)); + for (String toolName : tool2exception.keySet()) + this.context.warning("{}: {}", toolName, tool2exception.get(toolName)); } } diff --git a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java index 4507d3781..2636f9592 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java @@ -460,15 +460,12 @@ private Path findFirstRecursive(Path dir, Predicate filter, boolean recurs @Override public List getFilesInDir(Path dir, Predicate filter) { - List files = null; + List files = new ArrayList<>(); try (Stream childStream = Files.list(dir)) { Iterator iterator = childStream.iterator(); while (iterator.hasNext()) { Path child = iterator.next(); if (filter.test(child)) { - if (files == null) { - files = new ArrayList<>(); - } files.add(child); } } diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/LocalToolCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/tool/LocalToolCommandlet.java index 8505dead7..df61914bb 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/LocalToolCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/LocalToolCommandlet.java @@ -80,7 +80,7 @@ protected boolean doInstall(boolean silent) { if (installedVersion == null) { this.context.success("Successfully installed {} in version {}", this.tool, resolvedVersion); } else { - this.context.success("Successfully installed {} in version {} replacing previous version {]", this.tool, + this.context.success("Successfully installed {} in version {} replacing previous version {}", this.tool, resolvedVersion, installedVersion); } postInstall(); From 82cc617d0804d063ad21dc2344b3edd63453766f Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Fri, 1 Dec 2023 00:37:52 +0100 Subject: [PATCH 20/93] changes after review --- .../main/java/com/devonfw/tools/ide/io/FileAccessImpl.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java index 4507d3781..2636f9592 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java @@ -460,15 +460,12 @@ private Path findFirstRecursive(Path dir, Predicate filter, boolean recurs @Override public List getFilesInDir(Path dir, Predicate filter) { - List files = null; + List files = new ArrayList<>(); try (Stream childStream = Files.list(dir)) { Iterator iterator = childStream.iterator(); while (iterator.hasNext()) { Path child = iterator.next(); if (filter.test(child)) { - if (files == null) { - files = new ArrayList<>(); - } files.add(child); } } From f1fede655c7aba9eabbf63f26017cb3c3819ba79 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Fri, 1 Dec 2023 00:46:41 +0100 Subject: [PATCH 21/93] reviewed changes --- .../com/devonfw/tools/ide/commandlet/UpdateCommandlet.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java index 7c7fffa11..691c1f030 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java @@ -58,7 +58,6 @@ private void updateSettings() { private void updateSoftware() { Set toolCommandlets = new HashSet<>(); - Map tool2exception = new HashMap<>(); // installed tools in IDE_HOME/software List softwares = this.context.getFileAccess().getFilesInDir(this.context.getSoftwarePath(), path -> true); @@ -85,6 +84,7 @@ private void updateSoftware() { } // update/install the toolCommandlets + Map tool2exception = new HashMap<>(); for (ToolCommandlet toolCommandlet : toolCommandlets) { try { this.context.step("Setting up {}", toolCommandlet.getName()); @@ -94,7 +94,6 @@ private void updateSoftware() { } } - if (tool2exception.isEmpty()) { this.context.success("All tools were successfully installed."); } else { From dbc3bd3ac295ce4d61daf4d0bb4c7adda9512358 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Wed, 13 Dec 2023 01:47:59 +0100 Subject: [PATCH 22/93] Update cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jörg Hohwiller --- .../com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java index a45aa2556..89c08622c 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java @@ -85,7 +85,7 @@ public void run() { private void doImportRepository(Path repositoryFile, boolean forceMode) { - this.context.info("Importing from {} ...", repositoryFile.toString()); + this.context.info("Importing repository from {} ...", repositoryFile.getFileName().toString()); RepositoryConfig repositoryConfig = loadProperties(repositoryFile); if (!repositoryConfig.active()) { From a582601b1949393da30bf898075ffc21452f831f Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Wed, 13 Dec 2023 01:48:08 +0100 Subject: [PATCH 23/93] Update cli/src/main/resources/nls/Ide_de.properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jörg Hohwiller --- cli/src/main/resources/nls/Ide_de.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/main/resources/nls/Ide_de.properties b/cli/src/main/resources/nls/Ide_de.properties index b1178f925..1c3099a49 100644 --- a/cli/src/main/resources/nls/Ide_de.properties +++ b/cli/src/main/resources/nls/Ide_de.properties @@ -5,7 +5,7 @@ options=Optionen: cmd-az=Werkzeug Kommando fuer Azure Kommandoschnittstelle. cmd-env=Gibt die zu setztenden und exportierenden Umgebungsvariablen aus. cmd-get-version=Zeigt die Version des selektierten Werkzeugs an. -cmd-repository=Richtet das vorkonfigurierte Projekt ein. +cmd-repository=Richtet das vorkonfigurierte Git Repository ein. cmd-gh=Werkzeug Kommando für die Github Kommandoschnittstelle. cmd-helm=Werkzeug Kommando für Helm (Kubernetes Package Manager) cmd-help=Zeigt diese Hilfe an. From 7e3bdd9f892aeb03113c799bae0d70a70b422ccf Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Wed, 13 Dec 2023 01:48:28 +0100 Subject: [PATCH 24/93] Update cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jörg Hohwiller --- .../com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java index 89c08622c..95b454fb3 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java @@ -72,7 +72,7 @@ public void run() { if (repositories == null) return; List propertiesFiles = this.context.getFileAccess().getFilesInDir(repositories, - path -> "properties".equals(FilenameUtil.getExtension(path.getFileName().toString()))); + path -> path.getFileName().toString().endsWith(".properties"))); if (propertiesFiles != null) { boolean forceMode = this.context.isForceMode(); From d71f68ee1a170794f080342302e2f7f3b86d1c72 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Wed, 13 Dec 2023 01:48:46 +0100 Subject: [PATCH 25/93] Update cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jörg Hohwiller --- cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java index 237fb8c6b..ad0921581 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java +++ b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java @@ -136,7 +136,7 @@ default void copy(Path source, Path target) { * Retrieves a list of file paths from the specified directory that satisfy the given predicate. * @param dir the folder to iterate through * @param filter the {@link Predicate} that determines whether a file should be included in the list. - * @return a list of paths that satisfy the provided {@link Predicate} or {@code null} if no match was found. + * @return a list of paths that satisfy the provided {@link Predicate}. Will be {@link List#isEmpty() empty} if no match was found but is never {@code null}. */ List getFilesInDir(Path dir, Predicate filter); From 1e5aba8d923551f4601d873c550dfd72d1471ff0 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Wed, 13 Dec 2023 01:49:08 +0100 Subject: [PATCH 26/93] Update cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jörg Hohwiller --- .../com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java index 95b454fb3..d7076a443 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java @@ -32,7 +32,7 @@ public RepositoryCommandlet(IdeContext context) { super(context); addKeyword(getName()); - add(new StringProperty("setup", true, null)); + addKeyword("setup"); this.repository = add(new PathProperty("", false, "repository")); } From c8cf771e0216708ee2f701e55470a7e8fb15c0ab Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Wed, 13 Dec 2023 01:49:29 +0100 Subject: [PATCH 27/93] Update cli/src/main/resources/nls/Ide.properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jörg Hohwiller --- cli/src/main/resources/nls/Ide.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/main/resources/nls/Ide.properties b/cli/src/main/resources/nls/Ide.properties index fe6032054..a704a24bd 100644 --- a/cli/src/main/resources/nls/Ide.properties +++ b/cli/src/main/resources/nls/Ide.properties @@ -5,7 +5,7 @@ options=Options: cmd-az=Tool commandlet for Azure CLI. cmd-env=Print the environment variables to set and export. cmd-get-version=Get the version of the selected tool. -cmd-repository=setup the pre-configured repository. +cmd-repository=setup the pre-configured git repository. cmd-gh=Tool commandlet for Github CLI. cmd-gradle=Tool commandlet for Gradle (Build-Tool) cmd-helm=Tool commandlet for Helm (Kubernetes Package Manager) From 4b601354937eb29e9e0dad48a60ab2144140be0e Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Wed, 13 Dec 2023 01:49:38 +0100 Subject: [PATCH 28/93] Update cli/src/main/resources/nls/Ide.properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jörg Hohwiller --- cli/src/main/resources/nls/Ide.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/main/resources/nls/Ide.properties b/cli/src/main/resources/nls/Ide.properties index a704a24bd..25e31bf8d 100644 --- a/cli/src/main/resources/nls/Ide.properties +++ b/cli/src/main/resources/nls/Ide.properties @@ -26,7 +26,7 @@ val-args=The commandline arguments to pass to the tool. val-tool=The tool commandlet to select. val-version=The tool version. val-set-version-version=The tool version to set. -val-repository-repository=The pre-configured repository to setup, omit to setup all active repositories. +val-repository-repository=The name of the properties file of the pre-configured git repository to setup, omit to setup all active repositories. version-banner=Current version of IDE is {} opt--batch=enable batch mode (non-interactive) opt--debug=enable debug logging From 061cfa52013ee82122b995a618e0e11e9b23878d Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Wed, 13 Dec 2023 01:49:51 +0100 Subject: [PATCH 29/93] Update cli/src/main/resources/nls/Ide_de.properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jörg Hohwiller --- cli/src/main/resources/nls/Ide_de.properties | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cli/src/main/resources/nls/Ide_de.properties b/cli/src/main/resources/nls/Ide_de.properties index 1c3099a49..b7e15fef2 100644 --- a/cli/src/main/resources/nls/Ide_de.properties +++ b/cli/src/main/resources/nls/Ide_de.properties @@ -25,8 +25,7 @@ val-args=Die Kommandozeilen-Argumente zur Übergabe an das Werkzeug. val-tool=Das zu selektierende Werkzeug Kommando. val-version=Die Werkzeug Version. val-set-version-version=Die zu setztende Werkzeug Version. -val-repository-project= Das vorkonfigurierte Projekt zum Einrichten. - +val-repository-repository=Der Name der Properties-Datei des vorkonfigurierten Git Repositories zum Einrichten. Falls nicht angegeben, werden alle aktiven Projekte eingerichtet. version-banner=Die aktuelle Version der IDE ist {} opt--batch=Aktiviert den Batch-Modus (nicht-interaktive Stapelverarbeitung) opt--debug=Aktiviert Debug-Ausgaben (Fehleranalyse) From c314c08ff5a27792af81e0ba978d103144d6b2fa Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Wed, 13 Dec 2023 01:50:29 +0100 Subject: [PATCH 30/93] reviewed changes --- .../ide/commandlet/RepositoryCommandlet.java | 25 +++++++++++-------- .../devonfw/tools/ide/context/IdeContext.java | 6 +++++ 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java index a45aa2556..8350d21fb 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java @@ -45,8 +45,8 @@ public String getName() { @Override public void run() { - Path repositoriesPath = this.context.getSettingsPath().resolve("repositories"); - Path legacyRepositoriesPath = this.context.getSettingsPath().resolve("projects"); + Path repositoriesPath = this.context.getSettingsPath().resolve(context.FOLDER_REPOSITORIES); + Path legacyRepositoriesPath = this.context.getSettingsPath().resolve(context.FOLDER_LEGACY_REPOSITORIES); Path repositoryFile = repository.getValue(); if (repositoryFile != null) { @@ -66,19 +66,22 @@ public void run() { doImportRepository(repositoryFile, true); } else { // If no specific repository is provided, check for repositories folder - Path repositories = Files.exists(repositoriesPath) ? repositoriesPath : - Files.exists(legacyRepositoriesPath) ? legacyRepositoriesPath : null; - - if (repositories == null) return; + 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 propertiesFiles = this.context.getFileAccess().getFilesInDir(repositories, path -> "properties".equals(FilenameUtil.getExtension(path.getFileName().toString()))); - if (propertiesFiles != null) { - boolean forceMode = this.context.isForceMode(); - for (Path propertiesFile : propertiesFiles) { - doImportRepository(propertiesFile, forceMode); - } + boolean forceMode = this.context.isForceMode(); + for (Path propertiesFile : propertiesFiles) { + doImportRepository(propertiesFile, forceMode); } } } diff --git a/cli/src/main/java/com/devonfw/tools/ide/context/IdeContext.java b/cli/src/main/java/com/devonfw/tools/ide/context/IdeContext.java index 0c8d0782f..a1a584d28 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/context/IdeContext.java +++ b/cli/src/main/java/com/devonfw/tools/ide/context/IdeContext.java @@ -58,6 +58,12 @@ public interface IdeContext extends IdeLogger { /** The name of the bin folder where executable files are found by default. */ String FOLDER_BIN = "bin"; + /** The name of the repositories folder where properties files are stores for each repository */ + String FOLDER_REPOSITORIES = "repositories"; + + /** The name of the repositories folder where properties files are stores for each repository */ + String FOLDER_LEGACY_REPOSITORIES = "projects"; + /** The name of the Contents folder inside a MacOS app. */ String FOLDER_CONTENTS = "Contents"; From 07d4a341b290da94df2ef5b33d775564b40a112a Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Wed, 13 Dec 2023 01:51:58 +0100 Subject: [PATCH 31/93] Update cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jörg Hohwiller --- .../ide/commandlet/RepositoryCommandlet.java | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java index d7076a443..c280c0f5f 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java @@ -124,16 +124,13 @@ private void doImportRepository(Path repositoryFile, boolean forceMode) { this.context.debug("Building project with ide command: {}", buildCmd); if (buildCmd != null && !buildCmd.isEmpty()) { String[] command = buildCmd.split("\\s+"); - this.context.getCommandletManager().getToolCommandlet(command[0]).install(true); - ProcessContext pc = this.context.newProcess(); - pc.executable(command[0]); - pc.addArgs(Arrays.copyOfRange(command, 1, command.length)); - Path buildPath = repositoryPath; - if (repositoryConfig.buildPath() != null) { - buildPath = buildPath.resolve(repositoryConfig.buildPath()); + ToolCommandlet commandlet = this.context.getCommandletManager().getToolCommandlet(command[0]); + List args = new java.util.ArrayList<>(command.length - 1); + for (int i = 1; i < command.length; i++) { + args.add(command[i]); } - pc.directory(buildPath); - pc.run(); + commandlet.arguments.setValue(args); + commandlet.run(); } else { this.context.info("Build command not set. Skipping build for repository."); } From 37da950e481683da8617354b5df8639d2246ec52 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Wed, 13 Dec 2023 01:52:30 +0100 Subject: [PATCH 32/93] updates --- .../com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java index 86e2cfbc0..b8434b572 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java @@ -77,7 +77,7 @@ public void run() { } List propertiesFiles = this.context.getFileAccess().getFilesInDir(repositories, - path -> path.getFileName().toString().endsWith(".properties"))); + path -> path.getFileName().toString().endsWith(".properties")); boolean forceMode = this.context.isForceMode(); for (Path propertiesFile : propertiesFiles) { From 05bcde10517b55f6883a33c88d44aba586fd4471 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Wed, 13 Dec 2023 02:15:44 +0100 Subject: [PATCH 33/93] reviewed changes --- .../tools/ide/commandlet/RepositoryCommandlet.java | 12 +++++------- .../java/com/devonfw/tools/ide/io/FileAccess.java | 2 +- .../com/devonfw/tools/ide/io/FileAccessImpl.java | 2 +- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java index d909a1349..f934db195 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java @@ -1,17 +1,15 @@ package com.devonfw.tools.ide.commandlet; import com.devonfw.tools.ide.context.IdeContext; -import com.devonfw.tools.ide.process.ProcessContext; import com.devonfw.tools.ide.property.PathProperty; -import com.devonfw.tools.ide.property.StringProperty; -import com.devonfw.tools.ide.util.FilenameUtil; +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.Arrays; +import java.util.ArrayList; import java.util.List; import java.util.Properties; @@ -76,7 +74,7 @@ public void run() { return; } - List propertiesFiles = this.context.getFileAccess().getFilesInDir(repositories, + List propertiesFiles = this.context.getFileAccess().getChildrenInDir(repositories, path -> path.getFileName().toString().endsWith(".properties")); boolean forceMode = this.context.isForceMode(); @@ -124,11 +122,11 @@ private void doImportRepository(Path repositoryFile, boolean forceMode) { this.context.gitPullOrClone(repositoryPath, gitUrl); String buildCmd = repositoryConfig.buildCmd(); - this.context.debug("Building project with ide command: {}", 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 args = new java.util.ArrayList<>(command.length - 1); + List args = new ArrayList<>(command.length - 1); for (int i = 1; i < command.length; i++) { args.add(command[i]); } diff --git a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java index ad0921581..dbf787c8f 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java +++ b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java @@ -138,6 +138,6 @@ default void copy(Path source, Path target) { * @param filter the {@link Predicate} that determines whether a file should be included in the list. * @return a list of paths that satisfy the provided {@link Predicate}. Will be {@link List#isEmpty() empty} if no match was found but is never {@code null}. */ - List getFilesInDir(Path dir, Predicate filter); + List getChildrenInDir(Path dir, Predicate filter); } diff --git a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java index 2636f9592..d7611736d 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java @@ -459,7 +459,7 @@ private Path findFirstRecursive(Path dir, Predicate filter, boolean recurs } @Override - public List getFilesInDir(Path dir, Predicate filter) { + public List getChildrenInDir(Path dir, Predicate filter) { List files = new ArrayList<>(); try (Stream childStream = Files.list(dir)) { Iterator iterator = childStream.iterator(); From f2ee210c4a90507d0ae3c8694aa64efdfab5edba Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Wed, 13 Dec 2023 03:11:30 +0100 Subject: [PATCH 34/93] reviewed changes --- .../ide/commandlet/RepositoryCommandlet.java | 32 +++----------- .../ide/commandlet/RepositoryConfig.java | 43 ++++++++++++++++++- 2 files changed, 47 insertions(+), 28 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java index f934db195..5ee6a9db1 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java @@ -13,6 +13,8 @@ import java.util.List; import java.util.Properties; +import static com.devonfw.tools.ide.commandlet.RepositoryConfig.loadProperties; + /** * {@link Commandlet} to setup a repository */ @@ -136,32 +138,10 @@ private void doImportRepository(Path repositoryFile, boolean forceMode) { this.context.info("Build command not set. Skipping build for repository."); } - if ("import".equals(repositoryConfig.eclipse()) && !Files.exists(repositoryPath.resolve(".project"))) { - //TODO: import repository to eclipse - } - } - - - private 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); + if (!Files.exists(repositoryPath.resolve(".project"))) { + for (String ideCommandlet : repositoryConfig.imports()) { + //TODO: import repository to ideCommandlet + } } - - 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"), - properties.getProperty("eclipse"), - Boolean.parseBoolean(properties.getProperty("active")) - ); } - } diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryConfig.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryConfig.java index e03d2765a..952fde680 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryConfig.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryConfig.java @@ -1,5 +1,13 @@ 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}. * @@ -10,7 +18,7 @@ * @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 eclipse Desired action for eclipse IDE. If equals to "import" all modules will be imported into eclipse. + * @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( @@ -21,6 +29,37 @@ public record RepositoryConfig( String gitBranch, String buildPath, String buildCmd, - String eclipse, + Set 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 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"))); + } + + private static Set 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(); + } + } } From 5773121b21c79da2b690763917395f1bb9b02ea6 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Fri, 15 Dec 2023 11:31:16 +0100 Subject: [PATCH 35/93] implemented repositoryProperty --- .../ide/commandlet/RepositoryCommandlet.java | 19 ++--- .../ide/property/RepositoryProperty.java | 70 +++++++++++++++++++ 2 files changed, 74 insertions(+), 15 deletions(-) create mode 100644 cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java index 5ee6a9db1..4587b2161 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java @@ -2,6 +2,7 @@ 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; @@ -21,7 +22,7 @@ public class RepositoryCommandlet extends Commandlet { /** the repository to setup. */ - public final PathProperty repository; + public final RepositoryProperty repository; /** * The constructor. @@ -33,7 +34,7 @@ public RepositoryCommandlet(IdeContext context) { super(context); addKeyword(getName()); addKeyword("setup"); - this.repository = add(new PathProperty("", false, "repository")); + this.repository = add(new RepositoryProperty("", false, "repository")); } @Override @@ -47,22 +48,10 @@ public void run() { Path repositoriesPath = this.context.getSettingsPath().resolve(context.FOLDER_REPOSITORIES); Path legacyRepositoriesPath = this.context.getSettingsPath().resolve(context.FOLDER_LEGACY_REPOSITORIES); - Path repositoryFile = repository.getValue(); + Path repositoryFile = repository.getValueAsPath(context); if (repositoryFile != null) { // Handle the case when a specific repository is provided - if (!Files.exists(repositoryFile)) { - repositoryFile = repositoriesPath.resolve(repositoryFile.getFileName().toString() + ".properties"); - } - if (!Files.exists(repositoryFile)) { - Path legacyRepositoryFile = legacyRepositoriesPath.resolve(repositoryFile.getFileName().toString()); - if (Files.exists(legacyRepositoryFile)) { - repositoryFile = legacyRepositoryFile; - } else { - this.context.warning("Could not find " + repositoryFile); - return; - } - } doImportRepository(repositoryFile, true); } else { // If no specific repository is provided, check for repositories folder diff --git a/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java b/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java new file mode 100644 index 000000000..0d10c6144 --- /dev/null +++ b/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java @@ -0,0 +1,70 @@ +package com.devonfw.tools.ide.property; + +import com.devonfw.tools.ide.context.IdeContext; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.function.Consumer; + +public class RepositoryProperty extends Property { + + /** + * The constructor. + * + * @param name the {@link #getName() property name}. + * @param required the {@link #isRequired() required flag}. + * @param alias the {@link #getAlias() property alias}. + */ + public RepositoryProperty(String name, boolean required, String alias) { + + this(name, required, alias, null); + } + + /** + * The constructor. + * + * @param name the {@link #getName() property name}. + * @param required the {@link #isRequired() required flag}. + * @param alias the {@link #getAlias() property alias}. + * @param validator the {@link Consumer} used to {@link #validate() validate} the {@link #getValue() value}. + */ + public RepositoryProperty(String name, boolean required, String alias, Consumer validator) { + + super(name, required, alias, validator); + } + + @Override + public Class getValueType() { + + return String.class; + } + + @Override + public String parse(String valueAsString) { + + return valueAsString; + } + + public Path getValueAsPath(IdeContext context) { + + Path repositoriesPath = context.getSettingsPath().resolve(context.FOLDER_REPOSITORIES); + Path legacyRepositoriesPath = context.getSettingsPath().resolve(context.FOLDER_LEGACY_REPOSITORIES); + + Path repositoryFile = Path.of(super.getValue()); + // Handle the case when a specific repository is provided + if (!Files.exists(repositoryFile)) { + repositoryFile = repositoriesPath.resolve(repositoryFile.getFileName().toString() + ".properties"); + } + if (!Files.exists(repositoryFile)) { + Path legacyRepositoryFile = legacyRepositoriesPath.resolve(repositoryFile.getFileName().toString()); + if (Files.exists(legacyRepositoryFile)) { + repositoryFile = legacyRepositoryFile; + } else { + throw new IllegalStateException("Could not find " + repositoryFile); + } + } + return repositoryFile; + } + + +} From fe607d27e386c4db298787b3aa3ee52976b482ae Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Tue, 19 Dec 2023 02:13:58 +0100 Subject: [PATCH 36/93] Update cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jörg Hohwiller --- .../java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java index 691c1f030..3306eb0a6 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java @@ -60,7 +60,7 @@ private void updateSoftware() { Set toolCommandlets = new HashSet<>(); // installed tools in IDE_HOME/software - List softwares = this.context.getFileAccess().getFilesInDir(this.context.getSoftwarePath(), path -> true); + List softwares = this.context.getFileAccess().getFilesInDir(this.context.getSoftwarePath(), Files::isDirectory); for (Path software : softwares) { String toolName = software.getFileName().toString(); try { From 91d057031ba72b05db356ff0586c5e4ff95e4e4d Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Tue, 19 Dec 2023 02:15:07 +0100 Subject: [PATCH 37/93] Update cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jörg Hohwiller --- .../com/devonfw/tools/ide/commandlet/UpdateCommandlet.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java index 3306eb0a6..5c4192aaa 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java @@ -71,8 +71,7 @@ private void updateSoftware() { } // regular tools in $IDE_TOOLS - String regularToolsString = this.context.getVariables().get("IDE_TOOLS"); - String[] regularTools = regularToolsString.split(",\\s"); + List regularTools = IdeVariables.IDE_TOOLS.get(this.context); for (String regularTool : regularTools) { toolCommandlets.add(this.context.getCommandletManager().getToolCommandlet(regularTool)); } From d047d6213f59e1daf9ccb3e91de1e62d28cfad56 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Tue, 19 Dec 2023 02:21:04 +0100 Subject: [PATCH 38/93] removed useless comment --- .../java/com/devonfw/tools/ide/property/RepositoryProperty.java | 1 - 1 file changed, 1 deletion(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java b/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java index 0d10c6144..2a52ec5bc 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java +++ b/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java @@ -51,7 +51,6 @@ public Path getValueAsPath(IdeContext context) { Path legacyRepositoriesPath = context.getSettingsPath().resolve(context.FOLDER_LEGACY_REPOSITORIES); Path repositoryFile = Path.of(super.getValue()); - // Handle the case when a specific repository is provided if (!Files.exists(repositoryFile)) { repositoryFile = repositoriesPath.resolve(repositoryFile.getFileName().toString() + ".properties"); } From 639721381e8324f8df6bab8c5dc7f2cac4f6fd5f Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Tue, 19 Dec 2023 02:23:54 +0100 Subject: [PATCH 39/93] merged #100 --- .../java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java index 691c1f030..0320e986e 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java @@ -60,7 +60,7 @@ private void updateSoftware() { Set toolCommandlets = new HashSet<>(); // installed tools in IDE_HOME/software - List softwares = this.context.getFileAccess().getFilesInDir(this.context.getSoftwarePath(), path -> true); + List softwares = this.context.getFileAccess().getChildrenInDir(this.context.getSoftwarePath(), path -> true); for (Path software : softwares) { String toolName = software.getFileName().toString(); try { From a0eb88fa8e92bbf75d7d80cd49ede8ccaae3403e Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Tue, 19 Dec 2023 04:13:12 +0100 Subject: [PATCH 40/93] small change --- .../java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java index 09a562d4b..6e77ca1e4 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java @@ -39,7 +39,7 @@ public void run() { updateSettings(); updateSoftware(); - //updateRepositories(); + updateRepositories(); } From c53d072f41e12fa0df91bfdd384e71100060fbca Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Tue, 19 Dec 2023 04:22:55 +0100 Subject: [PATCH 41/93] javadoc for StepContainer class --- .../tools/ide/commandlet/StepContainer.java | 31 +++++++++++++++++++ .../tools/ide/context/AbstractIdeContext.java | 3 -- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/StepContainer.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/StepContainer.java index 074c095d8..159a58bfc 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/StepContainer.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/StepContainer.java @@ -6,13 +6,27 @@ import java.util.ArrayList; import java.util.List; +/** + * A utility class to manage and log the progress of steps in a process. + * Each step can be started, ended with success or failure, and the overall completion + * status can be checked. + * @throws CliException if one or more steps fail. + */ public class StepContainer { private final IdeContext context; + /** List of steps that ended successfully. */ private List successfulSteps; + + /** List of steps that failed. */ private List failedSteps; + /** + * The constructor. + * + * @param context the {@link IdeContext}. + */ public StepContainer(IdeContext context) { this.context = context; @@ -20,11 +34,23 @@ public StepContainer(IdeContext context) { failedSteps = new ArrayList<>(); } + /** + * Logs the start of a step. + * + * @param stepName the name of the step. + */ public void startStep(String stepName) { this.context.step("Starting step: {}", stepName); } + /** + * Logs the end of a step, indicating success or failure. + * + * @param stepName the name of the step. + * @param success {@code true} if the step succeeded, {@code false} otherwise. + * @param e the exception associated with the failure, or {@code null} if the step succeeded. + */ public void endStep(String stepName, boolean success, Throwable e) { if (success) { @@ -39,6 +65,11 @@ public void endStep(String stepName, boolean success, Throwable e) { } } + /** + * Checks the overall completion status of all steps. + * + * @throws CliException if one or more steps fail, providing a detailed summary. + */ public void complete() { if (failedSteps.isEmpty()) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java b/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java index e4d652685..3843463fb 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java +++ b/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java @@ -87,8 +87,6 @@ public abstract class AbstractIdeContext implements IdeContext { private final FileAccess fileAccess; - private final StepContainer stepContainer; - private final CommandletManager commandletManager; private final ToolRepository defaultToolRepository; @@ -132,7 +130,6 @@ public AbstractIdeContext(IdeLogLevel minLogLevel, Function Date: Tue, 19 Dec 2023 04:34:20 +0100 Subject: [PATCH 42/93] small changes --- .../java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java | 1 + .../devonfw/tools/ide/{commandlet => common}/StepContainer.java | 2 +- .../java/com/devonfw/tools/ide/context/AbstractIdeContext.java | 1 - cli/src/main/java/com/devonfw/tools/ide/context/IdeContext.java | 1 - 4 files changed, 2 insertions(+), 3 deletions(-) rename cli/src/main/java/com/devonfw/tools/ide/{commandlet => common}/StepContainer.java (98%) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java index 6e77ca1e4..c70e2fa3c 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java @@ -1,5 +1,6 @@ package com.devonfw.tools.ide.commandlet; +import com.devonfw.tools.ide.common.StepContainer; import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.repo.CustomTool; import com.devonfw.tools.ide.tool.CustomToolCommandlet; diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/StepContainer.java b/cli/src/main/java/com/devonfw/tools/ide/common/StepContainer.java similarity index 98% rename from cli/src/main/java/com/devonfw/tools/ide/commandlet/StepContainer.java rename to cli/src/main/java/com/devonfw/tools/ide/common/StepContainer.java index 159a58bfc..bd11e1e91 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/StepContainer.java +++ b/cli/src/main/java/com/devonfw/tools/ide/common/StepContainer.java @@ -1,4 +1,4 @@ -package com.devonfw.tools.ide.commandlet; +package com.devonfw.tools.ide.common; import com.devonfw.tools.ide.cli.CliException; import com.devonfw.tools.ide.context.IdeContext; diff --git a/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java b/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java index 3843463fb..8bda33623 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java +++ b/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java @@ -14,7 +14,6 @@ import com.devonfw.tools.ide.commandlet.CommandletManager; import com.devonfw.tools.ide.commandlet.CommandletManagerImpl; -import com.devonfw.tools.ide.commandlet.StepContainer; import com.devonfw.tools.ide.common.SystemPath; import com.devonfw.tools.ide.environment.AbstractEnvironmentVariables; import com.devonfw.tools.ide.environment.EnvironmentVariables; diff --git a/cli/src/main/java/com/devonfw/tools/ide/context/IdeContext.java b/cli/src/main/java/com/devonfw/tools/ide/context/IdeContext.java index 8d8430b78..a1a584d28 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/context/IdeContext.java +++ b/cli/src/main/java/com/devonfw/tools/ide/context/IdeContext.java @@ -6,7 +6,6 @@ import com.devonfw.tools.ide.cli.CliAbortException; import com.devonfw.tools.ide.cli.CliException; import com.devonfw.tools.ide.commandlet.CommandletManager; -import com.devonfw.tools.ide.commandlet.StepContainer; import com.devonfw.tools.ide.common.SystemPath; import com.devonfw.tools.ide.environment.EnvironmentVariables; import com.devonfw.tools.ide.environment.EnvironmentVariablesType; From 68ddfa824dc92a98ce30f02c8d82826cee68a5de Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Tue, 19 Dec 2023 04:37:18 +0100 Subject: [PATCH 43/93] removed useless imports --- cli/src/main/java/com/devonfw/tools/ide/tool/ToolCommandlet.java | 1 - 1 file changed, 1 deletion(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/ToolCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/tool/ToolCommandlet.java index 6539317d3..f5ab8b3be 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/ToolCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/ToolCommandlet.java @@ -5,7 +5,6 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; -import java.util.Random; import java.util.Set; import java.util.stream.Stream; From c40042f8ba564468fdf7af1d523d6f6218f00cd4 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Mon, 25 Dec 2023 02:47:22 +0100 Subject: [PATCH 44/93] updated updateSettings when no settingsfolder is found, prompt user for link to clone --- .../ide/commandlet/UpdateCommandlet.java | 32 ++++++++++++++++--- .../tools/ide/context/AbstractIdeContext.java | 8 +++++ .../devonfw/tools/ide/context/IdeContext.java | 2 ++ 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java index c70e2fa3c..8f0b09b5a 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java @@ -2,6 +2,7 @@ import com.devonfw.tools.ide.common.StepContainer; import com.devonfw.tools.ide.context.IdeContext; +import com.devonfw.tools.ide.property.StringProperty; import com.devonfw.tools.ide.repo.CustomTool; import com.devonfw.tools.ide.tool.CustomToolCommandlet; import com.devonfw.tools.ide.tool.ToolCommandlet; @@ -16,7 +17,9 @@ */ public class UpdateCommandlet extends Commandlet { - private static final String SETTINGS_REPO_URL = "https://github.com/devonfw/ide-settings"; + private static final String DEFAULT_SETTINGS_REPO_URL = "https://github.com/devonfw/ide-settings"; + + private final StringProperty settingsRepo; /** * The constructor. @@ -27,6 +30,7 @@ public UpdateCommandlet(IdeContext context) { super(context); addKeyword(getName()); + settingsRepo = add(new StringProperty("", false, "settingsRepository")); } @Override @@ -39,8 +43,8 @@ public String getName() { public void run() { updateSettings(); - updateSoftware(); - updateRepositories(); + //updateSoftware(); + //updateRepositories(); } @@ -50,10 +54,28 @@ private void updateSettings() { Path settingsPath = this.context.getSettingsPath(); if (Files.isDirectory(settingsPath)) { // perform git pull on the settings repo - this.context.gitPullOrClone(settingsPath, SETTINGS_REPO_URL); + this.context.gitPullOrClone(settingsPath, DEFAULT_SETTINGS_REPO_URL); this.context.success("Successfully updated settings repository."); } else { - throw new IllegalStateException("Cannot find settings repository."); + // check if a settings repository is given then clone, otherwise prompt user for a repository. + String repository = settingsRepo.getValue(); + if (repository == null) { + if (this.context.isBatchMode()) { + repository = DEFAULT_SETTINGS_REPO_URL; + } else { + this.context.info("Missing your settings at {} and no SETTINGS_URL is defined.", settingsPath); + this.context.info("Further details can be found here:"); + this.context.info("https://github.com/devonfw/IDEasy/blob/main/documentation/settings.asciidoc"); + this.context.info("Please contact the technical lead of your project to get the SETTINGS_URL for your project."); + this.context.info("In case you just want to test IDEasy you may simply hit return to install the default settings."); + this.context.info(); + repository = this.context.read("Settings URL [" + DEFAULT_SETTINGS_REPO_URL +"]: "); + } + } + if (repository.isBlank()) { + repository = DEFAULT_SETTINGS_REPO_URL; + } + this.context.gitPullOrClone(settingsPath, repository); } } diff --git a/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java b/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java index 8bda33623..b0eee3fcf 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java +++ b/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java @@ -698,6 +698,14 @@ public O question(String question, O... options) { return option; } + + public String read(String msg) { + + interaction(msg); + return readLine(); + } + + /** * @return the input from the end-user (e.g. read from the console). */ diff --git a/cli/src/main/java/com/devonfw/tools/ide/context/IdeContext.java b/cli/src/main/java/com/devonfw/tools/ide/context/IdeContext.java index a1a584d28..c36d4fd85 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/context/IdeContext.java +++ b/cli/src/main/java/com/devonfw/tools/ide/context/IdeContext.java @@ -177,6 +177,8 @@ default boolean question(String question) { @SuppressWarnings("unchecked") O question(String question, O... options); + String read(String message); + /** * Will ask the given question. If the user answers with "yes" the method will return and the process can continue. * Otherwise if the user answers with "no" an exception is thrown to abort further processing. From 25e4167aeb116b0e6584b538157bde5da23b210e Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Tue, 2 Jan 2024 02:20:53 +0100 Subject: [PATCH 45/93] added stepContainer, update settings asks for link when no settingsfolder was found --- .../tools/ide/commandlet/UpdateCommandlet.java | 9 +++++---- .../tools/ide/context/AbstractIdeContext.java | 12 ------------ .../com/devonfw/tools/ide/context/IdeContext.java | 5 ++++- .../devonfw/tools/ide/context/IdeContextConsole.java | 2 +- .../tools/ide/context/AbstractIdeTestContext.java | 2 +- 5 files changed, 11 insertions(+), 19 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java index 8f0b09b5a..3d3ea91e8 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java @@ -43,8 +43,8 @@ public String getName() { public void run() { updateSettings(); - //updateSoftware(); - //updateRepositories(); + updateSoftware(); + updateRepositories(); } @@ -68,8 +68,9 @@ private void updateSettings() { this.context.info("https://github.com/devonfw/IDEasy/blob/main/documentation/settings.asciidoc"); this.context.info("Please contact the technical lead of your project to get the SETTINGS_URL for your project."); this.context.info("In case you just want to test IDEasy you may simply hit return to install the default settings."); - this.context.info(); - repository = this.context.read("Settings URL [" + DEFAULT_SETTINGS_REPO_URL +"]: "); + this.context.info(""); + this.context.info("Settings URL [{}]:", DEFAULT_SETTINGS_REPO_URL); + repository = this.context.readLine(); } } if (repository.isBlank()) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java b/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java index b0eee3fcf..268a8b241 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java +++ b/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java @@ -699,18 +699,6 @@ public O question(String question, O... options) { } - public String read(String msg) { - - interaction(msg); - return readLine(); - } - - - /** - * @return the input from the end-user (e.g. read from the console). - */ - protected abstract String readLine(); - private static void addMapping(Map mapping, String key, O option) { O duplicate = mapping.put(key, option); diff --git a/cli/src/main/java/com/devonfw/tools/ide/context/IdeContext.java b/cli/src/main/java/com/devonfw/tools/ide/context/IdeContext.java index c36d4fd85..d468d7719 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/context/IdeContext.java +++ b/cli/src/main/java/com/devonfw/tools/ide/context/IdeContext.java @@ -177,7 +177,10 @@ default boolean question(String question) { @SuppressWarnings("unchecked") O question(String question, O... options); - String read(String message); + /** + * @return the input from the end-user (e.g. read from the console). + */ + String readLine(); /** * Will ask the given question. If the user answers with "yes" the method will return and the process can continue. diff --git a/cli/src/main/java/com/devonfw/tools/ide/context/IdeContextConsole.java b/cli/src/main/java/com/devonfw/tools/ide/context/IdeContextConsole.java index 0e3844103..82f39e7db 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/context/IdeContextConsole.java +++ b/cli/src/main/java/com/devonfw/tools/ide/context/IdeContextConsole.java @@ -35,7 +35,7 @@ public IdeContextConsole(IdeLogLevel minLogLevel, Appendable out, boolean colore } @Override - protected String readLine() { + public String readLine() { if (this.scanner == null) { return System.console().readLine(); diff --git a/cli/src/test/java/com/devonfw/tools/ide/context/AbstractIdeTestContext.java b/cli/src/test/java/com/devonfw/tools/ide/context/AbstractIdeTestContext.java index d47ab227f..c54b0ca91 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/context/AbstractIdeTestContext.java +++ b/cli/src/test/java/com/devonfw/tools/ide/context/AbstractIdeTestContext.java @@ -42,7 +42,7 @@ public boolean isTest() { } @Override - protected String readLine() { + public String readLine() { if (this.answerIndex >= this.answers.length) { throw new IllegalStateException("End of answers reached!"); From 48db7c38f0b79cb1400d4c1987ceb69b14eb0650 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Tue, 2 Jan 2024 02:32:33 +0100 Subject: [PATCH 46/93] added null check --- .../java/com/devonfw/tools/ide/property/RepositoryProperty.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java b/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java index 2a52ec5bc..26f22ac9d 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java +++ b/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java @@ -51,6 +51,8 @@ public Path getValueAsPath(IdeContext context) { Path legacyRepositoriesPath = context.getSettingsPath().resolve(context.FOLDER_LEGACY_REPOSITORIES); Path repositoryFile = Path.of(super.getValue()); + if (repositoryFile == null) return null; + if (!Files.exists(repositoryFile)) { repositoryFile = repositoriesPath.resolve(repositoryFile.getFileName().toString() + ".properties"); } From 4c2291aa19460133851a6ff5606292bbc5fe7234 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Tue, 2 Jan 2024 02:40:45 +0100 Subject: [PATCH 47/93] Update RepositoryProperty.java --- .../devonfw/tools/ide/property/RepositoryProperty.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java b/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java index 26f22ac9d..60be91c18 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java +++ b/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java @@ -50,8 +50,12 @@ public Path getValueAsPath(IdeContext context) { Path repositoriesPath = context.getSettingsPath().resolve(context.FOLDER_REPOSITORIES); Path legacyRepositoriesPath = context.getSettingsPath().resolve(context.FOLDER_LEGACY_REPOSITORIES); - Path repositoryFile = Path.of(super.getValue()); - if (repositoryFile == null) return null; + Path repositoryFile; + if (super.getValue() != null) { + repositoryFile = Path.of(super.getValue()); + } else { + return null; + } if (!Files.exists(repositoryFile)) { repositoryFile = repositoriesPath.resolve(repositoryFile.getFileName().toString() + ".properties"); From dcecb50ce1c067e4d56dec3fe57461e3e9544862 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Tue, 2 Jan 2024 02:41:32 +0100 Subject: [PATCH 48/93] Update RepositoryProperty.java --- .../devonfw/tools/ide/property/RepositoryProperty.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java b/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java index 26f22ac9d..60be91c18 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java +++ b/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java @@ -50,8 +50,12 @@ public Path getValueAsPath(IdeContext context) { Path repositoriesPath = context.getSettingsPath().resolve(context.FOLDER_REPOSITORIES); Path legacyRepositoriesPath = context.getSettingsPath().resolve(context.FOLDER_LEGACY_REPOSITORIES); - Path repositoryFile = Path.of(super.getValue()); - if (repositoryFile == null) return null; + Path repositoryFile; + if (super.getValue() != null) { + repositoryFile = Path.of(super.getValue()); + } else { + return null; + } if (!Files.exists(repositoryFile)) { repositoryFile = repositoriesPath.resolve(repositoryFile.getFileName().toString() + ".properties"); From f89b889c972a1e52abb0e3af729430525da61a96 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Wed, 3 Jan 2024 00:56:22 +0100 Subject: [PATCH 49/93] created CreateCommandlet --- .../ide/commandlet/CommandletManagerImpl.java | 1 + .../ide/commandlet/CreateCommandlet.java | 54 +++++++++++++++++++ .../com/devonfw/tools/ide/io/FileAccess.java | 14 +++++ .../devonfw/tools/ide/io/FileAccessImpl.java | 14 +++++ cli/src/main/resources/nls/Ide.properties | 2 + 5 files changed, 85 insertions(+) create mode 100644 cli/src/main/java/com/devonfw/tools/ide/commandlet/CreateCommandlet.java diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java index 9f0164e9f..6daa7b120 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java @@ -61,6 +61,7 @@ private CommandletManagerImpl(IdeContext context) { add(new VersionListCommandlet(context)); add(new RepositoryCommandlet(context)); add(new UpdateCommandlet(context)); + add(new CreateCommandlet(context)); add(new Gh(context)); add(new Helm(context)); add(new Java(context)); diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/CreateCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CreateCommandlet.java new file mode 100644 index 000000000..7eea3b932 --- /dev/null +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CreateCommandlet.java @@ -0,0 +1,54 @@ +package com.devonfw.tools.ide.commandlet; + +import com.devonfw.tools.ide.context.IdeContext; +import com.devonfw.tools.ide.property.PathProperty; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +/** + * {@link Commandlet} to create a new IDEasy instance + */ +public class CreateCommandlet extends Commandlet { + + private final PathProperty createPathProperty; + + /** + * The constructor. + * + * @param context the {@link IdeContext}. + */ + public CreateCommandlet(IdeContext context) { + + super(context); + addKeyword(getName()); + createPathProperty = add(new PathProperty("", false, "createPath")); + } + + @Override + public String getName() { + + return "create"; + } + + @Override + public void run() { + + Path createPath = createPathProperty.getValue(); + if (createPath == null || createPath.toString().isBlank()) { + // pwd + createPath = this.context.getCwd(); + } + + if (!Files.isDirectory(createPath)) { + this.context.getFileAccess().mkdirs(createPath); + } + + if (!this.context.getFileAccess().isEmptyDir(createPath)) { + this.context.askToContinue("Directory is not empty, continue?"); + } + + this.context.getCommandletManager().getCommandlet(UpdateCommandlet.class).run(); + } +} diff --git a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java index dbf787c8f..3517cf670 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java +++ b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java @@ -140,4 +140,18 @@ default void copy(Path source, Path target) { */ List getChildrenInDir(Path dir, Predicate filter); + /** + * Retrieves a list of file paths from the specified directory. + * @param dir the folder to iterate through + * @return a list of paths that satisfy the provided {@link Predicate}. Will be {@link List#isEmpty() empty} if no match was found but is never {@code null}. + */ + List getChildrenInDir(Path dir); + + /** + * Checks if a directory is empty. + * @param dir The path of the directory to check. + * @return {@code true} if the directory is empty, {@code false} otherwise. + */ + boolean isEmptyDir(Path dir); + } diff --git a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java index 930685813..16a97a3bf 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java @@ -477,4 +477,18 @@ public List getChildrenInDir(Path dir, Predicate filter) { return files; } + @Override + public List getChildrenInDir(Path dir) { + + return getChildrenInDir(dir, path -> true); + } + + @Override + public boolean isEmptyDir(Path dir) { + + return getChildrenInDir(dir).isEmpty(); + } + + + } diff --git a/cli/src/main/resources/nls/Ide.properties b/cli/src/main/resources/nls/Ide.properties index c83b3ea46..72c447432 100644 --- a/cli/src/main/resources/nls/Ide.properties +++ b/cli/src/main/resources/nls/Ide.properties @@ -6,6 +6,7 @@ cmd-az=Tool commandlet for Azure CLI. cmd-env=Print the environment variables to set and export. cmd-get-version=Get the version of the selected tool. cmd-repository=setup the pre-configured git repository. +cmd-create=create a new IDEasy instance. cmd-gh=Tool commandlet for Github CLI. cmd-gradle=Tool commandlet for Gradle (Build-Tool) cmd-helm=Tool commandlet for Helm (Kubernetes Package Manager) @@ -28,6 +29,7 @@ val-tool=The tool commandlet to select. val-version=The tool version. val-set-version-version=The tool version to set. val-repository-repository=The name of the properties file of the pre-configured git repository to setup, omit to setup all active repositories. +val-create-CreatePath=The path where the new IDEasy instance will be created. version-banner=Current version of IDE is {} opt--batch=enable batch mode (non-interactive) opt--debug=enable debug logging From 4880d124b25711fe8cff4c1fef809ece7c0bf87c Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Sun, 7 Jan 2024 22:25:00 +0100 Subject: [PATCH 50/93] Update cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jörg Hohwiller --- .../com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java index 4587b2161..87351f80c 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java @@ -17,7 +17,7 @@ import static com.devonfw.tools.ide.commandlet.RepositoryConfig.loadProperties; /** - * {@link Commandlet} to setup a repository + * {@link Commandlet} to setup one or multiple GIT repositories for development. */ public class RepositoryCommandlet extends Commandlet { From de061fe4398c3124136853b1f0ded2043df45c2e Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Sun, 7 Jan 2024 22:25:12 +0100 Subject: [PATCH 51/93] Update cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jörg Hohwiller --- .../devonfw/tools/ide/commandlet/RepositoryCommandlet.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java index 87351f80c..e10e75b8e 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java @@ -46,8 +46,8 @@ public String getName() { @Override public void run() { - Path repositoriesPath = this.context.getSettingsPath().resolve(context.FOLDER_REPOSITORIES); - Path legacyRepositoriesPath = this.context.getSettingsPath().resolve(context.FOLDER_LEGACY_REPOSITORIES); + Path repositoriesPath = this.context.getSettingsPath().resolve(IdeContext.FOLDER_REPOSITORIES); + Path legacyRepositoriesPath = this.context.getSettingsPath().resolve(IdeContext.FOLDER_LEGACY_REPOSITORIES); Path repositoryFile = repository.getValueAsPath(context); if (repositoryFile != null) { From e62ea03522fc39fc152148ed8a84939541011e93 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Sun, 7 Jan 2024 22:28:10 +0100 Subject: [PATCH 52/93] Update cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jörg Hohwiller --- .../com/devonfw/tools/ide/property/RepositoryProperty.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java b/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java index 60be91c18..5d5f32090 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java +++ b/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java @@ -47,8 +47,8 @@ public String parse(String valueAsString) { public Path getValueAsPath(IdeContext context) { - Path repositoriesPath = context.getSettingsPath().resolve(context.FOLDER_REPOSITORIES); - Path legacyRepositoriesPath = context.getSettingsPath().resolve(context.FOLDER_LEGACY_REPOSITORIES); + Path repositoriesPath = context.getSettingsPath().resolve(IdeContext.FOLDER_REPOSITORIES); + Path legacyRepositoriesPath = context.getSettingsPath().resolve(IdeContext.FOLDER_LEGACY_REPOSITORIES); Path repositoryFile; if (super.getValue() != null) { From 8a35bbd1c076ee5b6ec53ea0a1f0bdb430800a6a Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Sun, 7 Jan 2024 22:28:50 +0100 Subject: [PATCH 53/93] Update cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jörg Hohwiller --- .../com/devonfw/tools/ide/property/RepositoryProperty.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java b/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java index 5d5f32090..a2f529305 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java +++ b/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java @@ -50,12 +50,11 @@ public Path getValueAsPath(IdeContext context) { Path repositoriesPath = context.getSettingsPath().resolve(IdeContext.FOLDER_REPOSITORIES); Path legacyRepositoriesPath = context.getSettingsPath().resolve(IdeContext.FOLDER_LEGACY_REPOSITORIES); - Path repositoryFile; - if (super.getValue() != null) { - repositoryFile = Path.of(super.getValue()); - } else { + String value = getValue(); + if (value == null) { return null; } + Path repositoryFile = Path.of(value); if (!Files.exists(repositoryFile)) { repositoryFile = repositoriesPath.resolve(repositoryFile.getFileName().toString() + ".properties"); From e8ba8f711d69f2a57d4d8efb02a43d5fdd5b1e59 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Sun, 7 Jan 2024 22:30:44 +0100 Subject: [PATCH 54/93] Update cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jörg Hohwiller --- .../java/com/devonfw/tools/ide/property/RepositoryProperty.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java b/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java index a2f529305..bf5f9b156 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java +++ b/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java @@ -57,7 +57,7 @@ public Path getValueAsPath(IdeContext context) { Path repositoryFile = Path.of(value); if (!Files.exists(repositoryFile)) { - repositoryFile = repositoriesPath.resolve(repositoryFile.getFileName().toString() + ".properties"); + repositoryFile = repositoriesPath.resolve(value + ".properties"); } if (!Files.exists(repositoryFile)) { Path legacyRepositoryFile = legacyRepositoriesPath.resolve(repositoryFile.getFileName().toString()); From 21f14dfcb3c592e33ab69c1acef3f0299f16d2b1 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Mon, 8 Jan 2024 01:03:13 +0100 Subject: [PATCH 55/93] reviewed changes --- .../ide/commandlet/RepositoryCommandlet.java | 10 ++------- .../ide/commandlet/RepositoryConfig.java | 2 +- .../tools/ide/context/AbstractIdeContext.java | 1 + .../com/devonfw/tools/ide/io/FileAccess.java | 9 ++++++++ .../devonfw/tools/ide/io/FileAccessImpl.java | 16 ++++++++++++++ .../ide/property/RepositoryProperty.java | 21 +++++++------------ 6 files changed, 37 insertions(+), 22 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java index e10e75b8e..92c95cf25 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java @@ -46,8 +46,6 @@ public String getName() { @Override public void run() { - Path repositoriesPath = this.context.getSettingsPath().resolve(IdeContext.FOLDER_REPOSITORIES); - Path legacyRepositoriesPath = this.context.getSettingsPath().resolve(IdeContext.FOLDER_LEGACY_REPOSITORIES); Path repositoryFile = repository.getValueAsPath(context); if (repositoryFile != null) { @@ -55,6 +53,8 @@ public void run() { 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; @@ -99,7 +99,6 @@ private void doImportRepository(Path repositoryFile, boolean forceMode) { } this.context.debug(repositoryConfig.toString()); - this.context.debug("Pull or clone git repository {} ...", repository); String workspace = repositoryConfig.workspace() != null ? repositoryConfig.workspace() : "main"; Path workspacePath = this.context.getIdeHome().resolve("workspaces").resolve(workspace); @@ -127,10 +126,5 @@ private void doImportRepository(Path repositoryFile, boolean forceMode) { this.context.info("Build command not set. Skipping build for repository."); } - if (!Files.exists(repositoryPath.resolve(".project"))) { - for (String ideCommandlet : repositoryConfig.imports()) { - //TODO: import repository to ideCommandlet - } - } } } diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryConfig.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryConfig.java index 952fde680..89dea973e 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryConfig.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryConfig.java @@ -45,7 +45,7 @@ public static RepositoryConfig loadProperties(Path filePath) { 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"))); + Boolean.parseBoolean(properties.getProperty("active").trim())); } private static Set getImports(Properties properties) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java b/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java index 8bda33623..286ce81cd 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java +++ b/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java @@ -601,6 +601,7 @@ public void gitPullOrClone(Path target, String gitRepoUrl) { if (!gitRepoUrl.startsWith("http")) { throw new IllegalArgumentException("Invalid git URL '" + gitRepoUrl + "'!"); } + debug("Pull or clone git repository {} ...", gitRepoUrl); ProcessContext pc = newProcess().directory(target).executable("git"); if (Files.isDirectory(target.resolve(".git"))) { ProcessResult result = pc.addArg("remote").run(true); diff --git a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java index dbf787c8f..617492da8 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java +++ b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java @@ -140,4 +140,13 @@ default void copy(Path source, Path target) { */ List getChildrenInDir(Path dir, Predicate filter); + /** + * Finds the existing file with the specified name in the given list of directories. + * + * @param fileName The name of the file to find. + * @param searchDirs The list of directories to search for the file. + * @return The {@code Path} of the existing file, or {@code null} if the file is not found. + */ + Path findExistingFile(String fileName, List searchDirs); + } diff --git a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java index 930685813..ed6f9d447 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java @@ -477,4 +477,20 @@ public List getChildrenInDir(Path dir, Predicate filter) { return files; } + @Override + public Path findExistingFile(String fileName, List searchDirs) { + + for (Path dir : searchDirs) { + Path filePath = dir.resolve(fileName); + try { + if (Files.exists(filePath)) { + return filePath; + } + } catch (SecurityException e) { + throw new IllegalStateException("SecurityException while checking file existence."); + } + } + return null; + } + } diff --git a/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java b/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java index bf5f9b156..c37a75bee 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java +++ b/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java @@ -4,6 +4,7 @@ import java.nio.file.Files; import java.nio.file.Path; +import java.util.Arrays; import java.util.function.Consumer; public class RepositoryProperty extends Property { @@ -47,28 +48,22 @@ public String parse(String valueAsString) { public Path getValueAsPath(IdeContext context) { - Path repositoriesPath = context.getSettingsPath().resolve(IdeContext.FOLDER_REPOSITORIES); - Path legacyRepositoriesPath = context.getSettingsPath().resolve(IdeContext.FOLDER_LEGACY_REPOSITORIES); - String value = getValue(); if (value == null) { return null; } + Path repositoryFile = Path.of(value); - if (!Files.exists(repositoryFile)) { - repositoryFile = repositoriesPath.resolve(value + ".properties"); + Path repositoriesPath = context.getSettingsPath().resolve(IdeContext.FOLDER_REPOSITORIES); + Path legacyRepositoriesPath = context.getSettingsPath().resolve(IdeContext.FOLDER_LEGACY_REPOSITORIES); + repositoryFile = context.getFileAccess().findExistingFile(value + ".properties", + Arrays.asList(repositoriesPath, legacyRepositoriesPath)); } - if (!Files.exists(repositoryFile)) { - Path legacyRepositoryFile = legacyRepositoriesPath.resolve(repositoryFile.getFileName().toString()); - if (Files.exists(legacyRepositoryFile)) { - repositoryFile = legacyRepositoryFile; - } else { - throw new IllegalStateException("Could not find " + repositoryFile); - } + if (repositoryFile == null) { + throw new IllegalStateException("Could not find " + value); } return repositoryFile; } - } From aa1fcca9c409cf8e9ecf006cda2f7d6419eaa333 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Thu, 11 Jan 2024 23:55:31 +0100 Subject: [PATCH 56/93] init instance --- .../ide/commandlet/CreateCommandlet.java | 48 +++++++++++++------ .../ide/commandlet/UpdateCommandlet.java | 7 +++ 2 files changed, 41 insertions(+), 14 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/CreateCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CreateCommandlet.java index 7eea3b932..7e888c5d7 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/CreateCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CreateCommandlet.java @@ -1,18 +1,20 @@ 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.io.FileAccess; +import com.devonfw.tools.ide.process.ProcessContext; +import com.devonfw.tools.ide.property.StringProperty; +import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; /** * {@link Commandlet} to create a new IDEasy instance */ public class CreateCommandlet extends Commandlet { - private final PathProperty createPathProperty; + private final StringProperty newInstance; /** * The constructor. @@ -23,7 +25,7 @@ public CreateCommandlet(IdeContext context) { super(context); addKeyword(getName()); - createPathProperty = add(new PathProperty("", false, "createPath")); + newInstance = add(new StringProperty("", false, "newInstance")); } @Override @@ -35,20 +37,38 @@ public String getName() { @Override public void run() { - Path createPath = createPathProperty.getValue(); - if (createPath == null || createPath.toString().isBlank()) { - // pwd - createPath = this.context.getCwd(); - } + String newInstanceName = newInstance.getValue(); + Path newInstancePath; - if (!Files.isDirectory(createPath)) { - this.context.getFileAccess().mkdirs(createPath); + if (newInstanceName == null) { + newInstancePath = this.context.getCwd(); + } else { + newInstancePath = this.context.getIdeRoot().resolve(newInstanceName); + this.context.getFileAccess().mkdirs(newInstancePath); } - - if (!this.context.getFileAccess().isEmptyDir(createPath)) { + if (!this.context.getFileAccess().isEmptyDir(newInstancePath)) { this.context.askToContinue("Directory is not empty, continue?"); } - this.context.getCommandletManager().getCommandlet(UpdateCommandlet.class).run(); + initializeInstance(newInstancePath); + ProcessContext pc = this.context.newProcess().executable(this.context.getIdeRoot().resolve("ide")); + pc.addArgs("update"); + pc.directory(newInstancePath); + pc.run(); + //TODO: update creates workspaces/main, get path of IDEasy in IDEROOT + } + + private void initializeInstance(Path newInstancePath) { + + FileAccess fileAccess = this.context.getFileAccess(); + fileAccess.mkdirs(newInstancePath.resolve(IdeContext.FOLDER_SOFTWARE)); + fileAccess.mkdirs(newInstancePath.resolve(IdeContext.FOLDER_UPDATES)); + fileAccess.mkdirs(newInstancePath.resolve(IdeContext.FOLDER_PLUGINS)); + fileAccess.mkdirs(newInstancePath.resolve("scripts")); + try { + Files.createFile(newInstancePath.resolve("setup")); + } catch(IOException e) { + throw new IllegalStateException("Could not initialize " + newInstancePath, e); + } } } diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java index 3d3ea91e8..5d8bcd334 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java @@ -43,10 +43,17 @@ public String getName() { public void run() { updateSettings(); + this.context.getFileAccess().mkdirs(this.context.getWorkspacePath()); + if (this.context.getConfPath() == null) { + createConf(); + } updateSoftware(); updateRepositories(); } + private void createConf() { + + } private void updateSettings() { From d6c3b4c138b3ea9677444891418bff6b54b4639c Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Wed, 17 Jan 2024 01:55:01 +0100 Subject: [PATCH 57/93] created setupConf --- .../ide/commandlet/CreateCommandlet.java | 17 ++++---- .../ide/commandlet/UpdateCommandlet.java | 40 +++++++++++++++++-- 2 files changed, 45 insertions(+), 12 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/CreateCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CreateCommandlet.java index 7e888c5d7..b2efe842c 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/CreateCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CreateCommandlet.java @@ -46,27 +46,28 @@ public void run() { newInstancePath = this.context.getIdeRoot().resolve(newInstanceName); this.context.getFileAccess().mkdirs(newInstancePath); } + + this.context.info("Creating new IDEasy instance in {}", newInstancePath); if (!this.context.getFileAccess().isEmptyDir(newInstancePath)) { this.context.askToContinue("Directory is not empty, continue?"); } initializeInstance(newInstancePath); - ProcessContext pc = this.context.newProcess().executable(this.context.getIdeRoot().resolve("ide")); + ProcessContext pc = this.context.newProcess().executable("IDEasy"); pc.addArgs("update"); pc.directory(newInstancePath); pc.run(); - //TODO: update creates workspaces/main, get path of IDEasy in IDEROOT } private void initializeInstance(Path newInstancePath) { - FileAccess fileAccess = this.context.getFileAccess(); - fileAccess.mkdirs(newInstancePath.resolve(IdeContext.FOLDER_SOFTWARE)); - fileAccess.mkdirs(newInstancePath.resolve(IdeContext.FOLDER_UPDATES)); - fileAccess.mkdirs(newInstancePath.resolve(IdeContext.FOLDER_PLUGINS)); - fileAccess.mkdirs(newInstancePath.resolve("scripts")); try { - Files.createFile(newInstancePath.resolve("setup")); + FileAccess fileAccess = this.context.getFileAccess(); + fileAccess.mkdirs(newInstancePath.resolve(IdeContext.FOLDER_SOFTWARE)); + fileAccess.mkdirs(newInstancePath.resolve(IdeContext.FOLDER_UPDATES)); + fileAccess.mkdirs(newInstancePath.resolve(IdeContext.FOLDER_PLUGINS)); + fileAccess.mkdirs(newInstancePath.resolve("scripts")); // to be removed after isIdeHome is changed + Files.createFile(newInstancePath.resolve("setup")); // to be removed after isIdeHome is changed } catch(IOException e) { throw new IllegalStateException("Could not initialize " + newInstancePath, e); } diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java index 5d8bcd334..8f369e1e5 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java @@ -8,9 +8,11 @@ import com.devonfw.tools.ide.tool.ToolCommandlet; import com.devonfw.tools.ide.variable.IdeVariables; +import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.*; +import java.util.stream.Stream; /** * {@link Commandlet} to update settings, software and repositories @@ -44,14 +46,44 @@ public void run() { updateSettings(); this.context.getFileAccess().mkdirs(this.context.getWorkspacePath()); - if (this.context.getConfPath() == null) { - createConf(); - } + this.context.getFileAccess().mkdirs(this.context.getConfPath()); + setupConf(this.context.getSettingsPath().resolve("devon"), this.context.getConfPath()); updateSoftware(); updateRepositories(); } - private void createConf() { + private void setupConf(Path template, Path conf) { + + + try (Stream childStream = Files.list(template)) { + Iterator iterator = childStream.iterator(); + while (iterator.hasNext()) { + Path child = iterator.next(); + String basename = child.getFileName().toString(); + + if (!basename.equals(".") && !basename.equals("..") && !basename.equals("*")) { + Path confPath = conf.resolve(basename); + + if (Files.isDirectory(child)) { + if (!Files.exists(confPath) || !Files.isDirectory(confPath)) { + this.context.getFileAccess().mkdirs(confPath); + } + setupConf(child, confPath); + } else if (Files.isRegularFile(child)) { + if (Files.isRegularFile(confPath)) { + this.context.debug("Configuration {} already exists - skipping to copy from {}", confPath, child); + } else { + if (!basename.equals("settings.xml")) { + this.context.debug("Copying template {} to {}.", child, confPath); + this.context.getFileAccess().copy(child, confPath); + } + } + } + } + } + } catch (IOException e) { + throw new IllegalStateException("Could not setup the conf folder.", e); + } } From da4949d3ac5368a7e26ab8c1037abb72ae049766 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Wed, 17 Jan 2024 16:59:39 +0100 Subject: [PATCH 58/93] bug fixes --- .../tools/ide/commandlet/CreateCommandlet.java | 11 +++++++++-- .../tools/ide/commandlet/UpdateCommandlet.java | 6 ++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/CreateCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CreateCommandlet.java index b2efe842c..924b4c8c8 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/CreateCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CreateCommandlet.java @@ -3,6 +3,7 @@ import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.io.FileAccess; import com.devonfw.tools.ide.process.ProcessContext; +import com.devonfw.tools.ide.process.ProcessResult; import com.devonfw.tools.ide.property.StringProperty; import java.io.IOException; @@ -56,7 +57,11 @@ public void run() { ProcessContext pc = this.context.newProcess().executable("IDEasy"); pc.addArgs("update"); pc.directory(newInstancePath); - pc.run(); + if (pc.run() == ProcessResult.SUCCESS) { + this.context.success("IDEasy Instance successfully created in {}", newInstancePath); + } else { + this.context.warning("Could not created IDEasy Instance."); + } } private void initializeInstance(Path newInstancePath) { @@ -67,7 +72,9 @@ private void initializeInstance(Path newInstancePath) { fileAccess.mkdirs(newInstancePath.resolve(IdeContext.FOLDER_UPDATES)); fileAccess.mkdirs(newInstancePath.resolve(IdeContext.FOLDER_PLUGINS)); fileAccess.mkdirs(newInstancePath.resolve("scripts")); // to be removed after isIdeHome is changed - Files.createFile(newInstancePath.resolve("setup")); // to be removed after isIdeHome is changed + if (!Files.exists(newInstancePath.resolve("setup"))) { + Files.createFile(newInstancePath.resolve("setup")); // to be removed after isIdeHome is changed + } } catch(IOException e) { throw new IllegalStateException("Could not initialize " + newInstancePath, e); } diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java index 8f369e1e5..436295819 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java @@ -46,15 +46,13 @@ public void run() { updateSettings(); this.context.getFileAccess().mkdirs(this.context.getWorkspacePath()); - this.context.getFileAccess().mkdirs(this.context.getConfPath()); - setupConf(this.context.getSettingsPath().resolve("devon"), this.context.getConfPath()); + setupConf(this.context.getSettingsPath().resolve("devon"), this.context.getIdeHome()); updateSoftware(); updateRepositories(); } private void setupConf(Path template, Path conf) { - try (Stream childStream = Files.list(template)) { Iterator iterator = childStream.iterator(); while (iterator.hasNext()) { @@ -74,7 +72,7 @@ private void setupConf(Path template, Path conf) { this.context.debug("Configuration {} already exists - skipping to copy from {}", confPath, child); } else { if (!basename.equals("settings.xml")) { - this.context.debug("Copying template {} to {}.", child, confPath); + this.context.info("Copying template {} to {}.", child, confPath); this.context.getFileAccess().copy(child, confPath); } } From 3181cde6897efa0b902a02496d7da5e03753978b Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Thu, 18 Jan 2024 19:57:02 +0100 Subject: [PATCH 59/93] comments --- .../com/devonfw/tools/ide/commandlet/CreateCommandlet.java | 2 +- .../com/devonfw/tools/ide/commandlet/UpdateCommandlet.java | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/CreateCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CreateCommandlet.java index 924b4c8c8..7f9a35871 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/CreateCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CreateCommandlet.java @@ -54,7 +54,7 @@ public void run() { } initializeInstance(newInstancePath); - ProcessContext pc = this.context.newProcess().executable("IDEasy"); + ProcessContext pc = this.context.newProcess().executable("ideasy"); //maybe later in a separate method runIdeCommand pc.addArgs("update"); pc.directory(newInstancePath); if (pc.run() == ProcessResult.SUCCESS) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java index 436295819..6d9d2b038 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java @@ -114,6 +114,7 @@ private void updateSettings() { repository = DEFAULT_SETTINGS_REPO_URL; } this.context.gitPullOrClone(settingsPath, repository); + this.context.success("Successfully cloned settings repository."); } } @@ -133,8 +134,10 @@ private void updateSoftware() { // regular tools in $IDE_TOOLS List regularTools = IdeVariables.IDE_TOOLS.get(this.context); - for (String regularTool : regularTools) { - toolCommandlets.add(this.context.getCommandletManager().getToolCommandlet(regularTool)); + if (regularTools != null) { + for (String regularTool : regularTools) { + toolCommandlets.add(this.context.getCommandletManager().getToolCommandlet(regularTool)); + } } // custom tools From 6ea0cc91c7bd72579300fb34c7ef5da1252281c3 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Thu, 18 Jan 2024 20:04:06 +0100 Subject: [PATCH 60/93] updated ide.properties --- cli/src/main/resources/nls/Ide.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/main/resources/nls/Ide.properties b/cli/src/main/resources/nls/Ide.properties index 72c447432..cdba2ccd3 100644 --- a/cli/src/main/resources/nls/Ide.properties +++ b/cli/src/main/resources/nls/Ide.properties @@ -29,7 +29,7 @@ val-tool=The tool commandlet to select. val-version=The tool version. val-set-version-version=The tool version to set. val-repository-repository=The name of the properties file of the pre-configured git repository to setup, omit to setup all active repositories. -val-create-CreatePath=The path where the new IDEasy instance will be created. +val-create-newInstance=The name of the new instance that will be created. version-banner=Current version of IDE is {} opt--batch=enable batch mode (non-interactive) opt--debug=enable debug logging From 7047ebd5df917cec646ac28d5669c064753d9a6a Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Thu, 18 Jan 2024 20:32:06 +0100 Subject: [PATCH 61/93] Update UpdateCommandlet.java --- .../ide/commandlet/UpdateCommandlet.java | 44 ++++++++++++++++++- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java index 3d3ea91e8..6d9d2b038 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java @@ -8,9 +8,11 @@ import com.devonfw.tools.ide.tool.ToolCommandlet; import com.devonfw.tools.ide.variable.IdeVariables; +import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.*; +import java.util.stream.Stream; /** * {@link Commandlet} to update settings, software and repositories @@ -43,10 +45,45 @@ public String getName() { public void run() { updateSettings(); + this.context.getFileAccess().mkdirs(this.context.getWorkspacePath()); + setupConf(this.context.getSettingsPath().resolve("devon"), this.context.getIdeHome()); updateSoftware(); updateRepositories(); } + private void setupConf(Path template, Path conf) { + + try (Stream childStream = Files.list(template)) { + Iterator iterator = childStream.iterator(); + while (iterator.hasNext()) { + Path child = iterator.next(); + String basename = child.getFileName().toString(); + + if (!basename.equals(".") && !basename.equals("..") && !basename.equals("*")) { + Path confPath = conf.resolve(basename); + + if (Files.isDirectory(child)) { + if (!Files.exists(confPath) || !Files.isDirectory(confPath)) { + this.context.getFileAccess().mkdirs(confPath); + } + setupConf(child, confPath); + } else if (Files.isRegularFile(child)) { + if (Files.isRegularFile(confPath)) { + this.context.debug("Configuration {} already exists - skipping to copy from {}", confPath, child); + } else { + if (!basename.equals("settings.xml")) { + this.context.info("Copying template {} to {}.", child, confPath); + this.context.getFileAccess().copy(child, confPath); + } + } + } + } + } + } catch (IOException e) { + throw new IllegalStateException("Could not setup the conf folder.", e); + } + + } private void updateSettings() { @@ -77,6 +114,7 @@ private void updateSettings() { repository = DEFAULT_SETTINGS_REPO_URL; } this.context.gitPullOrClone(settingsPath, repository); + this.context.success("Successfully cloned settings repository."); } } @@ -96,8 +134,10 @@ private void updateSoftware() { // regular tools in $IDE_TOOLS List regularTools = IdeVariables.IDE_TOOLS.get(this.context); - for (String regularTool : regularTools) { - toolCommandlets.add(this.context.getCommandletManager().getToolCommandlet(regularTool)); + if (regularTools != null) { + for (String regularTool : regularTools) { + toolCommandlets.add(this.context.getCommandletManager().getToolCommandlet(regularTool)); + } } // custom tools From e494943262d41b1ded7468a336a907e3c9173449 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Thu, 18 Jan 2024 20:36:43 +0100 Subject: [PATCH 62/93] javadoc --- cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java index 51aca5a22..1135a4df1 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java +++ b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java @@ -161,7 +161,7 @@ default void copy(Path source, Path target) { /** * Retrieves a list of file paths from the specified directory. * @param dir the folder to iterate through - * @return a list of paths that satisfy the provided {@link Predicate}. Will be {@link List#isEmpty() empty} if no match was found but is never {@code null}. + * @return a list of paths. Will be {@link List#isEmpty() empty} if directory is empty but is never {@code null}. */ List getChildrenInDir(Path dir); From cbeee6b411d075fd23e64926190e6540adbe246b Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Fri, 19 Jan 2024 03:34:16 +0100 Subject: [PATCH 63/93] Update CreateCommandlet.java --- .../java/com/devonfw/tools/ide/commandlet/CreateCommandlet.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/CreateCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CreateCommandlet.java index 7f9a35871..f92c7919d 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/CreateCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CreateCommandlet.java @@ -54,7 +54,7 @@ public void run() { } initializeInstance(newInstancePath); - ProcessContext pc = this.context.newProcess().executable("ideasy"); //maybe later in a separate method runIdeCommand + ProcessContext pc = this.context.newProcess().executable("ideasy"); pc.addArgs("update"); pc.directory(newInstancePath); if (pc.run() == ProcessResult.SUCCESS) { From bc7b48ad7463b89cd66b11252e5dcfeabdeaa7af Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Thu, 25 Jan 2024 12:17:09 +0100 Subject: [PATCH 64/93] wrote test for repositoryCom --- .../commandlet/RepositoryCommandletTest.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 cli/src/test/java/com/devonfw/tools/ide/commandlet/RepositoryCommandletTest.java diff --git a/cli/src/test/java/com/devonfw/tools/ide/commandlet/RepositoryCommandletTest.java b/cli/src/test/java/com/devonfw/tools/ide/commandlet/RepositoryCommandletTest.java new file mode 100644 index 000000000..6ff0738b7 --- /dev/null +++ b/cli/src/test/java/com/devonfw/tools/ide/commandlet/RepositoryCommandletTest.java @@ -0,0 +1,32 @@ +package com.devonfw.tools.ide.commandlet; + +import com.devonfw.tools.ide.context.AbstractIdeContextTest; +import com.devonfw.tools.ide.context.IdeContext; +import com.devonfw.tools.ide.context.IdeTestContext; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Test of {@link RepositoryCommandlet} + */ +class RepositoryCommandletTest extends AbstractIdeContextTest { + + @Test + public void runWithSpecifiedRepository() { + + //arrange + String path = "workspaces/foo-test/my-git-repo"; + IdeTestContext context = newContext("basic", path, true); + RepositoryCommandlet repositoryCommandlet = context.getCommandletManager().getCommandlet(RepositoryCommandlet.class); + repositoryCommandlet.repository.setValueAsString("repo", context); + //act + repositoryCommandlet.run(); + //assert + + + + + } + +} \ No newline at end of file From e21f222c1a67342cc667363006296290332d225f Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Fri, 26 Jan 2024 04:20:11 +0100 Subject: [PATCH 65/93] changed repositoryProperty to FileProperty inheritance --- .../ide/commandlet/RepositoryCommandlet.java | 4 +- .../ide/property/RepositoryProperty.java | 39 ++++++++----------- .../commandlet/RepositoryCommandletTest.java | 32 --------------- 3 files changed, 18 insertions(+), 57 deletions(-) delete mode 100644 cli/src/test/java/com/devonfw/tools/ide/commandlet/RepositoryCommandletTest.java diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java index 92c95cf25..66a16c23d 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java @@ -34,7 +34,7 @@ public RepositoryCommandlet(IdeContext context) { super(context); addKeyword(getName()); addKeyword("setup"); - this.repository = add(new RepositoryProperty("", false, "repository")); + this.repository = add(new RepositoryProperty("", false, "repository", true)); } @Override @@ -46,7 +46,7 @@ public String getName() { @Override public void run() { - Path repositoryFile = repository.getValueAsPath(context); + Path repositoryFile = repository.getValue(); if (repositoryFile != null) { // Handle the case when a specific repository is provided diff --git a/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java b/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java index c841c6169..0439a0fb5 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java +++ b/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java @@ -7,7 +7,7 @@ import java.util.Arrays; import java.util.function.Consumer; -public class RepositoryProperty extends Property { +public class RepositoryProperty extends FileProperty { /** * The constructor. @@ -15,10 +15,11 @@ public class RepositoryProperty extends Property { * @param name the {@link #getName() property name}. * @param required the {@link #isRequired() required flag}. * @param alias the {@link #getAlias() property alias}. + * @param mustExist the {@link #isPathRequiredToExist() required to exist flag}. */ - public RepositoryProperty(String name, boolean required, String alias) { + public RepositoryProperty(String name, boolean required, String alias, boolean mustExist) { - this(name, required, alias, null); + super(name, required, alias, mustExist); } /** @@ -27,41 +28,33 @@ public RepositoryProperty(String name, boolean required, String alias) { * @param name the {@link #getName() property name}. * @param required the {@link #isRequired() required flag}. * @param alias the {@link #getAlias() property alias}. + * @param mustExist the {@link #isPathRequiredToExist() required to exist flag}. * @param validator the {@link Consumer} used to {@link #validate() validate} the {@link #getValue() value}. */ - public RepositoryProperty(String name, boolean required, String alias, Consumer validator) { + public RepositoryProperty(String name, boolean required, String alias, boolean mustExist, Consumer validator) { - super(name, required, alias, validator); + super(name, required, alias, mustExist, validator); } - @Override - public Class getValueType() { + public Path parse(String valueAsString, IdeContext context) { - return String.class; - } - - @Override - public String parse(String valueAsString, IdeContext context) { - - return null; - } - - public Path getValueAsPath(IdeContext context) { - - String value = getValue(); - if (value == null) { + if (valueAsString == null) { return null; } - Path repositoryFile = Path.of(value); + Path repositoryFile = Path.of(valueAsString); if (!Files.exists(repositoryFile)) { Path repositoriesPath = context.getSettingsPath().resolve(IdeContext.FOLDER_REPOSITORIES); Path legacyRepositoriesPath = context.getSettingsPath().resolve(IdeContext.FOLDER_LEGACY_REPOSITORIES); - repositoryFile = context.getFileAccess().findExistingFile(value + ".properties", + String propertiesFileName = valueAsString; + if (!valueAsString.endsWith(".properties")) { + propertiesFileName += ".properties"; + } + repositoryFile = context.getFileAccess().findExistingFile(propertiesFileName, Arrays.asList(repositoriesPath, legacyRepositoriesPath)); } if (repositoryFile == null) { - throw new IllegalStateException("Could not find " + value); + throw new IllegalStateException("Could not find properties file: " + valueAsString); } return repositoryFile; } diff --git a/cli/src/test/java/com/devonfw/tools/ide/commandlet/RepositoryCommandletTest.java b/cli/src/test/java/com/devonfw/tools/ide/commandlet/RepositoryCommandletTest.java deleted file mode 100644 index 6ff0738b7..000000000 --- a/cli/src/test/java/com/devonfw/tools/ide/commandlet/RepositoryCommandletTest.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.devonfw.tools.ide.commandlet; - -import com.devonfw.tools.ide.context.AbstractIdeContextTest; -import com.devonfw.tools.ide.context.IdeContext; -import com.devonfw.tools.ide.context.IdeTestContext; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * Test of {@link RepositoryCommandlet} - */ -class RepositoryCommandletTest extends AbstractIdeContextTest { - - @Test - public void runWithSpecifiedRepository() { - - //arrange - String path = "workspaces/foo-test/my-git-repo"; - IdeTestContext context = newContext("basic", path, true); - RepositoryCommandlet repositoryCommandlet = context.getCommandletManager().getCommandlet(RepositoryCommandlet.class); - repositoryCommandlet.repository.setValueAsString("repo", context); - //act - repositoryCommandlet.run(); - //assert - - - - - } - -} \ No newline at end of file From bf64dfb6733dc6e6f544dbe170e6e7bf435f6a96 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Thu, 1 Feb 2024 00:33:44 +0100 Subject: [PATCH 66/93] Update CreateCommandlet.java --- .../java/com/devonfw/tools/ide/commandlet/CreateCommandlet.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/CreateCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CreateCommandlet.java index f92c7919d..c4c9cda89 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/CreateCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CreateCommandlet.java @@ -60,7 +60,7 @@ public void run() { if (pc.run() == ProcessResult.SUCCESS) { this.context.success("IDEasy Instance successfully created in {}", newInstancePath); } else { - this.context.warning("Could not created IDEasy Instance."); + this.context.warning("Could not create IDEasy Instance."); } } From df05c7c2b933ea94ebb2c1d783b3e7013d43c221 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Fri, 2 Feb 2024 11:18:00 +0100 Subject: [PATCH 67/93] Update Ide_de.properties --- cli/src/main/resources/nls/Ide_de.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/main/resources/nls/Ide_de.properties b/cli/src/main/resources/nls/Ide_de.properties index eabfb4b02..37f980e9b 100644 --- a/cli/src/main/resources/nls/Ide_de.properties +++ b/cli/src/main/resources/nls/Ide_de.properties @@ -8,7 +8,7 @@ cmd---version=Gibt die Version von IDEasy aus. cmd-eclipse=Werkzeug Kommando für Eclipse (IDE) cmd-env=Gibt die zu setztenden und exportierenden Umgebungsvariablen aus. cmd-get-version=Zeigt die Version des selektierten Werkzeugs an. -cmd-gh=Werkzeug Kommando für die Github Kommandoschnittstelle +cmd-gh=Werkzeug Kommando für die Github Kommandoschnittstelle. cmd-repository=Richtet das vorkonfigurierte Git Repository ein. cmd-helm=Werkzeug Kommando für Helm (Kubernetes Package Manager) cmd-help=Zeigt diese Hilfe an. From f1ae20df5655d76f1d35baa0791fbc891d7008f1 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Fri, 2 Feb 2024 11:21:04 +0100 Subject: [PATCH 68/93] Update CommandletManagerImpl.java --- .../com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java index 5e27a815a..cf417a8b3 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java @@ -16,7 +16,6 @@ import com.devonfw.tools.ide.tool.gh.Gh; import com.devonfw.tools.ide.tool.gradle.Gradle; import com.devonfw.tools.ide.tool.helm.Helm; -import com.devonfw.tools.ide.tool.intellij.Intellij; import com.devonfw.tools.ide.tool.java.Java; import com.devonfw.tools.ide.tool.kotlinc.Kotlinc; import com.devonfw.tools.ide.tool.kotlinc.KotlincNative; @@ -81,7 +80,6 @@ public CommandletManagerImpl(IdeContext context) { add(new Azure(context)); add(new Aws(context)); add(new Cobigen(context)); - add(new Intellij(context)); } private void add(Commandlet commandlet) { From ea4b41029517762791635405fdbda15080ccbe8e Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Fri, 16 Feb 2024 00:17:38 +0100 Subject: [PATCH 69/93] typo --- .../java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java index 6d9d2b038..39235d6cc 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java @@ -128,7 +128,7 @@ private void updateSoftware() { String toolName = software.getFileName().toString(); ToolCommandlet toolCommandlet = this.context.getCommandletManager().getToolCommandletOrNull(toolName); if (toolCommandlet != null) { - toolCommandlets.add(this.context.getCommandletManager().getToolCommandletOrNull(toolName)); + toolCommandlets.add(toolCommandlet); } } From 7b119df7dfff3d730576e674ac64230664f42fc4 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Wed, 21 Feb 2024 15:04:22 +0100 Subject: [PATCH 70/93] Update cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jörg Hohwiller --- .../main/java/com/devonfw/tools/ide/io/FileAccessImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java index 822c4087c..1fc4b1229 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java @@ -652,8 +652,8 @@ public Path findExistingFile(String fileName, List searchDirs) { if (Files.exists(filePath)) { return filePath; } - } catch (SecurityException e) { - throw new IllegalStateException("SecurityException while checking file existence."); + } catch (Exception e) { + throw new IllegalStateException("Unexpected error while checking existence of file "+filePath+" .", e); } } return null; From 03f2f732abc7ddba29b837a885e3b2a4642a1167 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Thu, 29 Feb 2024 04:00:29 +0100 Subject: [PATCH 71/93] resolved conflicts --- .../com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java index 66a16c23d..bf16a0bbf 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java @@ -109,7 +109,7 @@ private void doImportRepository(Path repositoryFile, boolean forceMode) { } Path repositoryPath = workspacePath.resolve(repository); - this.context.gitPullOrClone(repositoryPath, gitUrl); + this.context.getGitContext().pullOrClone(gitUrl, repositoryPath); String buildCmd = repositoryConfig.buildCmd(); this.context.debug("Building repository with ide command: {}", buildCmd); From 572ffb1b4fd81b7e11e39c39f07d6b11d52f182f Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Thu, 29 Feb 2024 04:18:38 +0100 Subject: [PATCH 72/93] resolved conflicts --- .../com/devonfw/tools/ide/commandlet/UpdateCommandlet.java | 4 ++-- .../com/devonfw/tools/ide/context/AbstractIdeContext.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java index 6d9d2b038..c24b348dd 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java @@ -91,7 +91,7 @@ private void updateSettings() { Path settingsPath = this.context.getSettingsPath(); if (Files.isDirectory(settingsPath)) { // perform git pull on the settings repo - this.context.gitPullOrClone(settingsPath, DEFAULT_SETTINGS_REPO_URL); + this.context.getGitContext().pull(settingsPath); this.context.success("Successfully updated settings repository."); } else { // check if a settings repository is given then clone, otherwise prompt user for a repository. @@ -113,7 +113,7 @@ private void updateSettings() { if (repository.isBlank()) { repository = DEFAULT_SETTINGS_REPO_URL; } - this.context.gitPullOrClone(settingsPath, repository); + this.context.getGitContext().pullOrClone(repository, settingsPath); this.context.success("Successfully cloned settings repository."); } } diff --git a/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java b/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java index 88c4d6a05..6ffddd21d 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java +++ b/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java @@ -651,7 +651,7 @@ public O question(String question, O... options) { /** * @return the input from the end-user (e.g. read from the console). */ - protected abstract String readLine(); + public abstract String readLine(); private static void addMapping(Map mapping, String key, O option) { From 7a3c3c23e3734cef6a8a927313596964d5669608 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Thu, 29 Feb 2024 05:54:12 +0100 Subject: [PATCH 73/93] added tests for repo commandlet --- .../ide/commandlet/RepositoryCommandlet.java | 2 +- .../commandlet/RepositoryCommandletTest.java | 112 ++++++++++++++++++ 2 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 cli/src/test/java/com/devonfw/tools/ide/commandlet/RepositoryCommandletTest.java diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java index bf16a0bbf..4d34de39a 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java @@ -94,7 +94,7 @@ private void doImportRepository(Path repositoryFile, boolean forceMode) { 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); + , repositoryFile.getFileName().toString()); return; } diff --git a/cli/src/test/java/com/devonfw/tools/ide/commandlet/RepositoryCommandletTest.java b/cli/src/test/java/com/devonfw/tools/ide/commandlet/RepositoryCommandletTest.java new file mode 100644 index 000000000..66146c7e8 --- /dev/null +++ b/cli/src/test/java/com/devonfw/tools/ide/commandlet/RepositoryCommandletTest.java @@ -0,0 +1,112 @@ +package com.devonfw.tools.ide.commandlet; + +import com.devonfw.tools.ide.context.AbstractIdeContextTest; +import com.devonfw.tools.ide.context.IdeContext; +import com.devonfw.tools.ide.context.IdeTestContext; +import com.devonfw.tools.ide.log.IdeLogLevel; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Properties; + +public class RepositoryCommandletTest extends AbstractIdeContextTest { + + IdeTestContext context = newContext("basic", null, true); + + /** + * Properties object used to write key-value pairs to the properties file "test.properties" + */ + Properties properties = new Properties(); + + private void createPropertiesFile() { + + try { + properties.setProperty("path", "test"); + properties.setProperty("workingsets", "test"); + properties.setProperty("workspace", "test"); + properties.setProperty("git_url", "test"); + properties.setProperty("git_branch", "test"); + properties.setProperty("build_path", "test"); + properties.setProperty("build_cmd", ""); + properties.setProperty("active", "test"); + + Path repositoriesPath = this.context.getSettingsPath().resolve(IdeContext.FOLDER_REPOSITORIES); + this.context.getFileAccess().mkdirs(repositoriesPath); + Path propertiesPath = repositoriesPath.resolve("test.properties"); + Files.createFile(propertiesPath); + saveProperties(properties); + } catch (IOException e) { + throw new IllegalStateException("Failed to create properties file during tests.", e); + } + + } + + private void saveProperties(Properties properties) { + + Path propertiesPath = this.context.getSettingsPath().resolve(IdeContext.FOLDER_REPOSITORIES) + .resolve("test.properties"); + try (var output = Files.newOutputStream(propertiesPath)) { + properties.store(output, null); + } catch (IOException e) { + throw new IllegalStateException("Failed to save properties file during tests.", e); + } + } + + @Test + public void testRunWithSpecificRepository() { + + // arrange + RepositoryCommandlet rc = context.getCommandletManager().getCommandlet(RepositoryCommandlet.class); + createPropertiesFile(); + rc.repository.setValueAsString("test", context); + // act + rc.run(); + // assert + assertLogMessage(context, IdeLogLevel.INFO, "Importing repository from test.properties ..."); + } + + @Test + public void testRunWithNoSpecificRepositoryAndInactive() { + + // arrange + RepositoryCommandlet rc = context.getCommandletManager().getCommandlet(RepositoryCommandlet.class); + createPropertiesFile(); + // act + rc.run(); + // assert + assertLogMessage(context, IdeLogLevel.INFO, "Importing repository from test.properties ..."); + assertLogMessage(context, IdeLogLevel.INFO, "Skipping repository - use force (-f) to setup all repositories ..."); + } + + @Test + public void testRunInvalidConfiguration() { + + // arrange + RepositoryCommandlet rc = context.getCommandletManager().getCommandlet(RepositoryCommandlet.class); + createPropertiesFile(); + properties.setProperty("path", ""); + properties.setProperty("git_url", ""); + saveProperties(properties); + rc.repository.setValueAsString("test", context); + // act + rc.run(); + // assert + assertLogMessage(context, IdeLogLevel.WARNING, + "Invalid repository configuration test.properties - both 'path' and 'git-url' have to be defined."); + } + + @Test + public void testRunNoRepositoriesOrProjectsFolderFound() { + + // arrange + RepositoryCommandlet rc = context.getCommandletManager().getCommandlet(RepositoryCommandlet.class); + Path repositoriesPath = this.context.getSettingsPath().resolve(IdeContext.FOLDER_REPOSITORIES); + this.context.getFileAccess().delete(repositoriesPath); + // act + rc.run(); + // assert + assertLogMessage(context, IdeLogLevel.WARNING, "Cannot find repositories folder nor projects folder."); + } +} \ No newline at end of file From e5b794acc1f6a989cbc87d88edec344d1c4201f1 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Thu, 29 Feb 2024 06:26:17 +0100 Subject: [PATCH 74/93] removed my stupid fix --- cli/src/main/java/com/devonfw/tools/ide/common/SystemPath.java | 1 - 1 file changed, 1 deletion(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/common/SystemPath.java b/cli/src/main/java/com/devonfw/tools/ide/common/SystemPath.java index cbd1f89ac..a1bb433b3 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/common/SystemPath.java +++ b/cli/src/main/java/com/devonfw/tools/ide/common/SystemPath.java @@ -179,7 +179,6 @@ public Path getPath(String tool) { */ public void setPath(String tool, Path path) { - this.paths.add(path); this.tool2pathMap.put(tool, path); } From 9254184b0362c03ff388c2eddcb92b4ed5d47baf Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Mon, 4 Mar 2024 16:38:14 +0100 Subject: [PATCH 75/93] removed * import --- .../java/com/devonfw/tools/ide/merge/DirectoryMerger.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/merge/DirectoryMerger.java b/cli/src/main/java/com/devonfw/tools/ide/merge/DirectoryMerger.java index fe3b8fd3a..612117c43 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/merge/DirectoryMerger.java +++ b/cli/src/main/java/com/devonfw/tools/ide/merge/DirectoryMerger.java @@ -3,7 +3,11 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.*; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; import org.jline.utils.Log; From 2b99a21077cff9261caee1fd8df172c9bb7c416e Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Wed, 13 Mar 2024 04:06:26 +0100 Subject: [PATCH 76/93] Update cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jörg Hohwiller --- .../com/devonfw/tools/ide/property/RepositoryProperty.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java b/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java index 0439a0fb5..df0c92b84 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java +++ b/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java @@ -15,11 +15,10 @@ public class RepositoryProperty extends FileProperty { * @param name the {@link #getName() property name}. * @param required the {@link #isRequired() required flag}. * @param alias the {@link #getAlias() property alias}. - * @param mustExist the {@link #isPathRequiredToExist() required to exist flag}. */ - public RepositoryProperty(String name, boolean required, String alias, boolean mustExist) { + public RepositoryProperty(String name, boolean required, String alias) { - super(name, required, alias, mustExist); + super(name, required, alias, true); } /** From 6129a5a6275134b06b7646979c2616df8d279d4c Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Wed, 13 Mar 2024 04:07:13 +0100 Subject: [PATCH 77/93] Update cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jörg Hohwiller --- .../com/devonfw/tools/ide/property/RepositoryProperty.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java b/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java index df0c92b84..8059c201e 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java +++ b/cli/src/main/java/com/devonfw/tools/ide/property/RepositoryProperty.java @@ -27,12 +27,11 @@ public RepositoryProperty(String name, boolean required, String alias) { * @param name the {@link #getName() property name}. * @param required the {@link #isRequired() required flag}. * @param alias the {@link #getAlias() property alias}. - * @param mustExist the {@link #isPathRequiredToExist() required to exist flag}. * @param validator the {@link Consumer} used to {@link #validate() validate} the {@link #getValue() value}. */ - public RepositoryProperty(String name, boolean required, String alias, boolean mustExist, Consumer validator) { + public RepositoryProperty(String name, boolean required, String alias, Consumer validator) { - super(name, required, alias, mustExist, validator); + super(name, required, alias, true, validator); } public Path parse(String valueAsString, IdeContext context) { From 4b5b005365b357fd5bcb2db22dce2f8b0cff5eff Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Wed, 13 Mar 2024 04:09:56 +0100 Subject: [PATCH 78/93] Update cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jörg Hohwiller --- .../com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java index 4d34de39a..762cbb79b 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java @@ -34,7 +34,7 @@ public RepositoryCommandlet(IdeContext context) { super(context); addKeyword(getName()); addKeyword("setup"); - this.repository = add(new RepositoryProperty("", false, "repository", true)); + this.repository = add(new RepositoryProperty("", false, "repository")); } @Override From d079c78ac84f9f7d9c5e793f0839ece754800132 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Wed, 13 Mar 2024 18:28:38 +0100 Subject: [PATCH 79/93] reviewed changes --- .../ide/commandlet/RepositoryCommandlet.java | 9 ++-- .../tools/ide/context/AbstractIdeContext.java | 2 +- .../devonfw/tools/ide/context/GitContext.java | 43 ++++++++++--------- .../tools/ide/context/GitContextImpl.java | 37 ++++++++-------- .../com/devonfw/tools/ide/io/FileAccess.java | 9 ++++ .../devonfw/tools/ide/io/FileAccessImpl.java | 16 +++++++ .../tools/ide/context/GitContextMock.java | 6 +-- .../tools/ide/context/GitContextTest.java | 10 ++--- 8 files changed, 76 insertions(+), 56 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java index 4d34de39a..17afc1010 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java @@ -65,7 +65,7 @@ public void run() { return; } - List propertiesFiles = this.context.getFileAccess().getChildrenInDir(repositories, + List propertiesFiles = this.context.getFileAccess().listChildren(repositories, path -> path.getFileName().toString().endsWith(".properties")); boolean forceMode = this.context.isForceMode(); @@ -104,12 +104,9 @@ private void doImportRepository(Path repositoryFile, boolean forceMode) { Path workspacePath = this.context.getIdeHome().resolve("workspaces").resolve(workspace); this.context.getFileAccess().mkdirs(workspacePath); - if (repositoryConfig.gitBranch() != null && !repositoryConfig.gitBranch().isEmpty()) { - gitUrl = gitUrl + "#" + repositoryConfig.gitBranch(); - } - Path repositoryPath = workspacePath.resolve(repository); - this.context.getGitContext().pullOrClone(gitUrl, repositoryPath); + String branch = repositoryConfig.gitBranch() != null ? repositoryConfig.gitBranch() : ""; + this.context.getGitContext().pullOrClone(gitUrl, branch, repositoryPath); String buildCmd = repositoryConfig.buildCmd(); this.context.debug("Building repository with ide command: {}", buildCmd); diff --git a/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java b/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java index a1c8466d3..d514eb48e 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java +++ b/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java @@ -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, "", this.urlsPath, "origin"); } this.urlMetadata = new UrlMetadata(this); } diff --git a/cli/src/main/java/com/devonfw/tools/ide/context/GitContext.java b/cli/src/main/java/com/devonfw/tools/ide/context/GitContext.java index acb7e2b6d..c79c7a7ea 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/context/GitContext.java +++ b/cli/src/main/java/com/devonfw/tools/ide/context/GitContext.java @@ -14,44 +14,45 @@ public interface GitContext extends IdeLogger { * 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. + * to check-out. * @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. + * to check-out. + * @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. + * name to check-out. + * @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. */ - void pullOrClone(String gitRepoUrl, Path targetRepository); + 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); @@ -59,8 +60,8 @@ public interface GitContext extends IdeLogger { * 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); @@ -68,8 +69,8 @@ public interface GitContext extends IdeLogger { * 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. */ @@ -79,8 +80,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); diff --git a/cli/src/main/java/com/devonfw/tools/ide/context/GitContextImpl.java b/cli/src/main/java/com/devonfw/tools/ide/context/GitContextImpl.java index c21898906..6d01b0e72 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/context/GitContextImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/context/GitContextImpl.java @@ -24,7 +24,9 @@ * Implements the {@link GitContext}. */ public class GitContextImpl implements GitContext { - private static final Duration GIT_PULL_CACHE_DELAY_MILLIS = Duration.ofMillis(30 * 60 * 1000);; + private static final Duration GIT_PULL_CACHE_DELAY_MILLIS = Duration.ofMillis(30 * 60 * 1000); + + ; private final IdeContext context; @@ -40,7 +42,7 @@ public GitContextImpl(IdeContext context) { } @Override - public void pullOrCloneIfNeeded(String repoUrl, Path targetRepository) { + public void pullOrCloneIfNeeded(String repoUrl, String branch, Path targetRepository) { Path gitDirectory = targetRepository.resolve(".git"); @@ -58,7 +60,7 @@ public void pullOrCloneIfNeeded(String repoUrl, Path targetRepository) { // Check if the file modification time is older than the delta threshold if ((currentTime - fileMTime > GIT_PULL_CACHE_DELAY_MILLIS.toMillis()) || context.isForceMode()) { - pullOrClone(repoUrl, targetRepository); + pullOrClone(repoUrl, "", targetRepository); try { Files.setLastModifiedTime(magicFilePath, FileTime.fromMillis(currentTime)); } catch (IOException e) { @@ -67,13 +69,13 @@ public void pullOrCloneIfNeeded(String repoUrl, Path targetRepository) { } } else { // If the .git directory does not exist, perform git clone - pullOrClone(repoUrl, targetRepository); + pullOrClone(repoUrl, branch, targetRepository); } } - public void pullOrFetchAndResetIfNeeded(String repoUrl, Path targetRepository, String remoteName, String branchName) { + public void pullOrFetchAndResetIfNeeded(String repoUrl, String branch, Path targetRepository, String remoteName) { - pullOrCloneIfNeeded(repoUrl, targetRepository); + pullOrCloneIfNeeded(repoUrl, branch, targetRepository); if (remoteName.isEmpty()) { reset(targetRepository, "origin", "master"); @@ -85,7 +87,7 @@ public void pullOrFetchAndResetIfNeeded(String repoUrl, Path targetRepository, S } @Override - public void pullOrClone(String gitRepoUrl, Path targetRepository) { + public void pullOrClone(String gitRepoUrl, String branch, Path targetRepository) { Objects.requireNonNull(targetRepository); Objects.requireNonNull(gitRepoUrl); @@ -112,12 +114,6 @@ public void pullOrClone(String gitRepoUrl, Path targetRepository) { } } } else { - String branch = ""; - int hashIndex = gitRepoUrl.indexOf("#"); - if (hashIndex != -1) { - branch = gitRepoUrl.substring(hashIndex + 1); - gitRepoUrl = gitRepoUrl.substring(0, hashIndex); - } clone(new GitUrl(gitRepoUrl, branch), targetRepository); if (!branch.isEmpty()) { this.processContext.addArgs("checkout", branch); @@ -130,8 +126,8 @@ public void pullOrClone(String gitRepoUrl, Path targetRepository) { * Handles errors which occurred during 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. * @param result the {@link ProcessResult} to evaluate. */ private void handleErrors(Path targetRepository, ProcessResult result) { @@ -144,8 +140,8 @@ private void handleErrors(Path targetRepository, ProcessResult result) { } else { this.context.error(message); if (this.context.isOnline()) { - this.context - .error("See above error for details. If you have local changes, please stash or revert and retry."); + this.context.error( + "See above error for details. If you have local changes, please stash or revert and retry."); } else { this.context.error( "It seems you are offline - please ensure Internet connectivity and retry or activate offline mode (-o or --offline)."); @@ -159,8 +155,8 @@ private void handleErrors(Path targetRepository, ProcessResult result) { * Lazily initializes the {@link ProcessContext}. * * @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. */ private void initializeProcessContext(Path targetRepository) { @@ -183,7 +179,8 @@ public void clone(GitUrl gitRepoUrl, Path targetRepository) { if (this.context.isQuietMode()) { this.processContext.addArg("-q"); } - this.processContext.addArgs("--recursive", parsedUrl, "--config", "core.autocrlf=false", "."); + this.processContext.addArgs("--recursive", "-b", gitRepoUrl.branch(), gitRepoUrl.url(), "--config", + "core.autocrlf=false", "."); result = this.processContext.run(ProcessMode.DEFAULT_CAPTURE); if (!result.isSuccessful()) { this.context.warning("Git failed to clone {} into {}.", parsedUrl, targetRepository); diff --git a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java index 2cbb80b9b..e1a2d2a14 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java +++ b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java @@ -223,4 +223,13 @@ default void extract(Path archiveFile, Path targetDir, Consumer postExtrac */ List listChildren(Path dir, Predicate filter); + /** + * Finds the existing file with the specified name in the given list of directories. + * + * @param fileName The name of the file to find. + * @param searchDirs The list of directories to search for the file. + * @return The {@code Path} of the existing file, or {@code null} if the file is not found. + */ + Path findExistingFile(String fileName, List searchDirs); + } diff --git a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java index 9517ceaa6..c6d0210b5 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java @@ -786,4 +786,20 @@ public List listChildren(Path dir, Predicate filter) { } return children; } + + @Override + public Path findExistingFile(String fileName, List searchDirs) { + + for (Path dir : searchDirs) { + Path filePath = dir.resolve(fileName); + try { + if (Files.exists(filePath)) { + return filePath; + } + } catch (Exception e) { + throw new IllegalStateException("Unexpected error while checking existence of file "+filePath+" .", e); + } + } + return null; + } } diff --git a/cli/src/test/java/com/devonfw/tools/ide/context/GitContextMock.java b/cli/src/test/java/com/devonfw/tools/ide/context/GitContextMock.java index f90fc6f49..15681af20 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/context/GitContextMock.java +++ b/cli/src/test/java/com/devonfw/tools/ide/context/GitContextMock.java @@ -7,17 +7,17 @@ public class GitContextMock implements GitContext { @Override - public void pullOrCloneIfNeeded(String repoUrl, Path targetRepository) { + public void pullOrCloneIfNeeded(String repoUrl, String branch, Path targetRepository) { } @Override - public void pullOrFetchAndResetIfNeeded(String repoUrl, Path targetRepository, String remoteName, String branchName) { + public void pullOrFetchAndResetIfNeeded(String repoUrl, String branch, Path targetRepository, String remoteName) { } @Override - public void pullOrClone(String gitRepoUrl, Path targetRepository) { + public void pullOrClone(String gitRepoUrl, String branch, Path targetRepository) { } diff --git a/cli/src/test/java/com/devonfw/tools/ide/context/GitContextTest.java b/cli/src/test/java/com/devonfw/tools/ide/context/GitContextTest.java index 4d773fd3d..090f0445b 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/context/GitContextTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/context/GitContextTest.java @@ -33,7 +33,7 @@ public void testRunGitCloneInOfflineModeThrowsException(@TempDir Path tempDir) { GitContext gitContext = new GitContextImpl(context); // act CliException e1 = assertThrows(CliException.class, () -> { - gitContext.pullOrClone(gitRepoUrl, tempDir); + gitContext.pullOrClone(gitRepoUrl, "", tempDir); }); // assert assertThat(e1).hasMessageContaining(gitRepoUrl).hasMessageContaining(tempDir.toString()) @@ -55,7 +55,7 @@ public void testRunGitClone(@TempDir Path tempDir) { IdeContext context = newGitContext(tempDir, errors, outs, 0, true); GitContext gitContext = new GitContextImpl(context); // act - gitContext.pullOrClone(gitRepoUrl, tempDir); + gitContext.pullOrClone(gitRepoUrl, "", tempDir); // assert assertThat(tempDir.resolve(".git").resolve("url")).hasContent(gitRepoUrl); } @@ -78,7 +78,7 @@ public void testRunGitPullWithoutForce(@TempDir Path tempDir) { Path gitFolderPath = tempDir.resolve(".git"); fileAccess.mkdirs(gitFolderPath); // act - gitContext.pullOrClone(gitRepoUrl, tempDir); + gitContext.pullOrClone(gitRepoUrl, "", tempDir); // assert assertThat(tempDir.resolve(".git").resolve("update")).hasContent(currentDate.toString()); } @@ -115,7 +115,7 @@ public void testRunGitPullWithForceStartsReset(@TempDir Path tempDir) { IdeContext context = newGitContext(tempDir, errors, outs, 0, true); GitContext gitContext = new GitContextImpl(context); // act - gitContext.pullOrFetchAndResetIfNeeded(gitRepoUrl, tempDir, "origin", "master"); + gitContext.pullOrFetchAndResetIfNeeded(gitRepoUrl, "master", tempDir, "origin"); // assert assertThat(modifiedFile).hasContent("original"); } @@ -143,7 +143,7 @@ public void testRunGitPullWithForceStartsCleanup(@TempDir Path tempDir) { throw new RuntimeException(e); } // act - gitContext.pullOrFetchAndResetIfNeeded(gitRepoUrl, tempDir, "origin", "master"); + gitContext.pullOrFetchAndResetIfNeeded(gitRepoUrl, "master", tempDir, "origin"); // assert assertThat(tempDir.resolve("new-folder")).doesNotExist(); } From 7a9bb0b5d6881fdc717f5bbc96157af0e7cf2d5d Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Wed, 13 Mar 2024 18:36:10 +0100 Subject: [PATCH 80/93] minor change --- .../devonfw/tools/ide/commandlet/RepositoryCommandlet.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java index 917f9cb62..d85cdcc9a 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java @@ -105,7 +105,10 @@ private void doImportRepository(Path repositoryFile, boolean forceMode) { this.context.getFileAccess().mkdirs(workspacePath); Path repositoryPath = workspacePath.resolve(repository); - String branch = repositoryConfig.gitBranch() != null ? repositoryConfig.gitBranch() : ""; + String branch = ""; + if (repositoryConfig.gitBranch() != null) { + branch = repositoryConfig.gitBranch(); + } this.context.getGitContext().pullOrClone(gitUrl, branch, repositoryPath); String buildCmd = repositoryConfig.buildCmd(); From 4236eaec8d5a6da996617dcd4f3f30427fcf0ff1 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Wed, 13 Mar 2024 18:59:18 +0100 Subject: [PATCH 81/93] crlf diff --- cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java index e1a2d2a14..e49770f0c 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java +++ b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java @@ -226,9 +226,9 @@ default void extract(Path archiveFile, Path targetDir, Consumer postExtrac /** * Finds the existing file with the specified name in the given list of directories. * - * @param fileName The name of the file to find. - * @param searchDirs The list of directories to search for the file. - * @return The {@code Path} of the existing file, or {@code null} if the file is not found. + * @param fileName The name of the file to find. + * @param searchDirs The list of directories to search for the file. + * @return The {@code Path} of the existing file, or {@code null} if the file is not found. */ Path findExistingFile(String fileName, List searchDirs); From 862eabc16f421a5206ab662dc4271b8732dac65b Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Wed, 13 Mar 2024 19:30:54 +0100 Subject: [PATCH 82/93] diff war --- .../com/devonfw/tools/ide/io/FileAccess.java | 470 +++++++++--------- 1 file changed, 235 insertions(+), 235 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java index e49770f0c..662e302e1 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java +++ b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java @@ -1,235 +1,235 @@ -package com.devonfw.tools.ide.io; - -import java.nio.file.Path; -import java.util.List; -import java.util.function.Consumer; -import java.util.function.Predicate; - -/** - * Interface that gives access to various operations on files. - */ -public interface FileAccess { - - /** - * Downloads a file from an arbitrary location. - * - * @param url the location of the binary file to download. May also be a local or remote path to copy from. - * @param targetFile the {@link Path} to the target file to download to. Should not already exists. Missing parent - * directories will be created automatically. - */ - void download(String url, Path targetFile); - - /** - * Creates the entire {@link Path} as directories if not already existing. - * - * @param directory the {@link Path} to - * {@link java.nio.file.Files#createDirectories(Path, java.nio.file.attribute.FileAttribute...) create}. - */ - void mkdirs(Path directory); - - /** - * @param file the {@link Path} to check. - * @return {@code true} if the given {@code file} points to an existing file, {@code false} otherwise (the given - * {@link Path} does not exist or is a directory). - */ - boolean isFile(Path file); - - /** - * @param folder the {@link Path} to check. - * @return {@code true} if the given {@code folder} points to an existing directory, {@code false} otherwise (a - * warning is logged in this case). - */ - boolean isExpectedFolder(Path folder); - - /** - * @param file the {@link Path} to compute the checksum of. - * @return the computed checksum (SHA-266). - */ - String checksum(Path file); - - /** - * Moves the given {@link Path} to the backup. - * - * @param fileOrFolder the {@link Path} to move to the backup (soft-deletion). - */ - void backup(Path fileOrFolder); - - /** - * @param source the source {@link Path file or folder} to move. - * @param targetDir the {@link Path} with the directory to move {@code source} into. - */ - void move(Path source, Path targetDir); - - /** - * Creates a symbolic link. If the given {@code targetLink} already exists and is a symbolic link or a Windows - * junction, it will be replaced. In case of missing privileges, Windows Junctions may be used as fallback, which must - * point to absolute paths. Therefore, the created link will be absolute instead of relative. - * - * @param source the source {@link Path} to link to, may be relative or absolute. - * @param targetLink the {@link Path} where the symbolic link shall be created pointing to {@code source}. - * @param relative - {@code true} if the symbolic link shall be relative, {@code false} if it shall be absolute. - */ - void symlink(Path source, Path targetLink, boolean relative); - - /** - * Creates a relative symbolic link. If the given {@code targetLink} already exists and is a symbolic link or a - * Windows junction, it will be replaced. In case of missing privileges, Windows Junctions may be used as fallback, - * which must point to absolute paths. Therefore, the created link will be absolute instead of relative. - * - * @param source the source {@link Path} to link to, may be relative or absolute. - * @param targetLink the {@link Path} where the symbolic link shall be created pointing to {@code source}. - */ - default void symlink(Path source, Path targetLink) { - - symlink(source, targetLink, true); - } - - /** - * @param source the source {@link Path file or folder} to copy. - * @param target the {@link Path} to copy {@code source} to. See {@link #copy(Path, Path, FileCopyMode)} for details. - * will always ensure that in the end you will find the same content of {@code source} in {@code target}. - */ - default void copy(Path source, Path target) { - - copy(source, target, FileCopyMode.COPY_TREE_FAIL_IF_EXISTS); - } - - /** - * @param source the source {@link Path file or folder} to copy. - * @param target the {@link Path} to copy {@code source} to. Unlike the Linux {@code cp} command this method will not - * take the filename of {@code source} and copy that to {@code target} in case that is an existing folder. Instead it - * will always be simple and stupid and just copy from {@code source} to {@code target}. Therefore the result is - * always clear and easy to predict and understand. Also you can easily rename a file to copy. While - * {@code cp my-file target} may lead to a different result than {@code cp my-file target/} this method will always - * ensure that in the end you will find the same content of {@code source} in {@code target}. - * @param fileOnly - {@code true} if {@code fileOrFolder} is expected to be a file and an exception shall be thrown if - * it is a directory, {@code false} otherwise (copy recursively). - */ - void copy(Path source, Path target, FileCopyMode fileOnly); - - /** - * @param archiveFile the {@link Path} to the file to extract. - * @param targetDir the {@link Path} to the directory where to extract the {@code archiveFile} to. - */ - default void extract(Path archiveFile, Path targetDir) { - - extract(archiveFile, targetDir, null); - } - - /** - * @param archiveFile the {@link Path} to the archive file to extract. - * @param targetDir the {@link Path} to the directory where to extract the {@code archiveFile}. - * @param postExtractHook the {@link Consumer} to be called after the extraction on the final folder before it is - * moved to {@code targetDir}. - */ - default void extract(Path archiveFile, Path targetDir, Consumer postExtractHook) { - - extract(archiveFile, targetDir, postExtractHook, true); - } - - /** - * @param archiveFile the {@link Path} to the archive file to extract. - * @param targetDir the {@link Path} to the directory where to extract the {@code archiveFile}. - * @param postExtractHook the {@link Consumer} to be called after the extraction on the final folder before it is - * moved to {@code targetDir}. - * @param extract {@code true} if the {@code archiveFile} should be extracted (default), {@code false} otherwise. - */ - void extract(Path archiveFile, Path targetDir, Consumer postExtractHook, boolean extract); - - /** - * Extracts a ZIP file what is the common archive format on Windows. Initially invented by PKZIP for MS-DOS and also - * famous from WinZIP software for Windows. - * - * @param file the ZIP file to extract. - * @param targetDir the {@link Path} with the directory to unzip to. - */ - void extractZip(Path file, Path targetDir); - - /** - * @param file the ZIP file to extract. - * @param targetDir the {@link Path} with the directory to unzip to. - * @param compression the {@link TarCompression} to use. - */ - void extractTar(Path file, Path targetDir, TarCompression compression); - - /** - * Extracts an Apple DMG (Disk Image) file that is similar to an ISO image. DMG files are commonly used for software - * releases on MacOS. Double-clicking such files on MacOS mounts them and show the application together with a - * symbolic link to the central applications folder and some help instructions. The user then copies the application - * to the applications folder via drag and drop in order to perform the installation. - * - * @param file the DMG file to extract. - * @param targetDir the target directory where to extract the contents to. - */ - void extractDmg(Path file, Path targetDir); - - /** - * Extracts an MSI (Microsoft Installer) file. MSI files are commonly used for software releases on Windows that allow - * an installation wizard and easy later uninstallation. - * - * @param file the MSI file to extract. - * @param targetDir the target directory where to extract the contents to. - */ - void extractMsi(Path file, Path targetDir); - - /** - * Extracts an Apple PKG (Package) file. PKG files are used instead of {@link #extractDmg(Path, Path) DMG files} if - * additional changes have to be performed like drivers to be installed. Similar to what - * {@link #extractMsi(Path, Path) MSI} is on Windows. PKG files are internally a xar based archive with a specific - * structure. - * - * @param file the PKG file to extract. - * @param targetDir the target directory where to extract the contents to. - */ - void extractPkg(Path file, Path targetDir); - - /** - * @param path the {@link Path} to convert. - * @return the absolute and physical {@link Path} (without symbolic links). - */ - Path toRealPath(Path path); - - /** - * Deletes the given {@link Path} idempotent and recursive. - * - * @param path the {@link Path} to delete. - */ - void delete(Path path); - - /** - * Creates a new temporary directory. ATTENTION: The user of this method is responsible to do house-keeping and - * {@link #delete(Path) delete} it after the work is done. - * - * @param name the default name of the temporary directory to create. A prefix or suffix may be added to ensure - * uniqueness. - * @return the {@link Path} to the newly created and unique temporary directory. - */ - Path createTempDir(String name); - - /** - * @param dir the folder to search. - * @param filter the {@link Predicate} used to find the {@link Predicate#test(Object) match}. - * @param recursive - {@code true} to search recursive in all sub-folders, {@code false} otherwise. - * @return the first child {@link Path} matching the given {@link Predicate} or {@code null} if no match was found. - */ - Path findFirst(Path dir, Predicate filter, boolean recursive); - - /** - * @param dir the {@link Path} to the directory where to list the children. - * @param filter the {@link Predicate} used to {@link Predicate#test(Object) decide} which children to include (if - * {@code true} is returned). - * @return all children of the given {@link Path} that match the given {@link Predicate}. Will be the empty list of - * the given {@link Path} is not an existing directory. - */ - List listChildren(Path dir, Predicate filter); - - /** - * Finds the existing file with the specified name in the given list of directories. - * - * @param fileName The name of the file to find. - * @param searchDirs The list of directories to search for the file. - * @return The {@code Path} of the existing file, or {@code null} if the file is not found. - */ - Path findExistingFile(String fileName, List searchDirs); - -} +package com.devonfw.tools.ide.io; + +import java.nio.file.Path; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Predicate; + +/** + * Interface that gives access to various operations on files. + */ +public interface FileAccess { + + /** + * Downloads a file from an arbitrary location. + * + * @param url the location of the binary file to download. May also be a local or remote path to copy from. + * @param targetFile the {@link Path} to the target file to download to. Should not already exists. Missing parent + * directories will be created automatically. + */ + void download(String url, Path targetFile); + + /** + * Creates the entire {@link Path} as directories if not already existing. + * + * @param directory the {@link Path} to + * {@link java.nio.file.Files#createDirectories(Path, java.nio.file.attribute.FileAttribute...) create}. + */ + void mkdirs(Path directory); + + /** + * @param file the {@link Path} to check. + * @return {@code true} if the given {@code file} points to an existing file, {@code false} otherwise (the given + * {@link Path} does not exist or is a directory). + */ + boolean isFile(Path file); + + /** + * @param folder the {@link Path} to check. + * @return {@code true} if the given {@code folder} points to an existing directory, {@code false} otherwise (a + * warning is logged in this case). + */ + boolean isExpectedFolder(Path folder); + + /** + * @param file the {@link Path} to compute the checksum of. + * @return the computed checksum (SHA-266). + */ + String checksum(Path file); + + /** + * Moves the given {@link Path} to the backup. + * + * @param fileOrFolder the {@link Path} to move to the backup (soft-deletion). + */ + void backup(Path fileOrFolder); + + /** + * @param source the source {@link Path file or folder} to move. + * @param targetDir the {@link Path} with the directory to move {@code source} into. + */ + void move(Path source, Path targetDir); + + /** + * Creates a symbolic link. If the given {@code targetLink} already exists and is a symbolic link or a Windows + * junction, it will be replaced. In case of missing privileges, Windows Junctions may be used as fallback, which must + * point to absolute paths. Therefore, the created link will be absolute instead of relative. + * + * @param source the source {@link Path} to link to, may be relative or absolute. + * @param targetLink the {@link Path} where the symbolic link shall be created pointing to {@code source}. + * @param relative - {@code true} if the symbolic link shall be relative, {@code false} if it shall be absolute. + */ + void symlink(Path source, Path targetLink, boolean relative); + + /** + * Creates a relative symbolic link. If the given {@code targetLink} already exists and is a symbolic link or a + * Windows junction, it will be replaced. In case of missing privileges, Windows Junctions may be used as fallback, + * which must point to absolute paths. Therefore, the created link will be absolute instead of relative. + * + * @param source the source {@link Path} to link to, may be relative or absolute. + * @param targetLink the {@link Path} where the symbolic link shall be created pointing to {@code source}. + */ + default void symlink(Path source, Path targetLink) { + + symlink(source, targetLink, true); + } + + /** + * @param source the source {@link Path file or folder} to copy. + * @param target the {@link Path} to copy {@code source} to. See {@link #copy(Path, Path, FileCopyMode)} for details. + * will always ensure that in the end you will find the same content of {@code source} in {@code target}. + */ + default void copy(Path source, Path target) { + + copy(source, target, FileCopyMode.COPY_TREE_FAIL_IF_EXISTS); + } + + /** + * @param source the source {@link Path file or folder} to copy. + * @param target the {@link Path} to copy {@code source} to. Unlike the Linux {@code cp} command this method will not + * take the filename of {@code source} and copy that to {@code target} in case that is an existing folder. Instead it + * will always be simple and stupid and just copy from {@code source} to {@code target}. Therefore the result is + * always clear and easy to predict and understand. Also you can easily rename a file to copy. While + * {@code cp my-file target} may lead to a different result than {@code cp my-file target/} this method will always + * ensure that in the end you will find the same content of {@code source} in {@code target}. + * @param fileOnly - {@code true} if {@code fileOrFolder} is expected to be a file and an exception shall be thrown if + * it is a directory, {@code false} otherwise (copy recursively). + */ + void copy(Path source, Path target, FileCopyMode fileOnly); + + /** + * @param archiveFile the {@link Path} to the file to extract. + * @param targetDir the {@link Path} to the directory where to extract the {@code archiveFile} to. + */ + default void extract(Path archiveFile, Path targetDir) { + + extract(archiveFile, targetDir, null); + } + + /** + * @param archiveFile the {@link Path} to the archive file to extract. + * @param targetDir the {@link Path} to the directory where to extract the {@code archiveFile}. + * @param postExtractHook the {@link Consumer} to be called after the extraction on the final folder before it is + * moved to {@code targetDir}. + */ + default void extract(Path archiveFile, Path targetDir, Consumer postExtractHook) { + + extract(archiveFile, targetDir, postExtractHook, true); + } + + /** + * @param archiveFile the {@link Path} to the archive file to extract. + * @param targetDir the {@link Path} to the directory where to extract the {@code archiveFile}. + * @param postExtractHook the {@link Consumer} to be called after the extraction on the final folder before it is + * moved to {@code targetDir}. + * @param extract {@code true} if the {@code archiveFile} should be extracted (default), {@code false} otherwise. + */ + void extract(Path archiveFile, Path targetDir, Consumer postExtractHook, boolean extract); + + /** + * Extracts a ZIP file what is the common archive format on Windows. Initially invented by PKZIP for MS-DOS and also + * famous from WinZIP software for Windows. + * + * @param file the ZIP file to extract. + * @param targetDir the {@link Path} with the directory to unzip to. + */ + void extractZip(Path file, Path targetDir); + + /** + * @param file the ZIP file to extract. + * @param targetDir the {@link Path} with the directory to unzip to. + * @param compression the {@link TarCompression} to use. + */ + void extractTar(Path file, Path targetDir, TarCompression compression); + + /** + * Extracts an Apple DMG (Disk Image) file that is similar to an ISO image. DMG files are commonly used for software + * releases on MacOS. Double-clicking such files on MacOS mounts them and show the application together with a + * symbolic link to the central applications folder and some help instructions. The user then copies the application + * to the applications folder via drag and drop in order to perform the installation. + * + * @param file the DMG file to extract. + * @param targetDir the target directory where to extract the contents to. + */ + void extractDmg(Path file, Path targetDir); + + /** + * Extracts an MSI (Microsoft Installer) file. MSI files are commonly used for software releases on Windows that allow + * an installation wizard and easy later uninstallation. + * + * @param file the MSI file to extract. + * @param targetDir the target directory where to extract the contents to. + */ + void extractMsi(Path file, Path targetDir); + + /** + * Extracts an Apple PKG (Package) file. PKG files are used instead of {@link #extractDmg(Path, Path) DMG files} if + * additional changes have to be performed like drivers to be installed. Similar to what + * {@link #extractMsi(Path, Path) MSI} is on Windows. PKG files are internally a xar based archive with a specific + * structure. + * + * @param file the PKG file to extract. + * @param targetDir the target directory where to extract the contents to. + */ + void extractPkg(Path file, Path targetDir); + + /** + * @param path the {@link Path} to convert. + * @return the absolute and physical {@link Path} (without symbolic links). + */ + Path toRealPath(Path path); + + /** + * Deletes the given {@link Path} idempotent and recursive. + * + * @param path the {@link Path} to delete. + */ + void delete(Path path); + + /** + * Creates a new temporary directory. ATTENTION: The user of this method is responsible to do house-keeping and + * {@link #delete(Path) delete} it after the work is done. + * + * @param name the default name of the temporary directory to create. A prefix or suffix may be added to ensure + * uniqueness. + * @return the {@link Path} to the newly created and unique temporary directory. + */ + Path createTempDir(String name); + + /** + * @param dir the folder to search. + * @param filter the {@link Predicate} used to find the {@link Predicate#test(Object) match}. + * @param recursive - {@code true} to search recursive in all sub-folders, {@code false} otherwise. + * @return the first child {@link Path} matching the given {@link Predicate} or {@code null} if no match was found. + */ + Path findFirst(Path dir, Predicate filter, boolean recursive); + + /** + * @param dir the {@link Path} to the directory where to list the children. + * @param filter the {@link Predicate} used to {@link Predicate#test(Object) decide} which children to include (if + * {@code true} is returned). + * @return all children of the given {@link Path} that match the given {@link Predicate}. Will be the empty list of + * the given {@link Path} is not an existing directory. + */ + List listChildren(Path dir, Predicate filter); + + /** + * Finds the existing file with the specified name in the given list of directories. + * + * @param fileName The name of the file to find. + * @param searchDirs The list of directories to search for the file. + * @return The {@code Path} of the existing file, or {@code null} if the file is not found. + */ + Path findExistingFile(String fileName, List searchDirs); + +} From de2c3f9020dbee99dc0aca785f1b07c449fece54 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Wed, 13 Mar 2024 22:04:23 +0100 Subject: [PATCH 83/93] solved minor bug --- .../tools/ide/context/GitContextImpl.java | 576 +++++++++--------- 1 file changed, 292 insertions(+), 284 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/context/GitContextImpl.java b/cli/src/main/java/com/devonfw/tools/ide/context/GitContextImpl.java index 6d01b0e72..c7d06718b 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/context/GitContextImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/context/GitContextImpl.java @@ -1,284 +1,292 @@ -package com.devonfw.tools.ide.context; - -import java.io.IOException; -import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.attribute.FileTime; -import java.time.Duration; -import java.util.AbstractMap; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; - -import com.devonfw.tools.ide.cli.CliException; -import com.devonfw.tools.ide.log.IdeLogLevel; -import com.devonfw.tools.ide.log.IdeSubLogger; -import com.devonfw.tools.ide.process.ProcessContext; -import com.devonfw.tools.ide.process.ProcessErrorHandling; -import com.devonfw.tools.ide.process.ProcessMode; -import com.devonfw.tools.ide.process.ProcessResult; - -/** - * Implements the {@link GitContext}. - */ -public class GitContextImpl implements GitContext { - private static final Duration GIT_PULL_CACHE_DELAY_MILLIS = Duration.ofMillis(30 * 60 * 1000); - - ; - - private final IdeContext context; - - private ProcessContext processContext; - - /** - * @param context the {@link IdeContext context}. - */ - public GitContextImpl(IdeContext context) { - - this.context = context; - - } - - @Override - public void pullOrCloneIfNeeded(String repoUrl, String branch, Path targetRepository) { - - Path gitDirectory = targetRepository.resolve(".git"); - - // Check if the .git directory exists - if (Files.isDirectory(gitDirectory)) { - Path magicFilePath = gitDirectory.resolve("HEAD"); - long currentTime = System.currentTimeMillis(); - // Get the modification time of the magic file - long fileMTime; - try { - fileMTime = Files.getLastModifiedTime(magicFilePath).toMillis(); - } catch (IOException e) { - throw new IllegalStateException("Could not read " + magicFilePath, e); - } - - // Check if the file modification time is older than the delta threshold - if ((currentTime - fileMTime > GIT_PULL_CACHE_DELAY_MILLIS.toMillis()) || context.isForceMode()) { - pullOrClone(repoUrl, "", targetRepository); - try { - Files.setLastModifiedTime(magicFilePath, FileTime.fromMillis(currentTime)); - } catch (IOException e) { - throw new IllegalStateException("Could not read or write in " + magicFilePath, e); - } - } - } else { - // If the .git directory does not exist, perform git clone - pullOrClone(repoUrl, branch, targetRepository); - } - } - - public void pullOrFetchAndResetIfNeeded(String repoUrl, String branch, Path targetRepository, String remoteName) { - - pullOrCloneIfNeeded(repoUrl, branch, targetRepository); - - if (remoteName.isEmpty()) { - reset(targetRepository, "origin", "master"); - } else { - reset(targetRepository, remoteName, "master"); - } - - cleanup(targetRepository); - } - - @Override - public void pullOrClone(String gitRepoUrl, String branch, Path targetRepository) { - - Objects.requireNonNull(targetRepository); - Objects.requireNonNull(gitRepoUrl); - - if (!gitRepoUrl.startsWith("http")) { - throw new IllegalArgumentException("Invalid git URL '" + gitRepoUrl + "'!"); - } - - initializeProcessContext(targetRepository); - if (Files.isDirectory(targetRepository.resolve(".git"))) { - // checks for remotes - ProcessResult result = this.processContext.addArg("remote").run(ProcessMode.DEFAULT_CAPTURE); - List remotes = result.getOut(); - if (remotes.isEmpty()) { - String message = targetRepository - + " is a local git repository with no remote - if you did this for testing, you may continue...\n" - + "Do you want to ignore the problem and continue anyhow?"; - this.context.askToContinue(message); - } else { - this.processContext.errorHandling(ProcessErrorHandling.WARNING); - - if (!this.context.isOffline()) { - pull(targetRepository); - } - } - } else { - clone(new GitUrl(gitRepoUrl, branch), targetRepository); - if (!branch.isEmpty()) { - this.processContext.addArgs("checkout", branch); - this.processContext.run(); - } - } - } - - /** - * Handles errors which occurred during 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. - * @param result the {@link ProcessResult} to evaluate. - */ - private void handleErrors(Path targetRepository, ProcessResult result) { - - if (!result.isSuccessful()) { - String message = "Failed to update git repository at " + targetRepository; - if (this.context.isOffline()) { - this.context.warning(message); - this.context.interaction("Continuing as we are in offline mode - results may be outdated!"); - } else { - this.context.error(message); - if (this.context.isOnline()) { - this.context.error( - "See above error for details. If you have local changes, please stash or revert and retry."); - } else { - this.context.error( - "It seems you are offline - please ensure Internet connectivity and retry or activate offline mode (-o or --offline)."); - } - this.context.askToContinue("Typically you should abort and fix the problem. Do you want to continue anyways?"); - } - } - } - - /** - * Lazily initializes the {@link ProcessContext}. - * - * @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. - */ - private void initializeProcessContext(Path targetRepository) { - - if (this.processContext == null) { - this.processContext = this.context.newProcess().directory(targetRepository).executable("git") - .withEnvVar("GIT_TERMINAL_PROMPT", "0"); - } - } - - @Override - public void clone(GitUrl gitRepoUrl, Path targetRepository) { - - URL parsedUrl = gitRepoUrl.parseUrl(); - initializeProcessContext(targetRepository); - ProcessResult result; - if (!this.context.isOffline()) { - this.context.getFileAccess().mkdirs(targetRepository); - this.context.requireOnline("git clone of " + parsedUrl); - this.processContext.addArg("clone"); - if (this.context.isQuietMode()) { - this.processContext.addArg("-q"); - } - this.processContext.addArgs("--recursive", "-b", gitRepoUrl.branch(), gitRepoUrl.url(), "--config", - "core.autocrlf=false", "."); - result = this.processContext.run(ProcessMode.DEFAULT_CAPTURE); - if (!result.isSuccessful()) { - this.context.warning("Git failed to clone {} into {}.", parsedUrl, targetRepository); - } - } else { - throw new CliException("Could not clone " + parsedUrl + " to " + targetRepository + " because you are offline."); - } - } - - @Override - public void pull(Path targetRepository) { - - initializeProcessContext(targetRepository); - ProcessResult result; - // pull from remote - result = this.processContext.addArg("--no-pager").addArg("pull").run(ProcessMode.DEFAULT_CAPTURE); - - if (!result.isSuccessful()) { - Map remoteAndBranchName = retrieveRemoteAndBranchName(); - context.warning("Git pull for {}/{} failed for repository {}.", remoteAndBranchName.get("remote"), - remoteAndBranchName.get("branch"), targetRepository); - handleErrors(targetRepository, result); - } - } - - private Map retrieveRemoteAndBranchName() { - - Map remoteAndBranchName = new HashMap<>(); - ProcessResult remoteResult = this.processContext.addArg("branch").addArg("-vv").run(ProcessMode.DEFAULT_CAPTURE); - List remotes = remoteResult.getOut(); - if (!remotes.isEmpty()) { - for (String remote : remotes) { - if (remote.startsWith("*")) { - String checkedOutBranch = remote.substring(remote.indexOf("[") + 1, remote.indexOf("]")); - remoteAndBranchName.put("remote", checkedOutBranch.substring(0, checkedOutBranch.indexOf("/"))); - // check if current repo is behind remote and omit message - if (checkedOutBranch.contains(":")) { - remoteAndBranchName.put("branch", - checkedOutBranch.substring(checkedOutBranch.indexOf("/") + 1, checkedOutBranch.indexOf(":"))); - } else { - remoteAndBranchName.put("branch", checkedOutBranch.substring(checkedOutBranch.indexOf("/") + 1)); - } - - } - } - } else { - return Map.ofEntries(new AbstractMap.SimpleEntry<>("remote", "unknown"), - new AbstractMap.SimpleEntry<>("branch", "unknown")); - } - - return remoteAndBranchName; - } - - @Override - public void reset(Path targetRepository, String remoteName, String branchName) { - - initializeProcessContext(targetRepository); - ProcessResult result; - // check for changed files - result = this.processContext.addArg("diff-index").addArg("--quiet").addArg("HEAD").run(ProcessMode.DEFAULT_CAPTURE); - - if (!result.isSuccessful()) { - // reset to origin/master - context.warning("Git has detected modified files -- attempting to reset {} to '{}/{}'.", targetRepository, - remoteName, branchName); - result = this.processContext.addArg("reset").addArg("--hard").addArg(remoteName + "/" + branchName) - .run(ProcessMode.DEFAULT_CAPTURE); - - if (!result.isSuccessful()) { - context.warning("Git failed to reset {} to '{}/{}'.", remoteName, branchName, targetRepository); - handleErrors(targetRepository, result); - } - } - } - - @Override - public void cleanup(Path targetRepository) { - - initializeProcessContext(targetRepository); - ProcessResult result; - // check for untracked files - result = this.processContext.addArg("ls-files").addArg("--other").addArg("--directory").addArg("--exclude-standard") - .run(ProcessMode.DEFAULT_CAPTURE); - - if (!result.getOut().isEmpty()) { - // delete untracked files - context.warning("Git detected untracked files in {} and is attempting a cleanup.", targetRepository); - result = this.processContext.addArg("clean").addArg("-df").run(ProcessMode.DEFAULT_CAPTURE); - - if (!result.isSuccessful()) { - context.warning("Git failed to clean the repository {}.", targetRepository); - } - } - } - - @Override - public IdeSubLogger level(IdeLogLevel level) { - - return null; - } -} +package com.devonfw.tools.ide.context; + +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.FileTime; +import java.time.Duration; +import java.util.AbstractMap; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import com.devonfw.tools.ide.cli.CliException; +import com.devonfw.tools.ide.log.IdeLogLevel; +import com.devonfw.tools.ide.log.IdeSubLogger; +import com.devonfw.tools.ide.process.ProcessContext; +import com.devonfw.tools.ide.process.ProcessErrorHandling; +import com.devonfw.tools.ide.process.ProcessMode; +import com.devonfw.tools.ide.process.ProcessResult; + +/** + * Implements the {@link GitContext}. + */ +public class GitContextImpl implements GitContext { + private static final Duration GIT_PULL_CACHE_DELAY_MILLIS = Duration.ofMillis(30 * 60 * 1000); + + ; + + private final IdeContext context; + + private ProcessContext processContext; + + /** + * @param context the {@link IdeContext context}. + */ + public GitContextImpl(IdeContext context) { + + this.context = context; + + } + + @Override + public void pullOrCloneIfNeeded(String repoUrl, String branch, Path targetRepository) { + + Path gitDirectory = targetRepository.resolve(".git"); + + // Check if the .git directory exists + if (Files.isDirectory(gitDirectory)) { + Path magicFilePath = gitDirectory.resolve("HEAD"); + long currentTime = System.currentTimeMillis(); + // Get the modification time of the magic file + long fileMTime; + try { + fileMTime = Files.getLastModifiedTime(magicFilePath).toMillis(); + } catch (IOException e) { + throw new IllegalStateException("Could not read " + magicFilePath, e); + } + + // Check if the file modification time is older than the delta threshold + if ((currentTime - fileMTime > GIT_PULL_CACHE_DELAY_MILLIS.toMillis()) || context.isForceMode()) { + pullOrClone(repoUrl, "", targetRepository); + try { + Files.setLastModifiedTime(magicFilePath, FileTime.fromMillis(currentTime)); + } catch (IOException e) { + throw new IllegalStateException("Could not read or write in " + magicFilePath, e); + } + } + } else { + // If the .git directory does not exist, perform git clone + pullOrClone(repoUrl, branch, targetRepository); + } + } + + public void pullOrFetchAndResetIfNeeded(String repoUrl, String branch, Path targetRepository, String remoteName) { + + pullOrCloneIfNeeded(repoUrl, branch, targetRepository); + + if (remoteName.isEmpty()) { + reset(targetRepository, "origin", "master"); + } else { + reset(targetRepository, remoteName, "master"); + } + + cleanup(targetRepository); + } + + @Override + public void pullOrClone(String gitRepoUrl, String branch, Path targetRepository) { + + Objects.requireNonNull(targetRepository); + Objects.requireNonNull(gitRepoUrl); + + if (!gitRepoUrl.startsWith("http")) { + throw new IllegalArgumentException("Invalid git URL '" + gitRepoUrl + "'!"); + } + + initializeProcessContext(targetRepository); + if (Files.isDirectory(targetRepository.resolve(".git"))) { + // checks for remotes + ProcessResult result = this.processContext.addArg("remote").run(ProcessMode.DEFAULT_CAPTURE); + List remotes = result.getOut(); + if (remotes.isEmpty()) { + String message = targetRepository + + " is a local git repository with no remote - if you did this for testing, you may continue...\n" + + "Do you want to ignore the problem and continue anyhow?"; + this.context.askToContinue(message); + } else { + this.processContext.errorHandling(ProcessErrorHandling.WARNING); + + if (!this.context.isOffline()) { + pull(targetRepository); + } + } + } else { + clone(new GitUrl(gitRepoUrl, branch), targetRepository); + if (!branch.isEmpty()) { + this.processContext.addArgs("checkout", branch); + this.processContext.run(); + } + } + } + + /** + * Handles errors which occurred during 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. + * @param result the {@link ProcessResult} to evaluate. + */ + private void handleErrors(Path targetRepository, ProcessResult result) { + + if (!result.isSuccessful()) { + String message = "Failed to update git repository at " + targetRepository; + if (this.context.isOffline()) { + this.context.warning(message); + this.context.interaction("Continuing as we are in offline mode - results may be outdated!"); + } else { + this.context.error(message); + if (this.context.isOnline()) { + this.context.error( + "See above error for details. If you have local changes, please stash or revert and retry."); + } else { + this.context.error( + "It seems you are offline - please ensure Internet connectivity and retry or activate offline mode (-o or --offline)."); + } + this.context.askToContinue("Typically you should abort and fix the problem. Do you want to continue anyways?"); + } + } + } + + /** + * Lazily initializes the {@link ProcessContext}. + * + * @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. + */ + private void initializeProcessContext(Path targetRepository) { + + if (this.processContext == null) { + this.processContext = this.context.newProcess().directory(targetRepository).executable("git") + .withEnvVar("GIT_TERMINAL_PROMPT", "0"); + } + } + + @Override + public void clone(GitUrl gitRepoUrl, Path targetRepository) { + + URL parsedUrl = gitRepoUrl.parseUrl(); + initializeProcessContext(targetRepository); + ProcessResult result; + if (!this.context.isOffline()) { + this.context.getFileAccess().mkdirs(targetRepository); + this.context.requireOnline("git clone of " + parsedUrl); + this.processContext.addArg("clone"); + if (this.context.isQuietMode()) { + this.processContext.addArg("-q"); + } + this.processContext.addArgs("--recursive", gitRepoUrl.url(), "--config", "core.autocrlf=false", "."); + result = this.processContext.run(ProcessMode.DEFAULT_CAPTURE); + if (!result.isSuccessful()) { + this.context.warning("Git failed to clone {} into {}.", parsedUrl, targetRepository); + } + String branch = gitRepoUrl.branch(); + if (branch != null) { + this.processContext.addArgs("checkout", branch); + this.processContext.run(); + result = this.processContext.run(ProcessMode.DEFAULT_CAPTURE); + if (!result.isSuccessful()) { + this.context.warning("Git failed to checkout to branch {}", branch); + } + } + } else { + throw new CliException("Could not clone " + parsedUrl + " to " + targetRepository + " because you are offline."); + } + } + + @Override + public void pull(Path targetRepository) { + + initializeProcessContext(targetRepository); + ProcessResult result; + // pull from remote + result = this.processContext.addArg("--no-pager").addArg("pull").run(ProcessMode.DEFAULT_CAPTURE); + + if (!result.isSuccessful()) { + Map remoteAndBranchName = retrieveRemoteAndBranchName(); + context.warning("Git pull for {}/{} failed for repository {}.", remoteAndBranchName.get("remote"), + remoteAndBranchName.get("branch"), targetRepository); + handleErrors(targetRepository, result); + } + } + + private Map retrieveRemoteAndBranchName() { + + Map remoteAndBranchName = new HashMap<>(); + ProcessResult remoteResult = this.processContext.addArg("branch").addArg("-vv").run(ProcessMode.DEFAULT_CAPTURE); + List remotes = remoteResult.getOut(); + if (!remotes.isEmpty()) { + for (String remote : remotes) { + if (remote.startsWith("*")) { + String checkedOutBranch = remote.substring(remote.indexOf("[") + 1, remote.indexOf("]")); + remoteAndBranchName.put("remote", checkedOutBranch.substring(0, checkedOutBranch.indexOf("/"))); + // check if current repo is behind remote and omit message + if (checkedOutBranch.contains(":")) { + remoteAndBranchName.put("branch", + checkedOutBranch.substring(checkedOutBranch.indexOf("/") + 1, checkedOutBranch.indexOf(":"))); + } else { + remoteAndBranchName.put("branch", checkedOutBranch.substring(checkedOutBranch.indexOf("/") + 1)); + } + + } + } + } else { + return Map.ofEntries(new AbstractMap.SimpleEntry<>("remote", "unknown"), + new AbstractMap.SimpleEntry<>("branch", "unknown")); + } + + return remoteAndBranchName; + } + + @Override + public void reset(Path targetRepository, String remoteName, String branchName) { + + initializeProcessContext(targetRepository); + ProcessResult result; + // check for changed files + result = this.processContext.addArg("diff-index").addArg("--quiet").addArg("HEAD").run(ProcessMode.DEFAULT_CAPTURE); + + if (!result.isSuccessful()) { + // reset to origin/master + context.warning("Git has detected modified files -- attempting to reset {} to '{}/{}'.", targetRepository, + remoteName, branchName); + result = this.processContext.addArg("reset").addArg("--hard").addArg(remoteName + "/" + branchName) + .run(ProcessMode.DEFAULT_CAPTURE); + + if (!result.isSuccessful()) { + context.warning("Git failed to reset {} to '{}/{}'.", remoteName, branchName, targetRepository); + handleErrors(targetRepository, result); + } + } + } + + @Override + public void cleanup(Path targetRepository) { + + initializeProcessContext(targetRepository); + ProcessResult result; + // check for untracked files + result = this.processContext.addArg("ls-files").addArg("--other").addArg("--directory").addArg("--exclude-standard") + .run(ProcessMode.DEFAULT_CAPTURE); + + if (!result.getOut().isEmpty()) { + // delete untracked files + context.warning("Git detected untracked files in {} and is attempting a cleanup.", targetRepository); + result = this.processContext.addArg("clean").addArg("-df").run(ProcessMode.DEFAULT_CAPTURE); + + if (!result.isSuccessful()) { + context.warning("Git failed to clean the repository {}.", targetRepository); + } + } + } + + @Override + public IdeSubLogger level(IdeLogLevel level) { + + return null; + } +} From 6a1eefcc6a0021f8d170255f4c3172373ec8a351 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Wed, 13 Mar 2024 22:30:26 +0100 Subject: [PATCH 84/93] cleanup --- .../java/com/devonfw/tools/ide/context/GitContextImpl.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/context/GitContextImpl.java b/cli/src/main/java/com/devonfw/tools/ide/context/GitContextImpl.java index c7d06718b..9813bdd5e 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/context/GitContextImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/context/GitContextImpl.java @@ -115,10 +115,6 @@ public void pullOrClone(String gitRepoUrl, String branch, Path targetRepository) } } else { clone(new GitUrl(gitRepoUrl, branch), targetRepository); - if (!branch.isEmpty()) { - this.processContext.addArgs("checkout", branch); - this.processContext.run(); - } } } @@ -187,7 +183,6 @@ public void clone(GitUrl gitRepoUrl, Path targetRepository) { String branch = gitRepoUrl.branch(); if (branch != null) { this.processContext.addArgs("checkout", branch); - this.processContext.run(); result = this.processContext.run(ProcessMode.DEFAULT_CAPTURE); if (!result.isSuccessful()) { this.context.warning("Git failed to checkout to branch {}", branch); From 8a06af51e5683157484b09c0d44703f8490fd558 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Wed, 13 Mar 2024 22:38:55 +0100 Subject: [PATCH 85/93] again crlf switching .. --- .../tools/ide/context/AbstractIdeContext.java | 1716 ++++++++--------- 1 file changed, 858 insertions(+), 858 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java b/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java index d514eb48e..9d60ccfa1 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java +++ b/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java @@ -1,858 +1,858 @@ -package com.devonfw.tools.ide.context; - -import com.devonfw.tools.ide.cli.CliArgument; -import com.devonfw.tools.ide.cli.CliArguments; -import com.devonfw.tools.ide.cli.CliException; -import com.devonfw.tools.ide.commandlet.Commandlet; -import com.devonfw.tools.ide.commandlet.CommandletManager; -import com.devonfw.tools.ide.commandlet.CommandletManagerImpl; -import com.devonfw.tools.ide.commandlet.ContextCommandlet; -import com.devonfw.tools.ide.commandlet.HelpCommandlet; -import com.devonfw.tools.ide.common.SystemPath; -import com.devonfw.tools.ide.completion.CompletionCandidate; -import com.devonfw.tools.ide.completion.CompletionCandidateCollector; -import com.devonfw.tools.ide.completion.CompletionCandidateCollectorDefault; -import com.devonfw.tools.ide.environment.AbstractEnvironmentVariables; -import com.devonfw.tools.ide.environment.EnvironmentVariables; -import com.devonfw.tools.ide.environment.EnvironmentVariablesType; -import com.devonfw.tools.ide.io.FileAccess; -import com.devonfw.tools.ide.io.FileAccessImpl; -import com.devonfw.tools.ide.log.IdeLogLevel; -import com.devonfw.tools.ide.log.IdeSubLogger; -import com.devonfw.tools.ide.log.IdeSubLoggerNone; -import com.devonfw.tools.ide.merge.DirectoryMerger; -import com.devonfw.tools.ide.os.SystemInfo; -import com.devonfw.tools.ide.os.SystemInfoImpl; -import com.devonfw.tools.ide.process.ProcessContext; -import com.devonfw.tools.ide.process.ProcessContextImpl; -import com.devonfw.tools.ide.process.ProcessResult; -import com.devonfw.tools.ide.property.Property; -import com.devonfw.tools.ide.repo.CustomToolRepository; -import com.devonfw.tools.ide.repo.CustomToolRepositoryImpl; -import com.devonfw.tools.ide.repo.DefaultToolRepository; -import com.devonfw.tools.ide.repo.ToolRepository; -import com.devonfw.tools.ide.url.model.UrlMetadata; - -import java.io.IOException; -import java.net.InetAddress; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Objects; -import java.util.function.Function; - -/** - * Abstract base implementation of {@link IdeContext}. - */ -public abstract class AbstractIdeContext implements IdeContext { - - private static final String IDE_URLS_GIT = "https://github.com/devonfw/ide-urls.git"; - - private final Map loggers; - - private final Path ideHome; - - private final Path ideRoot; - - private final Path confPath; - - private final Path settingsPath; - - private final Path softwarePath; - - private final Path softwareRepositoryPath; - - private final Path pluginsPath; - - private final Path workspacePath; - - private final String workspaceName; - - private final Path urlsPath; - - private final Path tempPath; - - private final Path tempDownloadPath; - - private final Path cwd; - - private final Path downloadPath; - - private final Path toolRepository; - - private final Path userHome; - - private final Path userHomeIde; - - private final SystemPath path; - - private final SystemInfo systemInfo; - - private final EnvironmentVariables variables; - - private final FileAccess fileAccess; - - private final CommandletManager commandletManager; - - private final ToolRepository defaultToolRepository; - - private final CustomToolRepository customToolRepository; - - private final DirectoryMerger workspaceMerger; - - private final Function loggerFactory; - - private boolean offlineMode; - - private boolean forceMode; - - private boolean batchMode; - - private boolean quietMode; - - private Locale locale; - - private UrlMetadata urlMetadata; - - private Path defaultExecutionDirectory; - - /** - * The constructor. - * - * @param minLogLevel the minimum {@link IdeLogLevel} to enable. Should be {@link IdeLogLevel#INFO} by default. - * @param factory the {@link Function} to create {@link IdeSubLogger} per {@link IdeLogLevel}. - * @param userDir the optional {@link Path} to current working directory. - * @param toolRepository @param toolRepository the {@link ToolRepository} of the context. If it is set to {@code null} - * {@link DefaultToolRepository} will be used. - */ - public AbstractIdeContext(IdeLogLevel minLogLevel, Function factory, Path userDir, - ToolRepository toolRepository) { - - super(); - this.loggerFactory = factory; - this.loggers = new HashMap<>(); - setLogLevel(minLogLevel); - this.systemInfo = SystemInfoImpl.INSTANCE; - this.commandletManager = new CommandletManagerImpl(this); - this.fileAccess = new FileAccessImpl(this); - String workspace = WORKSPACE_MAIN; - if (userDir == null) { - this.cwd = Path.of(System.getProperty("user.dir")); - } else { - this.cwd = userDir.toAbsolutePath(); - } - // detect IDE_HOME and WORKSPACE - Path currentDir = this.cwd; - String name1 = ""; - String name2 = ""; - while (currentDir != null) { - trace("Looking for IDE_HOME in {}", currentDir); - if (isIdeHome(currentDir)) { - if (FOLDER_WORKSPACES.equals(name1)) { - workspace = name2; - } - break; - } - name2 = name1; - int nameCount = currentDir.getNameCount(); - if (nameCount >= 1) { - name1 = currentDir.getName(nameCount - 1).toString(); - } - currentDir = getParentPath(currentDir); - } - // detection completed, initializing variables - this.ideHome = currentDir; - this.workspaceName = workspace; - if (this.ideHome == null) { - info(getMessageIdeHomeNotFound()); - this.workspacePath = null; - this.ideRoot = null; - this.confPath = null; - this.settingsPath = null; - this.softwarePath = null; - this.pluginsPath = null; - } else { - debug(getMessageIdeHomeFound()); - this.workspacePath = this.ideHome.resolve(FOLDER_WORKSPACES).resolve(this.workspaceName); - Path ideRootPath = this.ideHome.getParent(); - String root = null; - if (!isTest()) { - root = System.getenv("IDE_ROOT"); - } - if (root != null) { - Path rootPath = Path.of(root); - if (Files.isDirectory(rootPath)) { - if (!ideRootPath.equals(rootPath)) { - warning( - "Variable IDE_ROOT is set to '{}' but for your project '{}' the path '{}' would have been expected.", - root, this.ideHome.getFileName(), ideRootPath); - } - ideRootPath = rootPath; - } else { - warning("Variable IDE_ROOT is not set to a valid directory '{}'." + root); - ideRootPath = null; - } - } - this.ideRoot = ideRootPath; - this.confPath = this.ideHome.resolve(FOLDER_CONF); - this.settingsPath = this.ideHome.resolve(FOLDER_SETTINGS); - this.softwarePath = this.ideHome.resolve(FOLDER_SOFTWARE); - this.pluginsPath = this.ideHome.resolve(FOLDER_PLUGINS); - } - if (this.ideRoot == null) { - this.toolRepository = null; - this.urlsPath = null; - this.tempPath = null; - this.tempDownloadPath = null; - this.softwareRepositoryPath = null; - } else { - Path ideBase = this.ideRoot.resolve(FOLDER_IDE); - this.toolRepository = ideBase.resolve("software"); - this.urlsPath = ideBase.resolve("urls"); - this.tempPath = ideBase.resolve("tmp"); - this.tempDownloadPath = this.tempPath.resolve(FOLDER_DOWNLOADS); - this.softwareRepositoryPath = ideBase.resolve(FOLDER_SOFTWARE); - if (Files.isDirectory(this.tempPath)) { - // TODO delete all files older than 1 day here... - } else { - this.fileAccess.mkdirs(this.tempDownloadPath); - } - } - if (isTest()) { - // only for testing... - if (this.ideHome == null) { - this.userHome = Path.of("/non-existing-user-home-for-testing"); - } else { - this.userHome = this.ideHome.resolve("home"); - } - } else { - this.userHome = Path.of(System.getProperty("user.home")); - } - this.userHomeIde = this.userHome.resolve(".ide"); - this.downloadPath = this.userHome.resolve("Downloads/ide"); - this.variables = createVariables(); - this.path = computeSystemPath(); - - if (toolRepository == null) { - this.defaultToolRepository = new DefaultToolRepository(this); - } else { - this.defaultToolRepository = toolRepository; - } - - this.customToolRepository = CustomToolRepositoryImpl.of(this); - this.workspaceMerger = new DirectoryMerger(this); - } - - private String getMessageIdeHomeFound() { - - return "IDE environment variables have been set for " + this.ideHome + " in workspace " + this.workspaceName; - } - - private String getMessageIdeHomeNotFound() { - - return "You are not inside an IDE installation: " + this.cwd; - } - - /** - * @return the status message about the {@link #getIdeHome() IDE_HOME} detection and environment variable - * initialization. - */ - public String getMessageIdeHome() { - - if (this.ideHome == null) { - return getMessageIdeHomeNotFound(); - } - return getMessageIdeHomeFound(); - } - - /** - * @return {@code true} if this is a test context for JUnits, {@code false} otherwise. - */ - public boolean isTest() { - - return isMock(); - } - - /** - * @return {@code true} if this is a mock context for JUnits, {@code false} otherwise. - */ - public boolean isMock() { - - return false; - } - - private SystemPath computeSystemPath() { - - return new SystemPath(this); - } - - private boolean isIdeHome(Path dir) { - - if (!Files.isDirectory(dir.resolve("workspaces"))) { - return false; - } else if (!Files.isDirectory(dir.resolve("settings"))) { - return false; - } - return true; - } - - private Path getParentPath(Path dir) { - - try { - Path linkDir = dir.toRealPath(); - if (!dir.equals(linkDir)) { - return linkDir; - } else { - return dir.getParent(); - } - } catch (IOException e) { - throw new IllegalStateException(e); - } - - } - - private EnvironmentVariables createVariables() { - - AbstractEnvironmentVariables system = EnvironmentVariables.ofSystem(this); - AbstractEnvironmentVariables user = extendVariables(system, this.userHomeIde, EnvironmentVariablesType.USER); - AbstractEnvironmentVariables settings = extendVariables(user, this.settingsPath, EnvironmentVariablesType.SETTINGS); - // TODO should we keep this workspace properties? Was this feature ever used? - AbstractEnvironmentVariables workspace = extendVariables(settings, this.workspacePath, - EnvironmentVariablesType.WORKSPACE); - AbstractEnvironmentVariables conf = extendVariables(workspace, this.confPath, EnvironmentVariablesType.CONF); - return conf.resolved(); - } - - private AbstractEnvironmentVariables extendVariables(AbstractEnvironmentVariables envVariables, Path propertiesPath, - EnvironmentVariablesType type) { - - Path propertiesFile = null; - if (propertiesPath == null) { - trace("Configuration directory for type {} does not exist.", type); - } else if (Files.isDirectory(propertiesPath)) { - propertiesFile = propertiesPath.resolve(EnvironmentVariables.DEFAULT_PROPERTIES); - boolean legacySupport = (type != EnvironmentVariablesType.USER); - if (legacySupport && !Files.exists(propertiesFile)) { - Path legacyFile = propertiesPath.resolve(EnvironmentVariables.LEGACY_PROPERTIES); - if (Files.exists(legacyFile)) { - propertiesFile = legacyFile; - } - } - } else { - debug("Configuration directory {} does not exist.", propertiesPath); - } - return envVariables.extend(propertiesFile, type); - } - - @Override - public SystemInfo getSystemInfo() { - - return this.systemInfo; - } - - @Override - public FileAccess getFileAccess() { - - return this.fileAccess; - } - - @Override - public CommandletManager getCommandletManager() { - - return this.commandletManager; - } - - @Override - public ToolRepository getDefaultToolRepository() { - - return this.defaultToolRepository; - } - - @Override - public CustomToolRepository getCustomToolRepository() { - - return this.customToolRepository; - } - - @Override - public Path getIdeHome() { - - return this.ideHome; - } - - @Override - public Path getIdeRoot() { - - return this.ideRoot; - } - - @Override - public Path getCwd() { - - return this.cwd; - } - - @Override - public Path getTempPath() { - - return this.tempPath; - } - - @Override - public Path getTempDownloadPath() { - - return this.tempDownloadPath; - } - - @Override - public Path getUserHome() { - - return this.userHome; - } - - @Override - public Path getUserHomeIde() { - - return this.userHomeIde; - } - - @Override - public Path getSettingsPath() { - - return this.settingsPath; - } - - @Override - public Path getConfPath() { - - return this.confPath; - } - - @Override - public Path getSoftwarePath() { - - return this.softwarePath; - } - - @Override - public Path getSoftwareRepositoryPath() { - - return this.softwareRepositoryPath; - } - - @Override - public Path getPluginsPath() { - - return this.pluginsPath; - } - - @Override - public String getWorkspaceName() { - - return this.workspaceName; - } - - @Override - public Path getWorkspacePath() { - - return this.workspacePath; - } - - @Override - public Path getDownloadPath() { - - return this.downloadPath; - } - - @Override - public Path getUrlsPath() { - - return this.urlsPath; - } - - @Override - public Path getToolRepositoryPath() { - - return this.toolRepository; - } - - @Override - public SystemPath getPath() { - - return this.path; - } - - @Override - public EnvironmentVariables getVariables() { - - return this.variables; - } - - @Override - public UrlMetadata getUrls() { - - if (this.urlMetadata == null) { - if (!isTest()) { - this.getGitContext().pullOrFetchAndResetIfNeeded(IDE_URLS_GIT, "", this.urlsPath, "origin"); - } - this.urlMetadata = new UrlMetadata(this); - } - return this.urlMetadata; - } - - @Override - public boolean isQuietMode() { - - return this.quietMode; - } - - /** - * @param quietMode new value of {@link #isQuietMode()}. - */ - public void setQuietMode(boolean quietMode) { - - this.quietMode = quietMode; - } - - @Override - public boolean isBatchMode() { - - return this.batchMode; - } - - /** - * @param batchMode new value of {@link #isBatchMode()}. - */ - public void setBatchMode(boolean batchMode) { - - this.batchMode = batchMode; - } - - @Override - public boolean isForceMode() { - - return this.forceMode; - } - - /** - * @param forceMode new value of {@link #isForceMode()}. - */ - public void setForceMode(boolean forceMode) { - - this.forceMode = forceMode; - } - - @Override - public boolean isOfflineMode() { - - return this.offlineMode; - } - - /** - * @param offlineMode new value of {@link #isOfflineMode()}. - */ - public void setOfflineMode(boolean offlineMode) { - - this.offlineMode = offlineMode; - } - - @Override - public boolean isOnline() { - - boolean online = false; - try { - int timeout = 1000; - online = InetAddress.getByName("github.com").isReachable(timeout); - } catch (Exception ignored) { - - } - return online; - } - - @Override - public Locale getLocale() { - - if (this.locale == null) { - return Locale.getDefault(); - } - return this.locale; - } - - /** - * @param locale new value of {@link #getLocale()}. - */ - public void setLocale(Locale locale) { - - this.locale = locale; - } - - @Override - public DirectoryMerger getWorkspaceMerger() { - - return this.workspaceMerger; - } - - /** - * @return the {@link #defaultExecutionDirectory} the directory in which a command process is executed. - */ - public Path getDefaultExecutionDirectory() { - - return this.defaultExecutionDirectory; - } - - /** - * @param defaultExecutionDirectory new value of {@link #getDefaultExecutionDirectory()}. - */ - public void setDefaultExecutionDirectory(Path defaultExecutionDirectory) { - - if (defaultExecutionDirectory != null) { - this.defaultExecutionDirectory = defaultExecutionDirectory; - } - } - - @Override - public GitContext getGitContext() { - - return new GitContextImpl(this); - } - - @Override - public ProcessContext newProcess() { - - ProcessContext processContext = createProcessContext(); - if (this.defaultExecutionDirectory != null) { - processContext.directory(this.defaultExecutionDirectory); - } - return processContext; - } - - /** - * @return a new instance of {@link ProcessContext}. - * @see #newProcess() - */ - protected ProcessContext createProcessContext() { - - return new ProcessContextImpl(this); - } - - @Override - public IdeSubLogger level(IdeLogLevel level) { - - IdeSubLogger logger = this.loggers.get(level); - Objects.requireNonNull(logger); - return logger; - } - - @SuppressWarnings("unchecked") - @Override - public O question(String question, O... options) { - - assert (options.length >= 2); - interaction(question); - Map mapping = new HashMap<>(options.length); - int i = 0; - for (O option : options) { - i++; - String key = "" + option; - addMapping(mapping, key, option); - String numericKey = Integer.toString(i); - if (numericKey.equals(key)) { - trace("Options should not be numeric: " + key); - } else { - addMapping(mapping, numericKey, option); - } - interaction("Option " + numericKey + ": " + key); - } - O option = null; - if (isBatchMode()) { - if (isForceMode()) { - option = options[0]; - interaction("" + option); - } - } else { - while (option == null) { - String answer = readLine(); - option = mapping.get(answer); - if (option == null) { - warning("Invalid answer: '" + answer + "' - please try again."); - } - } - } - return option; - } - - /** - * @return the input from the end-user (e.g. read from the console). - */ - protected abstract String readLine(); - - private static void addMapping(Map mapping, String key, O option) { - - O duplicate = mapping.put(key, option); - if (duplicate != null) { - throw new IllegalArgumentException("Duplicated option " + key); - } - } - - /** - * Sets the log level. - * - * @param logLevel {@link IdeLogLevel} - */ - public void setLogLevel(IdeLogLevel logLevel) { - - for (IdeLogLevel level : IdeLogLevel.values()) { - IdeSubLogger logger; - if (level.ordinal() < logLevel.ordinal()) { - logger = new IdeSubLoggerNone(level); - } else { - logger = this.loggerFactory.apply(level); - } - this.loggers.put(level, logger); - } - } - - /** - * Finds the matching {@link Commandlet} to run, applies {@link CliArguments} to its - * {@link Commandlet#getProperties() properties} and will execute it. - * - * @param arguments the {@link CliArgument}. - * @return the return code of the execution. - */ - public int run(CliArguments arguments) { - - CliArgument current = arguments.current(); - if (!current.isEnd()) { - String keyword = current.get(); - Commandlet firstCandidate = this.commandletManager.getCommandletByFirstKeyword(keyword); - boolean matches; - if (firstCandidate != null) { - matches = applyAndRun(arguments.copy(), firstCandidate); - if (matches) { - return ProcessResult.SUCCESS; - } - } - for (Commandlet cmd : this.commandletManager.getCommandlets()) { - if (cmd != firstCandidate) { - matches = applyAndRun(arguments.copy(), cmd); - if (matches) { - return ProcessResult.SUCCESS; - } - } - } - error("Invalid arguments: {}", current.getArgs()); - } - this.commandletManager.getCommandlet(HelpCommandlet.class).run(); - return 1; - } - - /** - * @param cmd the potential {@link Commandlet} to - * {@link #apply(CliArguments, Commandlet, CompletionCandidateCollector) apply} and {@link Commandlet#run() run}. - * @return {@code true} if the given {@link Commandlet} matched and did {@link Commandlet#run() run} successfully, - * {@code false} otherwise (the {@link Commandlet} did not match and we have to try a different candidate). - */ - private boolean applyAndRun(CliArguments arguments, Commandlet cmd) { - - boolean matches = apply(arguments, cmd, null); - if (matches) { - matches = cmd.validate(); - } - if (matches) { - debug("Running commandlet {}", cmd); - if (cmd.isIdeHomeRequired() && (this.ideHome == null)) { - throw new CliException(getMessageIdeHomeNotFound()); - } - cmd.run(); - } else { - trace("Commandlet did not match"); - } - return matches; - } - - /** - * @param arguments the {@link CliArguments#ofCompletion(String...) completion arguments}. - * @param includeContextOptions to include the options of {@link ContextCommandlet}. - * @return the {@link List} of {@link CompletionCandidate}s to suggest. - */ - public List complete(CliArguments arguments, boolean includeContextOptions) { - - CompletionCandidateCollector collector = new CompletionCandidateCollectorDefault(this); - if (arguments.current().isStart()) { - arguments.next(); - } - if (includeContextOptions) { - ContextCommandlet cc = new ContextCommandlet(); - for (Property property : cc.getProperties()) { - assert (property.isOption()); - property.apply(arguments, this, cc, collector); - } - } - CliArgument current = arguments.current(); - if (!current.isEnd()) { - String keyword = current.get(); - Commandlet firstCandidate = this.commandletManager.getCommandletByFirstKeyword(keyword); - boolean matches = false; - if (firstCandidate != null) { - matches = apply(arguments.copy(), firstCandidate, collector); - } else if (current.isCombinedShortOption()) { - collector.add(keyword, null, null, null); - } - if (!matches) { - for (Commandlet cmd : this.commandletManager.getCommandlets()) { - if (cmd != firstCandidate) { - apply(arguments.copy(), cmd, collector); - } - } - } - } - return collector.getSortedCandidates(); - } - - /** - * @param arguments the {@link CliArguments} to apply. Will be {@link CliArguments#next() consumed} as they are - * matched. Consider passing a {@link CliArguments#copy() copy} as needed. - * @param cmd the potential {@link Commandlet} to match. - * @param collector the {@link CompletionCandidateCollector}. - * @return {@code true} if the given {@link Commandlet} matches to the given {@link CliArgument}(s) and those have - * been applied (set in the {@link Commandlet} and {@link Commandlet#validate() validated}), {@code false} otherwise - * (the {@link Commandlet} did not match and we have to try a different candidate). - */ - public boolean apply(CliArguments arguments, Commandlet cmd, CompletionCandidateCollector collector) { - - trace("Trying to match arguments to commandlet {}", cmd.getName()); - CliArgument currentArgument = arguments.current(); - Iterator> propertyIterator; - if (currentArgument.isCompletion()) { - propertyIterator = cmd.getProperties().iterator(); - } else { - propertyIterator = cmd.getValues().iterator(); - } - while (!currentArgument.isEnd()) { - trace("Trying to match argument '{}'", currentArgument); - Property property = null; - if (!arguments.isEndOptions()) { - property = cmd.getOption(currentArgument.getKey()); - } - if (property == null) { - if (!propertyIterator.hasNext()) { - trace("No option or next value found"); - return false; - } - property = propertyIterator.next(); - } - trace("Next property candidate to match argument is {}", property); - boolean matches = property.apply(arguments, this, cmd, collector); - if (!matches || currentArgument.isCompletion()) { - return false; - } - currentArgument = arguments.current(); - } - return true; - } - -} +package com.devonfw.tools.ide.context; + +import com.devonfw.tools.ide.cli.CliArgument; +import com.devonfw.tools.ide.cli.CliArguments; +import com.devonfw.tools.ide.cli.CliException; +import com.devonfw.tools.ide.commandlet.Commandlet; +import com.devonfw.tools.ide.commandlet.CommandletManager; +import com.devonfw.tools.ide.commandlet.CommandletManagerImpl; +import com.devonfw.tools.ide.commandlet.ContextCommandlet; +import com.devonfw.tools.ide.commandlet.HelpCommandlet; +import com.devonfw.tools.ide.common.SystemPath; +import com.devonfw.tools.ide.completion.CompletionCandidate; +import com.devonfw.tools.ide.completion.CompletionCandidateCollector; +import com.devonfw.tools.ide.completion.CompletionCandidateCollectorDefault; +import com.devonfw.tools.ide.environment.AbstractEnvironmentVariables; +import com.devonfw.tools.ide.environment.EnvironmentVariables; +import com.devonfw.tools.ide.environment.EnvironmentVariablesType; +import com.devonfw.tools.ide.io.FileAccess; +import com.devonfw.tools.ide.io.FileAccessImpl; +import com.devonfw.tools.ide.log.IdeLogLevel; +import com.devonfw.tools.ide.log.IdeSubLogger; +import com.devonfw.tools.ide.log.IdeSubLoggerNone; +import com.devonfw.tools.ide.merge.DirectoryMerger; +import com.devonfw.tools.ide.os.SystemInfo; +import com.devonfw.tools.ide.os.SystemInfoImpl; +import com.devonfw.tools.ide.process.ProcessContext; +import com.devonfw.tools.ide.process.ProcessContextImpl; +import com.devonfw.tools.ide.process.ProcessResult; +import com.devonfw.tools.ide.property.Property; +import com.devonfw.tools.ide.repo.CustomToolRepository; +import com.devonfw.tools.ide.repo.CustomToolRepositoryImpl; +import com.devonfw.tools.ide.repo.DefaultToolRepository; +import com.devonfw.tools.ide.repo.ToolRepository; +import com.devonfw.tools.ide.url.model.UrlMetadata; + +import java.io.IOException; +import java.net.InetAddress; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; + +/** + * Abstract base implementation of {@link IdeContext}. + */ +public abstract class AbstractIdeContext implements IdeContext { + + private static final String IDE_URLS_GIT = "https://github.com/devonfw/ide-urls.git"; + + private final Map loggers; + + private final Path ideHome; + + private final Path ideRoot; + + private final Path confPath; + + private final Path settingsPath; + + private final Path softwarePath; + + private final Path softwareRepositoryPath; + + private final Path pluginsPath; + + private final Path workspacePath; + + private final String workspaceName; + + private final Path urlsPath; + + private final Path tempPath; + + private final Path tempDownloadPath; + + private final Path cwd; + + private final Path downloadPath; + + private final Path toolRepository; + + private final Path userHome; + + private final Path userHomeIde; + + private final SystemPath path; + + private final SystemInfo systemInfo; + + private final EnvironmentVariables variables; + + private final FileAccess fileAccess; + + private final CommandletManager commandletManager; + + private final ToolRepository defaultToolRepository; + + private final CustomToolRepository customToolRepository; + + private final DirectoryMerger workspaceMerger; + + private final Function loggerFactory; + + private boolean offlineMode; + + private boolean forceMode; + + private boolean batchMode; + + private boolean quietMode; + + private Locale locale; + + private UrlMetadata urlMetadata; + + private Path defaultExecutionDirectory; + + /** + * The constructor. + * + * @param minLogLevel the minimum {@link IdeLogLevel} to enable. Should be {@link IdeLogLevel#INFO} by default. + * @param factory the {@link Function} to create {@link IdeSubLogger} per {@link IdeLogLevel}. + * @param userDir the optional {@link Path} to current working directory. + * @param toolRepository @param toolRepository the {@link ToolRepository} of the context. If it is set to {@code null} + * {@link DefaultToolRepository} will be used. + */ + public AbstractIdeContext(IdeLogLevel minLogLevel, Function factory, Path userDir, + ToolRepository toolRepository) { + + super(); + this.loggerFactory = factory; + this.loggers = new HashMap<>(); + setLogLevel(minLogLevel); + this.systemInfo = SystemInfoImpl.INSTANCE; + this.commandletManager = new CommandletManagerImpl(this); + this.fileAccess = new FileAccessImpl(this); + String workspace = WORKSPACE_MAIN; + if (userDir == null) { + this.cwd = Path.of(System.getProperty("user.dir")); + } else { + this.cwd = userDir.toAbsolutePath(); + } + // detect IDE_HOME and WORKSPACE + Path currentDir = this.cwd; + String name1 = ""; + String name2 = ""; + while (currentDir != null) { + trace("Looking for IDE_HOME in {}", currentDir); + if (isIdeHome(currentDir)) { + if (FOLDER_WORKSPACES.equals(name1)) { + workspace = name2; + } + break; + } + name2 = name1; + int nameCount = currentDir.getNameCount(); + if (nameCount >= 1) { + name1 = currentDir.getName(nameCount - 1).toString(); + } + currentDir = getParentPath(currentDir); + } + // detection completed, initializing variables + this.ideHome = currentDir; + this.workspaceName = workspace; + if (this.ideHome == null) { + info(getMessageIdeHomeNotFound()); + this.workspacePath = null; + this.ideRoot = null; + this.confPath = null; + this.settingsPath = null; + this.softwarePath = null; + this.pluginsPath = null; + } else { + debug(getMessageIdeHomeFound()); + this.workspacePath = this.ideHome.resolve(FOLDER_WORKSPACES).resolve(this.workspaceName); + Path ideRootPath = this.ideHome.getParent(); + String root = null; + if (!isTest()) { + root = System.getenv("IDE_ROOT"); + } + if (root != null) { + Path rootPath = Path.of(root); + if (Files.isDirectory(rootPath)) { + if (!ideRootPath.equals(rootPath)) { + warning( + "Variable IDE_ROOT is set to '{}' but for your project '{}' the path '{}' would have been expected.", + root, this.ideHome.getFileName(), ideRootPath); + } + ideRootPath = rootPath; + } else { + warning("Variable IDE_ROOT is not set to a valid directory '{}'." + root); + ideRootPath = null; + } + } + this.ideRoot = ideRootPath; + this.confPath = this.ideHome.resolve(FOLDER_CONF); + this.settingsPath = this.ideHome.resolve(FOLDER_SETTINGS); + this.softwarePath = this.ideHome.resolve(FOLDER_SOFTWARE); + this.pluginsPath = this.ideHome.resolve(FOLDER_PLUGINS); + } + if (this.ideRoot == null) { + this.toolRepository = null; + this.urlsPath = null; + this.tempPath = null; + this.tempDownloadPath = null; + this.softwareRepositoryPath = null; + } else { + Path ideBase = this.ideRoot.resolve(FOLDER_IDE); + this.toolRepository = ideBase.resolve("software"); + this.urlsPath = ideBase.resolve("urls"); + this.tempPath = ideBase.resolve("tmp"); + this.tempDownloadPath = this.tempPath.resolve(FOLDER_DOWNLOADS); + this.softwareRepositoryPath = ideBase.resolve(FOLDER_SOFTWARE); + if (Files.isDirectory(this.tempPath)) { + // TODO delete all files older than 1 day here... + } else { + this.fileAccess.mkdirs(this.tempDownloadPath); + } + } + if (isTest()) { + // only for testing... + if (this.ideHome == null) { + this.userHome = Path.of("/non-existing-user-home-for-testing"); + } else { + this.userHome = this.ideHome.resolve("home"); + } + } else { + this.userHome = Path.of(System.getProperty("user.home")); + } + this.userHomeIde = this.userHome.resolve(".ide"); + this.downloadPath = this.userHome.resolve("Downloads/ide"); + this.variables = createVariables(); + this.path = computeSystemPath(); + + if (toolRepository == null) { + this.defaultToolRepository = new DefaultToolRepository(this); + } else { + this.defaultToolRepository = toolRepository; + } + + this.customToolRepository = CustomToolRepositoryImpl.of(this); + this.workspaceMerger = new DirectoryMerger(this); + } + + private String getMessageIdeHomeFound() { + + return "IDE environment variables have been set for " + this.ideHome + " in workspace " + this.workspaceName; + } + + private String getMessageIdeHomeNotFound() { + + return "You are not inside an IDE installation: " + this.cwd; + } + + /** + * @return the status message about the {@link #getIdeHome() IDE_HOME} detection and environment variable + * initialization. + */ + public String getMessageIdeHome() { + + if (this.ideHome == null) { + return getMessageIdeHomeNotFound(); + } + return getMessageIdeHomeFound(); + } + + /** + * @return {@code true} if this is a test context for JUnits, {@code false} otherwise. + */ + public boolean isTest() { + + return isMock(); + } + + /** + * @return {@code true} if this is a mock context for JUnits, {@code false} otherwise. + */ + public boolean isMock() { + + return false; + } + + private SystemPath computeSystemPath() { + + return new SystemPath(this); + } + + private boolean isIdeHome(Path dir) { + + if (!Files.isDirectory(dir.resolve("workspaces"))) { + return false; + } else if (!Files.isDirectory(dir.resolve("settings"))) { + return false; + } + return true; + } + + private Path getParentPath(Path dir) { + + try { + Path linkDir = dir.toRealPath(); + if (!dir.equals(linkDir)) { + return linkDir; + } else { + return dir.getParent(); + } + } catch (IOException e) { + throw new IllegalStateException(e); + } + + } + + private EnvironmentVariables createVariables() { + + AbstractEnvironmentVariables system = EnvironmentVariables.ofSystem(this); + AbstractEnvironmentVariables user = extendVariables(system, this.userHomeIde, EnvironmentVariablesType.USER); + AbstractEnvironmentVariables settings = extendVariables(user, this.settingsPath, EnvironmentVariablesType.SETTINGS); + // TODO should we keep this workspace properties? Was this feature ever used? + AbstractEnvironmentVariables workspace = extendVariables(settings, this.workspacePath, + EnvironmentVariablesType.WORKSPACE); + AbstractEnvironmentVariables conf = extendVariables(workspace, this.confPath, EnvironmentVariablesType.CONF); + return conf.resolved(); + } + + private AbstractEnvironmentVariables extendVariables(AbstractEnvironmentVariables envVariables, Path propertiesPath, + EnvironmentVariablesType type) { + + Path propertiesFile = null; + if (propertiesPath == null) { + trace("Configuration directory for type {} does not exist.", type); + } else if (Files.isDirectory(propertiesPath)) { + propertiesFile = propertiesPath.resolve(EnvironmentVariables.DEFAULT_PROPERTIES); + boolean legacySupport = (type != EnvironmentVariablesType.USER); + if (legacySupport && !Files.exists(propertiesFile)) { + Path legacyFile = propertiesPath.resolve(EnvironmentVariables.LEGACY_PROPERTIES); + if (Files.exists(legacyFile)) { + propertiesFile = legacyFile; + } + } + } else { + debug("Configuration directory {} does not exist.", propertiesPath); + } + return envVariables.extend(propertiesFile, type); + } + + @Override + public SystemInfo getSystemInfo() { + + return this.systemInfo; + } + + @Override + public FileAccess getFileAccess() { + + return this.fileAccess; + } + + @Override + public CommandletManager getCommandletManager() { + + return this.commandletManager; + } + + @Override + public ToolRepository getDefaultToolRepository() { + + return this.defaultToolRepository; + } + + @Override + public CustomToolRepository getCustomToolRepository() { + + return this.customToolRepository; + } + + @Override + public Path getIdeHome() { + + return this.ideHome; + } + + @Override + public Path getIdeRoot() { + + return this.ideRoot; + } + + @Override + public Path getCwd() { + + return this.cwd; + } + + @Override + public Path getTempPath() { + + return this.tempPath; + } + + @Override + public Path getTempDownloadPath() { + + return this.tempDownloadPath; + } + + @Override + public Path getUserHome() { + + return this.userHome; + } + + @Override + public Path getUserHomeIde() { + + return this.userHomeIde; + } + + @Override + public Path getSettingsPath() { + + return this.settingsPath; + } + + @Override + public Path getConfPath() { + + return this.confPath; + } + + @Override + public Path getSoftwarePath() { + + return this.softwarePath; + } + + @Override + public Path getSoftwareRepositoryPath() { + + return this.softwareRepositoryPath; + } + + @Override + public Path getPluginsPath() { + + return this.pluginsPath; + } + + @Override + public String getWorkspaceName() { + + return this.workspaceName; + } + + @Override + public Path getWorkspacePath() { + + return this.workspacePath; + } + + @Override + public Path getDownloadPath() { + + return this.downloadPath; + } + + @Override + public Path getUrlsPath() { + + return this.urlsPath; + } + + @Override + public Path getToolRepositoryPath() { + + return this.toolRepository; + } + + @Override + public SystemPath getPath() { + + return this.path; + } + + @Override + public EnvironmentVariables getVariables() { + + return this.variables; + } + + @Override + public UrlMetadata getUrls() { + + if (this.urlMetadata == null) { + if (!isTest()) { + this.getGitContext().pullOrFetchAndResetIfNeeded(IDE_URLS_GIT, "master", this.urlsPath, "origin"); + } + this.urlMetadata = new UrlMetadata(this); + } + return this.urlMetadata; + } + + @Override + public boolean isQuietMode() { + + return this.quietMode; + } + + /** + * @param quietMode new value of {@link #isQuietMode()}. + */ + public void setQuietMode(boolean quietMode) { + + this.quietMode = quietMode; + } + + @Override + public boolean isBatchMode() { + + return this.batchMode; + } + + /** + * @param batchMode new value of {@link #isBatchMode()}. + */ + public void setBatchMode(boolean batchMode) { + + this.batchMode = batchMode; + } + + @Override + public boolean isForceMode() { + + return this.forceMode; + } + + /** + * @param forceMode new value of {@link #isForceMode()}. + */ + public void setForceMode(boolean forceMode) { + + this.forceMode = forceMode; + } + + @Override + public boolean isOfflineMode() { + + return this.offlineMode; + } + + /** + * @param offlineMode new value of {@link #isOfflineMode()}. + */ + public void setOfflineMode(boolean offlineMode) { + + this.offlineMode = offlineMode; + } + + @Override + public boolean isOnline() { + + boolean online = false; + try { + int timeout = 1000; + online = InetAddress.getByName("github.com").isReachable(timeout); + } catch (Exception ignored) { + + } + return online; + } + + @Override + public Locale getLocale() { + + if (this.locale == null) { + return Locale.getDefault(); + } + return this.locale; + } + + /** + * @param locale new value of {@link #getLocale()}. + */ + public void setLocale(Locale locale) { + + this.locale = locale; + } + + @Override + public DirectoryMerger getWorkspaceMerger() { + + return this.workspaceMerger; + } + + /** + * @return the {@link #defaultExecutionDirectory} the directory in which a command process is executed. + */ + public Path getDefaultExecutionDirectory() { + + return this.defaultExecutionDirectory; + } + + /** + * @param defaultExecutionDirectory new value of {@link #getDefaultExecutionDirectory()}. + */ + public void setDefaultExecutionDirectory(Path defaultExecutionDirectory) { + + if (defaultExecutionDirectory != null) { + this.defaultExecutionDirectory = defaultExecutionDirectory; + } + } + + @Override + public GitContext getGitContext() { + + return new GitContextImpl(this); + } + + @Override + public ProcessContext newProcess() { + + ProcessContext processContext = createProcessContext(); + if (this.defaultExecutionDirectory != null) { + processContext.directory(this.defaultExecutionDirectory); + } + return processContext; + } + + /** + * @return a new instance of {@link ProcessContext}. + * @see #newProcess() + */ + protected ProcessContext createProcessContext() { + + return new ProcessContextImpl(this); + } + + @Override + public IdeSubLogger level(IdeLogLevel level) { + + IdeSubLogger logger = this.loggers.get(level); + Objects.requireNonNull(logger); + return logger; + } + + @SuppressWarnings("unchecked") + @Override + public O question(String question, O... options) { + + assert (options.length >= 2); + interaction(question); + Map mapping = new HashMap<>(options.length); + int i = 0; + for (O option : options) { + i++; + String key = "" + option; + addMapping(mapping, key, option); + String numericKey = Integer.toString(i); + if (numericKey.equals(key)) { + trace("Options should not be numeric: " + key); + } else { + addMapping(mapping, numericKey, option); + } + interaction("Option " + numericKey + ": " + key); + } + O option = null; + if (isBatchMode()) { + if (isForceMode()) { + option = options[0]; + interaction("" + option); + } + } else { + while (option == null) { + String answer = readLine(); + option = mapping.get(answer); + if (option == null) { + warning("Invalid answer: '" + answer + "' - please try again."); + } + } + } + return option; + } + + /** + * @return the input from the end-user (e.g. read from the console). + */ + protected abstract String readLine(); + + private static void addMapping(Map mapping, String key, O option) { + + O duplicate = mapping.put(key, option); + if (duplicate != null) { + throw new IllegalArgumentException("Duplicated option " + key); + } + } + + /** + * Sets the log level. + * + * @param logLevel {@link IdeLogLevel} + */ + public void setLogLevel(IdeLogLevel logLevel) { + + for (IdeLogLevel level : IdeLogLevel.values()) { + IdeSubLogger logger; + if (level.ordinal() < logLevel.ordinal()) { + logger = new IdeSubLoggerNone(level); + } else { + logger = this.loggerFactory.apply(level); + } + this.loggers.put(level, logger); + } + } + + /** + * Finds the matching {@link Commandlet} to run, applies {@link CliArguments} to its + * {@link Commandlet#getProperties() properties} and will execute it. + * + * @param arguments the {@link CliArgument}. + * @return the return code of the execution. + */ + public int run(CliArguments arguments) { + + CliArgument current = arguments.current(); + if (!current.isEnd()) { + String keyword = current.get(); + Commandlet firstCandidate = this.commandletManager.getCommandletByFirstKeyword(keyword); + boolean matches; + if (firstCandidate != null) { + matches = applyAndRun(arguments.copy(), firstCandidate); + if (matches) { + return ProcessResult.SUCCESS; + } + } + for (Commandlet cmd : this.commandletManager.getCommandlets()) { + if (cmd != firstCandidate) { + matches = applyAndRun(arguments.copy(), cmd); + if (matches) { + return ProcessResult.SUCCESS; + } + } + } + error("Invalid arguments: {}", current.getArgs()); + } + this.commandletManager.getCommandlet(HelpCommandlet.class).run(); + return 1; + } + + /** + * @param cmd the potential {@link Commandlet} to + * {@link #apply(CliArguments, Commandlet, CompletionCandidateCollector) apply} and {@link Commandlet#run() run}. + * @return {@code true} if the given {@link Commandlet} matched and did {@link Commandlet#run() run} successfully, + * {@code false} otherwise (the {@link Commandlet} did not match and we have to try a different candidate). + */ + private boolean applyAndRun(CliArguments arguments, Commandlet cmd) { + + boolean matches = apply(arguments, cmd, null); + if (matches) { + matches = cmd.validate(); + } + if (matches) { + debug("Running commandlet {}", cmd); + if (cmd.isIdeHomeRequired() && (this.ideHome == null)) { + throw new CliException(getMessageIdeHomeNotFound()); + } + cmd.run(); + } else { + trace("Commandlet did not match"); + } + return matches; + } + + /** + * @param arguments the {@link CliArguments#ofCompletion(String...) completion arguments}. + * @param includeContextOptions to include the options of {@link ContextCommandlet}. + * @return the {@link List} of {@link CompletionCandidate}s to suggest. + */ + public List complete(CliArguments arguments, boolean includeContextOptions) { + + CompletionCandidateCollector collector = new CompletionCandidateCollectorDefault(this); + if (arguments.current().isStart()) { + arguments.next(); + } + if (includeContextOptions) { + ContextCommandlet cc = new ContextCommandlet(); + for (Property property : cc.getProperties()) { + assert (property.isOption()); + property.apply(arguments, this, cc, collector); + } + } + CliArgument current = arguments.current(); + if (!current.isEnd()) { + String keyword = current.get(); + Commandlet firstCandidate = this.commandletManager.getCommandletByFirstKeyword(keyword); + boolean matches = false; + if (firstCandidate != null) { + matches = apply(arguments.copy(), firstCandidate, collector); + } else if (current.isCombinedShortOption()) { + collector.add(keyword, null, null, null); + } + if (!matches) { + for (Commandlet cmd : this.commandletManager.getCommandlets()) { + if (cmd != firstCandidate) { + apply(arguments.copy(), cmd, collector); + } + } + } + } + return collector.getSortedCandidates(); + } + + /** + * @param arguments the {@link CliArguments} to apply. Will be {@link CliArguments#next() consumed} as they are + * matched. Consider passing a {@link CliArguments#copy() copy} as needed. + * @param cmd the potential {@link Commandlet} to match. + * @param collector the {@link CompletionCandidateCollector}. + * @return {@code true} if the given {@link Commandlet} matches to the given {@link CliArgument}(s) and those have + * been applied (set in the {@link Commandlet} and {@link Commandlet#validate() validated}), {@code false} otherwise + * (the {@link Commandlet} did not match and we have to try a different candidate). + */ + public boolean apply(CliArguments arguments, Commandlet cmd, CompletionCandidateCollector collector) { + + trace("Trying to match arguments to commandlet {}", cmd.getName()); + CliArgument currentArgument = arguments.current(); + Iterator> propertyIterator; + if (currentArgument.isCompletion()) { + propertyIterator = cmd.getProperties().iterator(); + } else { + propertyIterator = cmd.getValues().iterator(); + } + while (!currentArgument.isEnd()) { + trace("Trying to match argument '{}'", currentArgument); + Property property = null; + if (!arguments.isEndOptions()) { + property = cmd.getOption(currentArgument.getKey()); + } + if (property == null) { + if (!propertyIterator.hasNext()) { + trace("No option or next value found"); + return false; + } + property = propertyIterator.next(); + } + trace("Next property candidate to match argument is {}", property); + boolean matches = property.apply(arguments, this, cmd, collector); + if (!matches || currentArgument.isCompletion()) { + return false; + } + currentArgument = arguments.current(); + } + return true; + } + +} From b7e76d07ed6ac3b57aa129779e614035559dd36b Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Wed, 13 Mar 2024 22:41:29 +0100 Subject: [PATCH 86/93] Update AbstractIdeContext.java --- .../tools/ide/context/AbstractIdeContext.java | 1716 ++++++++--------- 1 file changed, 858 insertions(+), 858 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java b/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java index 9d60ccfa1..30c995813 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java +++ b/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java @@ -1,858 +1,858 @@ -package com.devonfw.tools.ide.context; - -import com.devonfw.tools.ide.cli.CliArgument; -import com.devonfw.tools.ide.cli.CliArguments; -import com.devonfw.tools.ide.cli.CliException; -import com.devonfw.tools.ide.commandlet.Commandlet; -import com.devonfw.tools.ide.commandlet.CommandletManager; -import com.devonfw.tools.ide.commandlet.CommandletManagerImpl; -import com.devonfw.tools.ide.commandlet.ContextCommandlet; -import com.devonfw.tools.ide.commandlet.HelpCommandlet; -import com.devonfw.tools.ide.common.SystemPath; -import com.devonfw.tools.ide.completion.CompletionCandidate; -import com.devonfw.tools.ide.completion.CompletionCandidateCollector; -import com.devonfw.tools.ide.completion.CompletionCandidateCollectorDefault; -import com.devonfw.tools.ide.environment.AbstractEnvironmentVariables; -import com.devonfw.tools.ide.environment.EnvironmentVariables; -import com.devonfw.tools.ide.environment.EnvironmentVariablesType; -import com.devonfw.tools.ide.io.FileAccess; -import com.devonfw.tools.ide.io.FileAccessImpl; -import com.devonfw.tools.ide.log.IdeLogLevel; -import com.devonfw.tools.ide.log.IdeSubLogger; -import com.devonfw.tools.ide.log.IdeSubLoggerNone; -import com.devonfw.tools.ide.merge.DirectoryMerger; -import com.devonfw.tools.ide.os.SystemInfo; -import com.devonfw.tools.ide.os.SystemInfoImpl; -import com.devonfw.tools.ide.process.ProcessContext; -import com.devonfw.tools.ide.process.ProcessContextImpl; -import com.devonfw.tools.ide.process.ProcessResult; -import com.devonfw.tools.ide.property.Property; -import com.devonfw.tools.ide.repo.CustomToolRepository; -import com.devonfw.tools.ide.repo.CustomToolRepositoryImpl; -import com.devonfw.tools.ide.repo.DefaultToolRepository; -import com.devonfw.tools.ide.repo.ToolRepository; -import com.devonfw.tools.ide.url.model.UrlMetadata; - -import java.io.IOException; -import java.net.InetAddress; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Objects; -import java.util.function.Function; - -/** - * Abstract base implementation of {@link IdeContext}. - */ -public abstract class AbstractIdeContext implements IdeContext { - - private static final String IDE_URLS_GIT = "https://github.com/devonfw/ide-urls.git"; - - private final Map loggers; - - private final Path ideHome; - - private final Path ideRoot; - - private final Path confPath; - - private final Path settingsPath; - - private final Path softwarePath; - - private final Path softwareRepositoryPath; - - private final Path pluginsPath; - - private final Path workspacePath; - - private final String workspaceName; - - private final Path urlsPath; - - private final Path tempPath; - - private final Path tempDownloadPath; - - private final Path cwd; - - private final Path downloadPath; - - private final Path toolRepository; - - private final Path userHome; - - private final Path userHomeIde; - - private final SystemPath path; - - private final SystemInfo systemInfo; - - private final EnvironmentVariables variables; - - private final FileAccess fileAccess; - - private final CommandletManager commandletManager; - - private final ToolRepository defaultToolRepository; - - private final CustomToolRepository customToolRepository; - - private final DirectoryMerger workspaceMerger; - - private final Function loggerFactory; - - private boolean offlineMode; - - private boolean forceMode; - - private boolean batchMode; - - private boolean quietMode; - - private Locale locale; - - private UrlMetadata urlMetadata; - - private Path defaultExecutionDirectory; - - /** - * The constructor. - * - * @param minLogLevel the minimum {@link IdeLogLevel} to enable. Should be {@link IdeLogLevel#INFO} by default. - * @param factory the {@link Function} to create {@link IdeSubLogger} per {@link IdeLogLevel}. - * @param userDir the optional {@link Path} to current working directory. - * @param toolRepository @param toolRepository the {@link ToolRepository} of the context. If it is set to {@code null} - * {@link DefaultToolRepository} will be used. - */ - public AbstractIdeContext(IdeLogLevel minLogLevel, Function factory, Path userDir, - ToolRepository toolRepository) { - - super(); - this.loggerFactory = factory; - this.loggers = new HashMap<>(); - setLogLevel(minLogLevel); - this.systemInfo = SystemInfoImpl.INSTANCE; - this.commandletManager = new CommandletManagerImpl(this); - this.fileAccess = new FileAccessImpl(this); - String workspace = WORKSPACE_MAIN; - if (userDir == null) { - this.cwd = Path.of(System.getProperty("user.dir")); - } else { - this.cwd = userDir.toAbsolutePath(); - } - // detect IDE_HOME and WORKSPACE - Path currentDir = this.cwd; - String name1 = ""; - String name2 = ""; - while (currentDir != null) { - trace("Looking for IDE_HOME in {}", currentDir); - if (isIdeHome(currentDir)) { - if (FOLDER_WORKSPACES.equals(name1)) { - workspace = name2; - } - break; - } - name2 = name1; - int nameCount = currentDir.getNameCount(); - if (nameCount >= 1) { - name1 = currentDir.getName(nameCount - 1).toString(); - } - currentDir = getParentPath(currentDir); - } - // detection completed, initializing variables - this.ideHome = currentDir; - this.workspaceName = workspace; - if (this.ideHome == null) { - info(getMessageIdeHomeNotFound()); - this.workspacePath = null; - this.ideRoot = null; - this.confPath = null; - this.settingsPath = null; - this.softwarePath = null; - this.pluginsPath = null; - } else { - debug(getMessageIdeHomeFound()); - this.workspacePath = this.ideHome.resolve(FOLDER_WORKSPACES).resolve(this.workspaceName); - Path ideRootPath = this.ideHome.getParent(); - String root = null; - if (!isTest()) { - root = System.getenv("IDE_ROOT"); - } - if (root != null) { - Path rootPath = Path.of(root); - if (Files.isDirectory(rootPath)) { - if (!ideRootPath.equals(rootPath)) { - warning( - "Variable IDE_ROOT is set to '{}' but for your project '{}' the path '{}' would have been expected.", - root, this.ideHome.getFileName(), ideRootPath); - } - ideRootPath = rootPath; - } else { - warning("Variable IDE_ROOT is not set to a valid directory '{}'." + root); - ideRootPath = null; - } - } - this.ideRoot = ideRootPath; - this.confPath = this.ideHome.resolve(FOLDER_CONF); - this.settingsPath = this.ideHome.resolve(FOLDER_SETTINGS); - this.softwarePath = this.ideHome.resolve(FOLDER_SOFTWARE); - this.pluginsPath = this.ideHome.resolve(FOLDER_PLUGINS); - } - if (this.ideRoot == null) { - this.toolRepository = null; - this.urlsPath = null; - this.tempPath = null; - this.tempDownloadPath = null; - this.softwareRepositoryPath = null; - } else { - Path ideBase = this.ideRoot.resolve(FOLDER_IDE); - this.toolRepository = ideBase.resolve("software"); - this.urlsPath = ideBase.resolve("urls"); - this.tempPath = ideBase.resolve("tmp"); - this.tempDownloadPath = this.tempPath.resolve(FOLDER_DOWNLOADS); - this.softwareRepositoryPath = ideBase.resolve(FOLDER_SOFTWARE); - if (Files.isDirectory(this.tempPath)) { - // TODO delete all files older than 1 day here... - } else { - this.fileAccess.mkdirs(this.tempDownloadPath); - } - } - if (isTest()) { - // only for testing... - if (this.ideHome == null) { - this.userHome = Path.of("/non-existing-user-home-for-testing"); - } else { - this.userHome = this.ideHome.resolve("home"); - } - } else { - this.userHome = Path.of(System.getProperty("user.home")); - } - this.userHomeIde = this.userHome.resolve(".ide"); - this.downloadPath = this.userHome.resolve("Downloads/ide"); - this.variables = createVariables(); - this.path = computeSystemPath(); - - if (toolRepository == null) { - this.defaultToolRepository = new DefaultToolRepository(this); - } else { - this.defaultToolRepository = toolRepository; - } - - this.customToolRepository = CustomToolRepositoryImpl.of(this); - this.workspaceMerger = new DirectoryMerger(this); - } - - private String getMessageIdeHomeFound() { - - return "IDE environment variables have been set for " + this.ideHome + " in workspace " + this.workspaceName; - } - - private String getMessageIdeHomeNotFound() { - - return "You are not inside an IDE installation: " + this.cwd; - } - - /** - * @return the status message about the {@link #getIdeHome() IDE_HOME} detection and environment variable - * initialization. - */ - public String getMessageIdeHome() { - - if (this.ideHome == null) { - return getMessageIdeHomeNotFound(); - } - return getMessageIdeHomeFound(); - } - - /** - * @return {@code true} if this is a test context for JUnits, {@code false} otherwise. - */ - public boolean isTest() { - - return isMock(); - } - - /** - * @return {@code true} if this is a mock context for JUnits, {@code false} otherwise. - */ - public boolean isMock() { - - return false; - } - - private SystemPath computeSystemPath() { - - return new SystemPath(this); - } - - private boolean isIdeHome(Path dir) { - - if (!Files.isDirectory(dir.resolve("workspaces"))) { - return false; - } else if (!Files.isDirectory(dir.resolve("settings"))) { - return false; - } - return true; - } - - private Path getParentPath(Path dir) { - - try { - Path linkDir = dir.toRealPath(); - if (!dir.equals(linkDir)) { - return linkDir; - } else { - return dir.getParent(); - } - } catch (IOException e) { - throw new IllegalStateException(e); - } - - } - - private EnvironmentVariables createVariables() { - - AbstractEnvironmentVariables system = EnvironmentVariables.ofSystem(this); - AbstractEnvironmentVariables user = extendVariables(system, this.userHomeIde, EnvironmentVariablesType.USER); - AbstractEnvironmentVariables settings = extendVariables(user, this.settingsPath, EnvironmentVariablesType.SETTINGS); - // TODO should we keep this workspace properties? Was this feature ever used? - AbstractEnvironmentVariables workspace = extendVariables(settings, this.workspacePath, - EnvironmentVariablesType.WORKSPACE); - AbstractEnvironmentVariables conf = extendVariables(workspace, this.confPath, EnvironmentVariablesType.CONF); - return conf.resolved(); - } - - private AbstractEnvironmentVariables extendVariables(AbstractEnvironmentVariables envVariables, Path propertiesPath, - EnvironmentVariablesType type) { - - Path propertiesFile = null; - if (propertiesPath == null) { - trace("Configuration directory for type {} does not exist.", type); - } else if (Files.isDirectory(propertiesPath)) { - propertiesFile = propertiesPath.resolve(EnvironmentVariables.DEFAULT_PROPERTIES); - boolean legacySupport = (type != EnvironmentVariablesType.USER); - if (legacySupport && !Files.exists(propertiesFile)) { - Path legacyFile = propertiesPath.resolve(EnvironmentVariables.LEGACY_PROPERTIES); - if (Files.exists(legacyFile)) { - propertiesFile = legacyFile; - } - } - } else { - debug("Configuration directory {} does not exist.", propertiesPath); - } - return envVariables.extend(propertiesFile, type); - } - - @Override - public SystemInfo getSystemInfo() { - - return this.systemInfo; - } - - @Override - public FileAccess getFileAccess() { - - return this.fileAccess; - } - - @Override - public CommandletManager getCommandletManager() { - - return this.commandletManager; - } - - @Override - public ToolRepository getDefaultToolRepository() { - - return this.defaultToolRepository; - } - - @Override - public CustomToolRepository getCustomToolRepository() { - - return this.customToolRepository; - } - - @Override - public Path getIdeHome() { - - return this.ideHome; - } - - @Override - public Path getIdeRoot() { - - return this.ideRoot; - } - - @Override - public Path getCwd() { - - return this.cwd; - } - - @Override - public Path getTempPath() { - - return this.tempPath; - } - - @Override - public Path getTempDownloadPath() { - - return this.tempDownloadPath; - } - - @Override - public Path getUserHome() { - - return this.userHome; - } - - @Override - public Path getUserHomeIde() { - - return this.userHomeIde; - } - - @Override - public Path getSettingsPath() { - - return this.settingsPath; - } - - @Override - public Path getConfPath() { - - return this.confPath; - } - - @Override - public Path getSoftwarePath() { - - return this.softwarePath; - } - - @Override - public Path getSoftwareRepositoryPath() { - - return this.softwareRepositoryPath; - } - - @Override - public Path getPluginsPath() { - - return this.pluginsPath; - } - - @Override - public String getWorkspaceName() { - - return this.workspaceName; - } - - @Override - public Path getWorkspacePath() { - - return this.workspacePath; - } - - @Override - public Path getDownloadPath() { - - return this.downloadPath; - } - - @Override - public Path getUrlsPath() { - - return this.urlsPath; - } - - @Override - public Path getToolRepositoryPath() { - - return this.toolRepository; - } - - @Override - public SystemPath getPath() { - - return this.path; - } - - @Override - public EnvironmentVariables getVariables() { - - return this.variables; - } - - @Override - public UrlMetadata getUrls() { - - if (this.urlMetadata == null) { - if (!isTest()) { - this.getGitContext().pullOrFetchAndResetIfNeeded(IDE_URLS_GIT, "master", this.urlsPath, "origin"); - } - this.urlMetadata = new UrlMetadata(this); - } - return this.urlMetadata; - } - - @Override - public boolean isQuietMode() { - - return this.quietMode; - } - - /** - * @param quietMode new value of {@link #isQuietMode()}. - */ - public void setQuietMode(boolean quietMode) { - - this.quietMode = quietMode; - } - - @Override - public boolean isBatchMode() { - - return this.batchMode; - } - - /** - * @param batchMode new value of {@link #isBatchMode()}. - */ - public void setBatchMode(boolean batchMode) { - - this.batchMode = batchMode; - } - - @Override - public boolean isForceMode() { - - return this.forceMode; - } - - /** - * @param forceMode new value of {@link #isForceMode()}. - */ - public void setForceMode(boolean forceMode) { - - this.forceMode = forceMode; - } - - @Override - public boolean isOfflineMode() { - - return this.offlineMode; - } - - /** - * @param offlineMode new value of {@link #isOfflineMode()}. - */ - public void setOfflineMode(boolean offlineMode) { - - this.offlineMode = offlineMode; - } - - @Override - public boolean isOnline() { - - boolean online = false; - try { - int timeout = 1000; - online = InetAddress.getByName("github.com").isReachable(timeout); - } catch (Exception ignored) { - - } - return online; - } - - @Override - public Locale getLocale() { - - if (this.locale == null) { - return Locale.getDefault(); - } - return this.locale; - } - - /** - * @param locale new value of {@link #getLocale()}. - */ - public void setLocale(Locale locale) { - - this.locale = locale; - } - - @Override - public DirectoryMerger getWorkspaceMerger() { - - return this.workspaceMerger; - } - - /** - * @return the {@link #defaultExecutionDirectory} the directory in which a command process is executed. - */ - public Path getDefaultExecutionDirectory() { - - return this.defaultExecutionDirectory; - } - - /** - * @param defaultExecutionDirectory new value of {@link #getDefaultExecutionDirectory()}. - */ - public void setDefaultExecutionDirectory(Path defaultExecutionDirectory) { - - if (defaultExecutionDirectory != null) { - this.defaultExecutionDirectory = defaultExecutionDirectory; - } - } - - @Override - public GitContext getGitContext() { - - return new GitContextImpl(this); - } - - @Override - public ProcessContext newProcess() { - - ProcessContext processContext = createProcessContext(); - if (this.defaultExecutionDirectory != null) { - processContext.directory(this.defaultExecutionDirectory); - } - return processContext; - } - - /** - * @return a new instance of {@link ProcessContext}. - * @see #newProcess() - */ - protected ProcessContext createProcessContext() { - - return new ProcessContextImpl(this); - } - - @Override - public IdeSubLogger level(IdeLogLevel level) { - - IdeSubLogger logger = this.loggers.get(level); - Objects.requireNonNull(logger); - return logger; - } - - @SuppressWarnings("unchecked") - @Override - public O question(String question, O... options) { - - assert (options.length >= 2); - interaction(question); - Map mapping = new HashMap<>(options.length); - int i = 0; - for (O option : options) { - i++; - String key = "" + option; - addMapping(mapping, key, option); - String numericKey = Integer.toString(i); - if (numericKey.equals(key)) { - trace("Options should not be numeric: " + key); - } else { - addMapping(mapping, numericKey, option); - } - interaction("Option " + numericKey + ": " + key); - } - O option = null; - if (isBatchMode()) { - if (isForceMode()) { - option = options[0]; - interaction("" + option); - } - } else { - while (option == null) { - String answer = readLine(); - option = mapping.get(answer); - if (option == null) { - warning("Invalid answer: '" + answer + "' - please try again."); - } - } - } - return option; - } - - /** - * @return the input from the end-user (e.g. read from the console). - */ - protected abstract String readLine(); - - private static void addMapping(Map mapping, String key, O option) { - - O duplicate = mapping.put(key, option); - if (duplicate != null) { - throw new IllegalArgumentException("Duplicated option " + key); - } - } - - /** - * Sets the log level. - * - * @param logLevel {@link IdeLogLevel} - */ - public void setLogLevel(IdeLogLevel logLevel) { - - for (IdeLogLevel level : IdeLogLevel.values()) { - IdeSubLogger logger; - if (level.ordinal() < logLevel.ordinal()) { - logger = new IdeSubLoggerNone(level); - } else { - logger = this.loggerFactory.apply(level); - } - this.loggers.put(level, logger); - } - } - - /** - * Finds the matching {@link Commandlet} to run, applies {@link CliArguments} to its - * {@link Commandlet#getProperties() properties} and will execute it. - * - * @param arguments the {@link CliArgument}. - * @return the return code of the execution. - */ - public int run(CliArguments arguments) { - - CliArgument current = arguments.current(); - if (!current.isEnd()) { - String keyword = current.get(); - Commandlet firstCandidate = this.commandletManager.getCommandletByFirstKeyword(keyword); - boolean matches; - if (firstCandidate != null) { - matches = applyAndRun(arguments.copy(), firstCandidate); - if (matches) { - return ProcessResult.SUCCESS; - } - } - for (Commandlet cmd : this.commandletManager.getCommandlets()) { - if (cmd != firstCandidate) { - matches = applyAndRun(arguments.copy(), cmd); - if (matches) { - return ProcessResult.SUCCESS; - } - } - } - error("Invalid arguments: {}", current.getArgs()); - } - this.commandletManager.getCommandlet(HelpCommandlet.class).run(); - return 1; - } - - /** - * @param cmd the potential {@link Commandlet} to - * {@link #apply(CliArguments, Commandlet, CompletionCandidateCollector) apply} and {@link Commandlet#run() run}. - * @return {@code true} if the given {@link Commandlet} matched and did {@link Commandlet#run() run} successfully, - * {@code false} otherwise (the {@link Commandlet} did not match and we have to try a different candidate). - */ - private boolean applyAndRun(CliArguments arguments, Commandlet cmd) { - - boolean matches = apply(arguments, cmd, null); - if (matches) { - matches = cmd.validate(); - } - if (matches) { - debug("Running commandlet {}", cmd); - if (cmd.isIdeHomeRequired() && (this.ideHome == null)) { - throw new CliException(getMessageIdeHomeNotFound()); - } - cmd.run(); - } else { - trace("Commandlet did not match"); - } - return matches; - } - - /** - * @param arguments the {@link CliArguments#ofCompletion(String...) completion arguments}. - * @param includeContextOptions to include the options of {@link ContextCommandlet}. - * @return the {@link List} of {@link CompletionCandidate}s to suggest. - */ - public List complete(CliArguments arguments, boolean includeContextOptions) { - - CompletionCandidateCollector collector = new CompletionCandidateCollectorDefault(this); - if (arguments.current().isStart()) { - arguments.next(); - } - if (includeContextOptions) { - ContextCommandlet cc = new ContextCommandlet(); - for (Property property : cc.getProperties()) { - assert (property.isOption()); - property.apply(arguments, this, cc, collector); - } - } - CliArgument current = arguments.current(); - if (!current.isEnd()) { - String keyword = current.get(); - Commandlet firstCandidate = this.commandletManager.getCommandletByFirstKeyword(keyword); - boolean matches = false; - if (firstCandidate != null) { - matches = apply(arguments.copy(), firstCandidate, collector); - } else if (current.isCombinedShortOption()) { - collector.add(keyword, null, null, null); - } - if (!matches) { - for (Commandlet cmd : this.commandletManager.getCommandlets()) { - if (cmd != firstCandidate) { - apply(arguments.copy(), cmd, collector); - } - } - } - } - return collector.getSortedCandidates(); - } - - /** - * @param arguments the {@link CliArguments} to apply. Will be {@link CliArguments#next() consumed} as they are - * matched. Consider passing a {@link CliArguments#copy() copy} as needed. - * @param cmd the potential {@link Commandlet} to match. - * @param collector the {@link CompletionCandidateCollector}. - * @return {@code true} if the given {@link Commandlet} matches to the given {@link CliArgument}(s) and those have - * been applied (set in the {@link Commandlet} and {@link Commandlet#validate() validated}), {@code false} otherwise - * (the {@link Commandlet} did not match and we have to try a different candidate). - */ - public boolean apply(CliArguments arguments, Commandlet cmd, CompletionCandidateCollector collector) { - - trace("Trying to match arguments to commandlet {}", cmd.getName()); - CliArgument currentArgument = arguments.current(); - Iterator> propertyIterator; - if (currentArgument.isCompletion()) { - propertyIterator = cmd.getProperties().iterator(); - } else { - propertyIterator = cmd.getValues().iterator(); - } - while (!currentArgument.isEnd()) { - trace("Trying to match argument '{}'", currentArgument); - Property property = null; - if (!arguments.isEndOptions()) { - property = cmd.getOption(currentArgument.getKey()); - } - if (property == null) { - if (!propertyIterator.hasNext()) { - trace("No option or next value found"); - return false; - } - property = propertyIterator.next(); - } - trace("Next property candidate to match argument is {}", property); - boolean matches = property.apply(arguments, this, cmd, collector); - if (!matches || currentArgument.isCompletion()) { - return false; - } - currentArgument = arguments.current(); - } - return true; - } - -} +package com.devonfw.tools.ide.context; + +import com.devonfw.tools.ide.cli.CliArgument; +import com.devonfw.tools.ide.cli.CliArguments; +import com.devonfw.tools.ide.cli.CliException; +import com.devonfw.tools.ide.commandlet.Commandlet; +import com.devonfw.tools.ide.commandlet.CommandletManager; +import com.devonfw.tools.ide.commandlet.CommandletManagerImpl; +import com.devonfw.tools.ide.commandlet.ContextCommandlet; +import com.devonfw.tools.ide.commandlet.HelpCommandlet; +import com.devonfw.tools.ide.common.SystemPath; +import com.devonfw.tools.ide.completion.CompletionCandidate; +import com.devonfw.tools.ide.completion.CompletionCandidateCollector; +import com.devonfw.tools.ide.completion.CompletionCandidateCollectorDefault; +import com.devonfw.tools.ide.environment.AbstractEnvironmentVariables; +import com.devonfw.tools.ide.environment.EnvironmentVariables; +import com.devonfw.tools.ide.environment.EnvironmentVariablesType; +import com.devonfw.tools.ide.io.FileAccess; +import com.devonfw.tools.ide.io.FileAccessImpl; +import com.devonfw.tools.ide.log.IdeLogLevel; +import com.devonfw.tools.ide.log.IdeSubLogger; +import com.devonfw.tools.ide.log.IdeSubLoggerNone; +import com.devonfw.tools.ide.merge.DirectoryMerger; +import com.devonfw.tools.ide.os.SystemInfo; +import com.devonfw.tools.ide.os.SystemInfoImpl; +import com.devonfw.tools.ide.process.ProcessContext; +import com.devonfw.tools.ide.process.ProcessContextImpl; +import com.devonfw.tools.ide.process.ProcessResult; +import com.devonfw.tools.ide.property.Property; +import com.devonfw.tools.ide.repo.CustomToolRepository; +import com.devonfw.tools.ide.repo.CustomToolRepositoryImpl; +import com.devonfw.tools.ide.repo.DefaultToolRepository; +import com.devonfw.tools.ide.repo.ToolRepository; +import com.devonfw.tools.ide.url.model.UrlMetadata; + +import java.io.IOException; +import java.net.InetAddress; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; + +/** + * Abstract base implementation of {@link IdeContext}. + */ +public abstract class AbstractIdeContext implements IdeContext { + + private static final String IDE_URLS_GIT = "https://github.com/devonfw/ide-urls.git"; + + private final Map loggers; + + private final Path ideHome; + + private final Path ideRoot; + + private final Path confPath; + + private final Path settingsPath; + + private final Path softwarePath; + + private final Path softwareRepositoryPath; + + private final Path pluginsPath; + + private final Path workspacePath; + + private final String workspaceName; + + private final Path urlsPath; + + private final Path tempPath; + + private final Path tempDownloadPath; + + private final Path cwd; + + private final Path downloadPath; + + private final Path toolRepository; + + private final Path userHome; + + private final Path userHomeIde; + + private final SystemPath path; + + private final SystemInfo systemInfo; + + private final EnvironmentVariables variables; + + private final FileAccess fileAccess; + + private final CommandletManager commandletManager; + + private final ToolRepository defaultToolRepository; + + private final CustomToolRepository customToolRepository; + + private final DirectoryMerger workspaceMerger; + + private final Function loggerFactory; + + private boolean offlineMode; + + private boolean forceMode; + + private boolean batchMode; + + private boolean quietMode; + + private Locale locale; + + private UrlMetadata urlMetadata; + + private Path defaultExecutionDirectory; + + /** + * The constructor. + * + * @param minLogLevel the minimum {@link IdeLogLevel} to enable. Should be {@link IdeLogLevel#INFO} by default. + * @param factory the {@link Function} to create {@link IdeSubLogger} per {@link IdeLogLevel}. + * @param userDir the optional {@link Path} to current working directory. + * @param toolRepository @param toolRepository the {@link ToolRepository} of the context. If it is set to {@code null} + * {@link DefaultToolRepository} will be used. + */ + public AbstractIdeContext(IdeLogLevel minLogLevel, Function factory, Path userDir, + ToolRepository toolRepository) { + + super(); + this.loggerFactory = factory; + this.loggers = new HashMap<>(); + setLogLevel(minLogLevel); + this.systemInfo = SystemInfoImpl.INSTANCE; + this.commandletManager = new CommandletManagerImpl(this); + this.fileAccess = new FileAccessImpl(this); + String workspace = WORKSPACE_MAIN; + if (userDir == null) { + this.cwd = Path.of(System.getProperty("user.dir")); + } else { + this.cwd = userDir.toAbsolutePath(); + } + // detect IDE_HOME and WORKSPACE + Path currentDir = this.cwd; + String name1 = ""; + String name2 = ""; + while (currentDir != null) { + trace("Looking for IDE_HOME in {}", currentDir); + if (isIdeHome(currentDir)) { + if (FOLDER_WORKSPACES.equals(name1)) { + workspace = name2; + } + break; + } + name2 = name1; + int nameCount = currentDir.getNameCount(); + if (nameCount >= 1) { + name1 = currentDir.getName(nameCount - 1).toString(); + } + currentDir = getParentPath(currentDir); + } + // detection completed, initializing variables + this.ideHome = currentDir; + this.workspaceName = workspace; + if (this.ideHome == null) { + info(getMessageIdeHomeNotFound()); + this.workspacePath = null; + this.ideRoot = null; + this.confPath = null; + this.settingsPath = null; + this.softwarePath = null; + this.pluginsPath = null; + } else { + debug(getMessageIdeHomeFound()); + this.workspacePath = this.ideHome.resolve(FOLDER_WORKSPACES).resolve(this.workspaceName); + Path ideRootPath = this.ideHome.getParent(); + String root = null; + if (!isTest()) { + root = System.getenv("IDE_ROOT"); + } + if (root != null) { + Path rootPath = Path.of(root); + if (Files.isDirectory(rootPath)) { + if (!ideRootPath.equals(rootPath)) { + warning( + "Variable IDE_ROOT is set to '{}' but for your project '{}' the path '{}' would have been expected.", + root, this.ideHome.getFileName(), ideRootPath); + } + ideRootPath = rootPath; + } else { + warning("Variable IDE_ROOT is not set to a valid directory '{}'." + root); + ideRootPath = null; + } + } + this.ideRoot = ideRootPath; + this.confPath = this.ideHome.resolve(FOLDER_CONF); + this.settingsPath = this.ideHome.resolve(FOLDER_SETTINGS); + this.softwarePath = this.ideHome.resolve(FOLDER_SOFTWARE); + this.pluginsPath = this.ideHome.resolve(FOLDER_PLUGINS); + } + if (this.ideRoot == null) { + this.toolRepository = null; + this.urlsPath = null; + this.tempPath = null; + this.tempDownloadPath = null; + this.softwareRepositoryPath = null; + } else { + Path ideBase = this.ideRoot.resolve(FOLDER_IDE); + this.toolRepository = ideBase.resolve("software"); + this.urlsPath = ideBase.resolve("urls"); + this.tempPath = ideBase.resolve("tmp"); + this.tempDownloadPath = this.tempPath.resolve(FOLDER_DOWNLOADS); + this.softwareRepositoryPath = ideBase.resolve(FOLDER_SOFTWARE); + if (Files.isDirectory(this.tempPath)) { + // TODO delete all files older than 1 day here... + } else { + this.fileAccess.mkdirs(this.tempDownloadPath); + } + } + if (isTest()) { + // only for testing... + if (this.ideHome == null) { + this.userHome = Path.of("/non-existing-user-home-for-testing"); + } else { + this.userHome = this.ideHome.resolve("home"); + } + } else { + this.userHome = Path.of(System.getProperty("user.home")); + } + this.userHomeIde = this.userHome.resolve(".ide"); + this.downloadPath = this.userHome.resolve("Downloads/ide"); + this.variables = createVariables(); + this.path = computeSystemPath(); + + if (toolRepository == null) { + this.defaultToolRepository = new DefaultToolRepository(this); + } else { + this.defaultToolRepository = toolRepository; + } + + this.customToolRepository = CustomToolRepositoryImpl.of(this); + this.workspaceMerger = new DirectoryMerger(this); + } + + private String getMessageIdeHomeFound() { + + return "IDE environment variables have been set for " + this.ideHome + " in workspace " + this.workspaceName; + } + + private String getMessageIdeHomeNotFound() { + + return "You are not inside an IDE installation: " + this.cwd; + } + + /** + * @return the status message about the {@link #getIdeHome() IDE_HOME} detection and environment variable + * initialization. + */ + public String getMessageIdeHome() { + + if (this.ideHome == null) { + return getMessageIdeHomeNotFound(); + } + return getMessageIdeHomeFound(); + } + + /** + * @return {@code true} if this is a test context for JUnits, {@code false} otherwise. + */ + public boolean isTest() { + + return isMock(); + } + + /** + * @return {@code true} if this is a mock context for JUnits, {@code false} otherwise. + */ + public boolean isMock() { + + return false; + } + + private SystemPath computeSystemPath() { + + return new SystemPath(this); + } + + private boolean isIdeHome(Path dir) { + + if (!Files.isDirectory(dir.resolve("workspaces"))) { + return false; + } else if (!Files.isDirectory(dir.resolve("settings"))) { + return false; + } + return true; + } + + private Path getParentPath(Path dir) { + + try { + Path linkDir = dir.toRealPath(); + if (!dir.equals(linkDir)) { + return linkDir; + } else { + return dir.getParent(); + } + } catch (IOException e) { + throw new IllegalStateException(e); + } + + } + + private EnvironmentVariables createVariables() { + + AbstractEnvironmentVariables system = EnvironmentVariables.ofSystem(this); + AbstractEnvironmentVariables user = extendVariables(system, this.userHomeIde, EnvironmentVariablesType.USER); + AbstractEnvironmentVariables settings = extendVariables(user, this.settingsPath, EnvironmentVariablesType.SETTINGS); + // TODO should we keep this workspace properties? Was this feature ever used? + AbstractEnvironmentVariables workspace = extendVariables(settings, this.workspacePath, + EnvironmentVariablesType.WORKSPACE); + AbstractEnvironmentVariables conf = extendVariables(workspace, this.confPath, EnvironmentVariablesType.CONF); + return conf.resolved(); + } + + private AbstractEnvironmentVariables extendVariables(AbstractEnvironmentVariables envVariables, Path propertiesPath, + EnvironmentVariablesType type) { + + Path propertiesFile = null; + if (propertiesPath == null) { + trace("Configuration directory for type {} does not exist.", type); + } else if (Files.isDirectory(propertiesPath)) { + propertiesFile = propertiesPath.resolve(EnvironmentVariables.DEFAULT_PROPERTIES); + boolean legacySupport = (type != EnvironmentVariablesType.USER); + if (legacySupport && !Files.exists(propertiesFile)) { + Path legacyFile = propertiesPath.resolve(EnvironmentVariables.LEGACY_PROPERTIES); + if (Files.exists(legacyFile)) { + propertiesFile = legacyFile; + } + } + } else { + debug("Configuration directory {} does not exist.", propertiesPath); + } + return envVariables.extend(propertiesFile, type); + } + + @Override + public SystemInfo getSystemInfo() { + + return this.systemInfo; + } + + @Override + public FileAccess getFileAccess() { + + return this.fileAccess; + } + + @Override + public CommandletManager getCommandletManager() { + + return this.commandletManager; + } + + @Override + public ToolRepository getDefaultToolRepository() { + + return this.defaultToolRepository; + } + + @Override + public CustomToolRepository getCustomToolRepository() { + + return this.customToolRepository; + } + + @Override + public Path getIdeHome() { + + return this.ideHome; + } + + @Override + public Path getIdeRoot() { + + return this.ideRoot; + } + + @Override + public Path getCwd() { + + return this.cwd; + } + + @Override + public Path getTempPath() { + + return this.tempPath; + } + + @Override + public Path getTempDownloadPath() { + + return this.tempDownloadPath; + } + + @Override + public Path getUserHome() { + + return this.userHome; + } + + @Override + public Path getUserHomeIde() { + + return this.userHomeIde; + } + + @Override + public Path getSettingsPath() { + + return this.settingsPath; + } + + @Override + public Path getConfPath() { + + return this.confPath; + } + + @Override + public Path getSoftwarePath() { + + return this.softwarePath; + } + + @Override + public Path getSoftwareRepositoryPath() { + + return this.softwareRepositoryPath; + } + + @Override + public Path getPluginsPath() { + + return this.pluginsPath; + } + + @Override + public String getWorkspaceName() { + + return this.workspaceName; + } + + @Override + public Path getWorkspacePath() { + + return this.workspacePath; + } + + @Override + public Path getDownloadPath() { + + return this.downloadPath; + } + + @Override + public Path getUrlsPath() { + + return this.urlsPath; + } + + @Override + public Path getToolRepositoryPath() { + + return this.toolRepository; + } + + @Override + public SystemPath getPath() { + + return this.path; + } + + @Override + public EnvironmentVariables getVariables() { + + return this.variables; + } + + @Override + public UrlMetadata getUrls() { + + if (this.urlMetadata == null) { + if (!isTest()) { + this.getGitContext().pullOrFetchAndResetIfNeeded(IDE_URLS_GIT, "master", this.urlsPath, "origin"); + } + this.urlMetadata = new UrlMetadata(this); + } + return this.urlMetadata; + } + + @Override + public boolean isQuietMode() { + + return this.quietMode; + } + + /** + * @param quietMode new value of {@link #isQuietMode()}. + */ + public void setQuietMode(boolean quietMode) { + + this.quietMode = quietMode; + } + + @Override + public boolean isBatchMode() { + + return this.batchMode; + } + + /** + * @param batchMode new value of {@link #isBatchMode()}. + */ + public void setBatchMode(boolean batchMode) { + + this.batchMode = batchMode; + } + + @Override + public boolean isForceMode() { + + return this.forceMode; + } + + /** + * @param forceMode new value of {@link #isForceMode()}. + */ + public void setForceMode(boolean forceMode) { + + this.forceMode = forceMode; + } + + @Override + public boolean isOfflineMode() { + + return this.offlineMode; + } + + /** + * @param offlineMode new value of {@link #isOfflineMode()}. + */ + public void setOfflineMode(boolean offlineMode) { + + this.offlineMode = offlineMode; + } + + @Override + public boolean isOnline() { + + boolean online = false; + try { + int timeout = 1000; + online = InetAddress.getByName("github.com").isReachable(timeout); + } catch (Exception ignored) { + + } + return online; + } + + @Override + public Locale getLocale() { + + if (this.locale == null) { + return Locale.getDefault(); + } + return this.locale; + } + + /** + * @param locale new value of {@link #getLocale()}. + */ + public void setLocale(Locale locale) { + + this.locale = locale; + } + + @Override + public DirectoryMerger getWorkspaceMerger() { + + return this.workspaceMerger; + } + + /** + * @return the {@link #defaultExecutionDirectory} the directory in which a command process is executed. + */ + public Path getDefaultExecutionDirectory() { + + return this.defaultExecutionDirectory; + } + + /** + * @param defaultExecutionDirectory new value of {@link #getDefaultExecutionDirectory()}. + */ + public void setDefaultExecutionDirectory(Path defaultExecutionDirectory) { + + if (defaultExecutionDirectory != null) { + this.defaultExecutionDirectory = defaultExecutionDirectory; + } + } + + @Override + public GitContext getGitContext() { + + return new GitContextImpl(this); + } + + @Override + public ProcessContext newProcess() { + + ProcessContext processContext = createProcessContext(); + if (this.defaultExecutionDirectory != null) { + processContext.directory(this.defaultExecutionDirectory); + } + return processContext; + } + + /** + * @return a new instance of {@link ProcessContext}. + * @see #newProcess() + */ + protected ProcessContext createProcessContext() { + + return new ProcessContextImpl(this); + } + + @Override + public IdeSubLogger level(IdeLogLevel level) { + + IdeSubLogger logger = this.loggers.get(level); + Objects.requireNonNull(logger); + return logger; + } + + @SuppressWarnings("unchecked") + @Override + public O question(String question, O... options) { + + assert (options.length >= 2); + interaction(question); + Map mapping = new HashMap<>(options.length); + int i = 0; + for (O option : options) { + i++; + String key = "" + option; + addMapping(mapping, key, option); + String numericKey = Integer.toString(i); + if (numericKey.equals(key)) { + trace("Options should not be numeric: " + key); + } else { + addMapping(mapping, numericKey, option); + } + interaction("Option " + numericKey + ": " + key); + } + O option = null; + if (isBatchMode()) { + if (isForceMode()) { + option = options[0]; + interaction("" + option); + } + } else { + while (option == null) { + String answer = readLine(); + option = mapping.get(answer); + if (option == null) { + warning("Invalid answer: '" + answer + "' - please try again."); + } + } + } + return option; + } + + /** + * @return the input from the end-user (e.g. read from the console). + */ + protected abstract String readLine(); + + private static void addMapping(Map mapping, String key, O option) { + + O duplicate = mapping.put(key, option); + if (duplicate != null) { + throw new IllegalArgumentException("Duplicated option " + key); + } + } + + /** + * Sets the log level. + * + * @param logLevel {@link IdeLogLevel} + */ + public void setLogLevel(IdeLogLevel logLevel) { + + for (IdeLogLevel level : IdeLogLevel.values()) { + IdeSubLogger logger; + if (level.ordinal() < logLevel.ordinal()) { + logger = new IdeSubLoggerNone(level); + } else { + logger = this.loggerFactory.apply(level); + } + this.loggers.put(level, logger); + } + } + + /** + * Finds the matching {@link Commandlet} to run, applies {@link CliArguments} to its + * {@link Commandlet#getProperties() properties} and will execute it. + * + * @param arguments the {@link CliArgument}. + * @return the return code of the execution. + */ + public int run(CliArguments arguments) { + + CliArgument current = arguments.current(); + if (!current.isEnd()) { + String keyword = current.get(); + Commandlet firstCandidate = this.commandletManager.getCommandletByFirstKeyword(keyword); + boolean matches; + if (firstCandidate != null) { + matches = applyAndRun(arguments.copy(), firstCandidate); + if (matches) { + return ProcessResult.SUCCESS; + } + } + for (Commandlet cmd : this.commandletManager.getCommandlets()) { + if (cmd != firstCandidate) { + matches = applyAndRun(arguments.copy(), cmd); + if (matches) { + return ProcessResult.SUCCESS; + } + } + } + error("Invalid arguments: {}", current.getArgs()); + } + this.commandletManager.getCommandlet(HelpCommandlet.class).run(); + return 1; + } + + /** + * @param cmd the potential {@link Commandlet} to + * {@link #apply(CliArguments, Commandlet, CompletionCandidateCollector) apply} and {@link Commandlet#run() run}. + * @return {@code true} if the given {@link Commandlet} matched and did {@link Commandlet#run() run} successfully, + * {@code false} otherwise (the {@link Commandlet} did not match and we have to try a different candidate). + */ + private boolean applyAndRun(CliArguments arguments, Commandlet cmd) { + + boolean matches = apply(arguments, cmd, null); + if (matches) { + matches = cmd.validate(); + } + if (matches) { + debug("Running commandlet {}", cmd); + if (cmd.isIdeHomeRequired() && (this.ideHome == null)) { + throw new CliException(getMessageIdeHomeNotFound()); + } + cmd.run(); + } else { + trace("Commandlet did not match"); + } + return matches; + } + + /** + * @param arguments the {@link CliArguments#ofCompletion(String...) completion arguments}. + * @param includeContextOptions to include the options of {@link ContextCommandlet}. + * @return the {@link List} of {@link CompletionCandidate}s to suggest. + */ + public List complete(CliArguments arguments, boolean includeContextOptions) { + + CompletionCandidateCollector collector = new CompletionCandidateCollectorDefault(this); + if (arguments.current().isStart()) { + arguments.next(); + } + if (includeContextOptions) { + ContextCommandlet cc = new ContextCommandlet(); + for (Property property : cc.getProperties()) { + assert (property.isOption()); + property.apply(arguments, this, cc, collector); + } + } + CliArgument current = arguments.current(); + if (!current.isEnd()) { + String keyword = current.get(); + Commandlet firstCandidate = this.commandletManager.getCommandletByFirstKeyword(keyword); + boolean matches = false; + if (firstCandidate != null) { + matches = apply(arguments.copy(), firstCandidate, collector); + } else if (current.isCombinedShortOption()) { + collector.add(keyword, null, null, null); + } + if (!matches) { + for (Commandlet cmd : this.commandletManager.getCommandlets()) { + if (cmd != firstCandidate) { + apply(arguments.copy(), cmd, collector); + } + } + } + } + return collector.getSortedCandidates(); + } + + /** + * @param arguments the {@link CliArguments} to apply. Will be {@link CliArguments#next() consumed} as they are + * matched. Consider passing a {@link CliArguments#copy() copy} as needed. + * @param cmd the potential {@link Commandlet} to match. + * @param collector the {@link CompletionCandidateCollector}. + * @return {@code true} if the given {@link Commandlet} matches to the given {@link CliArgument}(s) and those have + * been applied (set in the {@link Commandlet} and {@link Commandlet#validate() validated}), {@code false} otherwise + * (the {@link Commandlet} did not match and we have to try a different candidate). + */ + public boolean apply(CliArguments arguments, Commandlet cmd, CompletionCandidateCollector collector) { + + trace("Trying to match arguments to commandlet {}", cmd.getName()); + CliArgument currentArgument = arguments.current(); + Iterator> propertyIterator; + if (currentArgument.isCompletion()) { + propertyIterator = cmd.getProperties().iterator(); + } else { + propertyIterator = cmd.getValues().iterator(); + } + while (!currentArgument.isEnd()) { + trace("Trying to match argument '{}'", currentArgument); + Property property = null; + if (!arguments.isEndOptions()) { + property = cmd.getOption(currentArgument.getKey()); + } + if (property == null) { + if (!propertyIterator.hasNext()) { + trace("No option or next value found"); + return false; + } + property = propertyIterator.next(); + } + trace("Next property candidate to match argument is {}", property); + boolean matches = property.apply(arguments, this, cmd, collector); + if (!matches || currentArgument.isCompletion()) { + return false; + } + currentArgument = arguments.current(); + } + return true; + } + +} From 6765d89dbb4ca9056814048f51858839ce5a5bf8 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Wed, 13 Mar 2024 22:42:36 +0100 Subject: [PATCH 87/93] Update GitContextImpl.java --- .../tools/ide/context/GitContextImpl.java | 574 +++++++++--------- 1 file changed, 287 insertions(+), 287 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/context/GitContextImpl.java b/cli/src/main/java/com/devonfw/tools/ide/context/GitContextImpl.java index 9813bdd5e..91022a728 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/context/GitContextImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/context/GitContextImpl.java @@ -1,287 +1,287 @@ -package com.devonfw.tools.ide.context; - -import java.io.IOException; -import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.attribute.FileTime; -import java.time.Duration; -import java.util.AbstractMap; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; - -import com.devonfw.tools.ide.cli.CliException; -import com.devonfw.tools.ide.log.IdeLogLevel; -import com.devonfw.tools.ide.log.IdeSubLogger; -import com.devonfw.tools.ide.process.ProcessContext; -import com.devonfw.tools.ide.process.ProcessErrorHandling; -import com.devonfw.tools.ide.process.ProcessMode; -import com.devonfw.tools.ide.process.ProcessResult; - -/** - * Implements the {@link GitContext}. - */ -public class GitContextImpl implements GitContext { - private static final Duration GIT_PULL_CACHE_DELAY_MILLIS = Duration.ofMillis(30 * 60 * 1000); - - ; - - private final IdeContext context; - - private ProcessContext processContext; - - /** - * @param context the {@link IdeContext context}. - */ - public GitContextImpl(IdeContext context) { - - this.context = context; - - } - - @Override - public void pullOrCloneIfNeeded(String repoUrl, String branch, Path targetRepository) { - - Path gitDirectory = targetRepository.resolve(".git"); - - // Check if the .git directory exists - if (Files.isDirectory(gitDirectory)) { - Path magicFilePath = gitDirectory.resolve("HEAD"); - long currentTime = System.currentTimeMillis(); - // Get the modification time of the magic file - long fileMTime; - try { - fileMTime = Files.getLastModifiedTime(magicFilePath).toMillis(); - } catch (IOException e) { - throw new IllegalStateException("Could not read " + magicFilePath, e); - } - - // Check if the file modification time is older than the delta threshold - if ((currentTime - fileMTime > GIT_PULL_CACHE_DELAY_MILLIS.toMillis()) || context.isForceMode()) { - pullOrClone(repoUrl, "", targetRepository); - try { - Files.setLastModifiedTime(magicFilePath, FileTime.fromMillis(currentTime)); - } catch (IOException e) { - throw new IllegalStateException("Could not read or write in " + magicFilePath, e); - } - } - } else { - // If the .git directory does not exist, perform git clone - pullOrClone(repoUrl, branch, targetRepository); - } - } - - public void pullOrFetchAndResetIfNeeded(String repoUrl, String branch, Path targetRepository, String remoteName) { - - pullOrCloneIfNeeded(repoUrl, branch, targetRepository); - - if (remoteName.isEmpty()) { - reset(targetRepository, "origin", "master"); - } else { - reset(targetRepository, remoteName, "master"); - } - - cleanup(targetRepository); - } - - @Override - public void pullOrClone(String gitRepoUrl, String branch, Path targetRepository) { - - Objects.requireNonNull(targetRepository); - Objects.requireNonNull(gitRepoUrl); - - if (!gitRepoUrl.startsWith("http")) { - throw new IllegalArgumentException("Invalid git URL '" + gitRepoUrl + "'!"); - } - - initializeProcessContext(targetRepository); - if (Files.isDirectory(targetRepository.resolve(".git"))) { - // checks for remotes - ProcessResult result = this.processContext.addArg("remote").run(ProcessMode.DEFAULT_CAPTURE); - List remotes = result.getOut(); - if (remotes.isEmpty()) { - String message = targetRepository - + " is a local git repository with no remote - if you did this for testing, you may continue...\n" - + "Do you want to ignore the problem and continue anyhow?"; - this.context.askToContinue(message); - } else { - this.processContext.errorHandling(ProcessErrorHandling.WARNING); - - if (!this.context.isOffline()) { - pull(targetRepository); - } - } - } else { - clone(new GitUrl(gitRepoUrl, branch), targetRepository); - } - } - - /** - * Handles errors which occurred during 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. - * @param result the {@link ProcessResult} to evaluate. - */ - private void handleErrors(Path targetRepository, ProcessResult result) { - - if (!result.isSuccessful()) { - String message = "Failed to update git repository at " + targetRepository; - if (this.context.isOffline()) { - this.context.warning(message); - this.context.interaction("Continuing as we are in offline mode - results may be outdated!"); - } else { - this.context.error(message); - if (this.context.isOnline()) { - this.context.error( - "See above error for details. If you have local changes, please stash or revert and retry."); - } else { - this.context.error( - "It seems you are offline - please ensure Internet connectivity and retry or activate offline mode (-o or --offline)."); - } - this.context.askToContinue("Typically you should abort and fix the problem. Do you want to continue anyways?"); - } - } - } - - /** - * Lazily initializes the {@link ProcessContext}. - * - * @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. - */ - private void initializeProcessContext(Path targetRepository) { - - if (this.processContext == null) { - this.processContext = this.context.newProcess().directory(targetRepository).executable("git") - .withEnvVar("GIT_TERMINAL_PROMPT", "0"); - } - } - - @Override - public void clone(GitUrl gitRepoUrl, Path targetRepository) { - - URL parsedUrl = gitRepoUrl.parseUrl(); - initializeProcessContext(targetRepository); - ProcessResult result; - if (!this.context.isOffline()) { - this.context.getFileAccess().mkdirs(targetRepository); - this.context.requireOnline("git clone of " + parsedUrl); - this.processContext.addArg("clone"); - if (this.context.isQuietMode()) { - this.processContext.addArg("-q"); - } - this.processContext.addArgs("--recursive", gitRepoUrl.url(), "--config", "core.autocrlf=false", "."); - result = this.processContext.run(ProcessMode.DEFAULT_CAPTURE); - if (!result.isSuccessful()) { - this.context.warning("Git failed to clone {} into {}.", parsedUrl, targetRepository); - } - String branch = gitRepoUrl.branch(); - if (branch != null) { - this.processContext.addArgs("checkout", branch); - result = this.processContext.run(ProcessMode.DEFAULT_CAPTURE); - if (!result.isSuccessful()) { - this.context.warning("Git failed to checkout to branch {}", branch); - } - } - } else { - throw new CliException("Could not clone " + parsedUrl + " to " + targetRepository + " because you are offline."); - } - } - - @Override - public void pull(Path targetRepository) { - - initializeProcessContext(targetRepository); - ProcessResult result; - // pull from remote - result = this.processContext.addArg("--no-pager").addArg("pull").run(ProcessMode.DEFAULT_CAPTURE); - - if (!result.isSuccessful()) { - Map remoteAndBranchName = retrieveRemoteAndBranchName(); - context.warning("Git pull for {}/{} failed for repository {}.", remoteAndBranchName.get("remote"), - remoteAndBranchName.get("branch"), targetRepository); - handleErrors(targetRepository, result); - } - } - - private Map retrieveRemoteAndBranchName() { - - Map remoteAndBranchName = new HashMap<>(); - ProcessResult remoteResult = this.processContext.addArg("branch").addArg("-vv").run(ProcessMode.DEFAULT_CAPTURE); - List remotes = remoteResult.getOut(); - if (!remotes.isEmpty()) { - for (String remote : remotes) { - if (remote.startsWith("*")) { - String checkedOutBranch = remote.substring(remote.indexOf("[") + 1, remote.indexOf("]")); - remoteAndBranchName.put("remote", checkedOutBranch.substring(0, checkedOutBranch.indexOf("/"))); - // check if current repo is behind remote and omit message - if (checkedOutBranch.contains(":")) { - remoteAndBranchName.put("branch", - checkedOutBranch.substring(checkedOutBranch.indexOf("/") + 1, checkedOutBranch.indexOf(":"))); - } else { - remoteAndBranchName.put("branch", checkedOutBranch.substring(checkedOutBranch.indexOf("/") + 1)); - } - - } - } - } else { - return Map.ofEntries(new AbstractMap.SimpleEntry<>("remote", "unknown"), - new AbstractMap.SimpleEntry<>("branch", "unknown")); - } - - return remoteAndBranchName; - } - - @Override - public void reset(Path targetRepository, String remoteName, String branchName) { - - initializeProcessContext(targetRepository); - ProcessResult result; - // check for changed files - result = this.processContext.addArg("diff-index").addArg("--quiet").addArg("HEAD").run(ProcessMode.DEFAULT_CAPTURE); - - if (!result.isSuccessful()) { - // reset to origin/master - context.warning("Git has detected modified files -- attempting to reset {} to '{}/{}'.", targetRepository, - remoteName, branchName); - result = this.processContext.addArg("reset").addArg("--hard").addArg(remoteName + "/" + branchName) - .run(ProcessMode.DEFAULT_CAPTURE); - - if (!result.isSuccessful()) { - context.warning("Git failed to reset {} to '{}/{}'.", remoteName, branchName, targetRepository); - handleErrors(targetRepository, result); - } - } - } - - @Override - public void cleanup(Path targetRepository) { - - initializeProcessContext(targetRepository); - ProcessResult result; - // check for untracked files - result = this.processContext.addArg("ls-files").addArg("--other").addArg("--directory").addArg("--exclude-standard") - .run(ProcessMode.DEFAULT_CAPTURE); - - if (!result.getOut().isEmpty()) { - // delete untracked files - context.warning("Git detected untracked files in {} and is attempting a cleanup.", targetRepository); - result = this.processContext.addArg("clean").addArg("-df").run(ProcessMode.DEFAULT_CAPTURE); - - if (!result.isSuccessful()) { - context.warning("Git failed to clean the repository {}.", targetRepository); - } - } - } - - @Override - public IdeSubLogger level(IdeLogLevel level) { - - return null; - } -} +package com.devonfw.tools.ide.context; + +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.FileTime; +import java.time.Duration; +import java.util.AbstractMap; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import com.devonfw.tools.ide.cli.CliException; +import com.devonfw.tools.ide.log.IdeLogLevel; +import com.devonfw.tools.ide.log.IdeSubLogger; +import com.devonfw.tools.ide.process.ProcessContext; +import com.devonfw.tools.ide.process.ProcessErrorHandling; +import com.devonfw.tools.ide.process.ProcessMode; +import com.devonfw.tools.ide.process.ProcessResult; + +/** + * Implements the {@link GitContext}. + */ +public class GitContextImpl implements GitContext { + private static final Duration GIT_PULL_CACHE_DELAY_MILLIS = Duration.ofMillis(30 * 60 * 1000); + + ; + + private final IdeContext context; + + private ProcessContext processContext; + + /** + * @param context the {@link IdeContext context}. + */ + public GitContextImpl(IdeContext context) { + + this.context = context; + + } + + @Override + public void pullOrCloneIfNeeded(String repoUrl, String branch, Path targetRepository) { + + Path gitDirectory = targetRepository.resolve(".git"); + + // Check if the .git directory exists + if (Files.isDirectory(gitDirectory)) { + Path magicFilePath = gitDirectory.resolve("HEAD"); + long currentTime = System.currentTimeMillis(); + // Get the modification time of the magic file + long fileMTime; + try { + fileMTime = Files.getLastModifiedTime(magicFilePath).toMillis(); + } catch (IOException e) { + throw new IllegalStateException("Could not read " + magicFilePath, e); + } + + // Check if the file modification time is older than the delta threshold + if ((currentTime - fileMTime > GIT_PULL_CACHE_DELAY_MILLIS.toMillis()) || context.isForceMode()) { + pullOrClone(repoUrl, "", targetRepository); + try { + Files.setLastModifiedTime(magicFilePath, FileTime.fromMillis(currentTime)); + } catch (IOException e) { + throw new IllegalStateException("Could not read or write in " + magicFilePath, e); + } + } + } else { + // If the .git directory does not exist, perform git clone + pullOrClone(repoUrl, branch, targetRepository); + } + } + + public void pullOrFetchAndResetIfNeeded(String repoUrl, String branch, Path targetRepository, String remoteName) { + + pullOrCloneIfNeeded(repoUrl, branch, targetRepository); + + if (remoteName.isEmpty()) { + reset(targetRepository, "origin", "master"); + } else { + reset(targetRepository, remoteName, "master"); + } + + cleanup(targetRepository); + } + + @Override + public void pullOrClone(String gitRepoUrl, String branch, Path targetRepository) { + + Objects.requireNonNull(targetRepository); + Objects.requireNonNull(gitRepoUrl); + + if (!gitRepoUrl.startsWith("http")) { + throw new IllegalArgumentException("Invalid git URL '" + gitRepoUrl + "'!"); + } + + initializeProcessContext(targetRepository); + if (Files.isDirectory(targetRepository.resolve(".git"))) { + // checks for remotes + ProcessResult result = this.processContext.addArg("remote").run(ProcessMode.DEFAULT_CAPTURE); + List remotes = result.getOut(); + if (remotes.isEmpty()) { + String message = targetRepository + + " is a local git repository with no remote - if you did this for testing, you may continue...\n" + + "Do you want to ignore the problem and continue anyhow?"; + this.context.askToContinue(message); + } else { + this.processContext.errorHandling(ProcessErrorHandling.WARNING); + + if (!this.context.isOffline()) { + pull(targetRepository); + } + } + } else { + clone(new GitUrl(gitRepoUrl, branch), targetRepository); + } + } + + /** + * Handles errors which occurred during 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. + * @param result the {@link ProcessResult} to evaluate. + */ + private void handleErrors(Path targetRepository, ProcessResult result) { + + if (!result.isSuccessful()) { + String message = "Failed to update git repository at " + targetRepository; + if (this.context.isOffline()) { + this.context.warning(message); + this.context.interaction("Continuing as we are in offline mode - results may be outdated!"); + } else { + this.context.error(message); + if (this.context.isOnline()) { + this.context.error( + "See above error for details. If you have local changes, please stash or revert and retry."); + } else { + this.context.error( + "It seems you are offline - please ensure Internet connectivity and retry or activate offline mode (-o or --offline)."); + } + this.context.askToContinue("Typically you should abort and fix the problem. Do you want to continue anyways?"); + } + } + } + + /** + * Lazily initializes the {@link ProcessContext}. + * + * @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. + */ + private void initializeProcessContext(Path targetRepository) { + + if (this.processContext == null) { + this.processContext = this.context.newProcess().directory(targetRepository).executable("git") + .withEnvVar("GIT_TERMINAL_PROMPT", "0"); + } + } + + @Override + public void clone(GitUrl gitRepoUrl, Path targetRepository) { + + URL parsedUrl = gitRepoUrl.parseUrl(); + initializeProcessContext(targetRepository); + ProcessResult result; + if (!this.context.isOffline()) { + this.context.getFileAccess().mkdirs(targetRepository); + this.context.requireOnline("git clone of " + parsedUrl); + this.processContext.addArg("clone"); + if (this.context.isQuietMode()) { + this.processContext.addArg("-q"); + } + this.processContext.addArgs("--recursive", gitRepoUrl.url(), "--config", "core.autocrlf=false", "."); + result = this.processContext.run(ProcessMode.DEFAULT_CAPTURE); + if (!result.isSuccessful()) { + this.context.warning("Git failed to clone {} into {}.", parsedUrl, targetRepository); + } + String branch = gitRepoUrl.branch(); + if (branch != null) { + this.processContext.addArgs("checkout", branch); + result = this.processContext.run(ProcessMode.DEFAULT_CAPTURE); + if (!result.isSuccessful()) { + this.context.warning("Git failed to checkout to branch {}", branch); + } + } + } else { + throw new CliException("Could not clone " + parsedUrl + " to " + targetRepository + " because you are offline."); + } + } + + @Override + public void pull(Path targetRepository) { + + initializeProcessContext(targetRepository); + ProcessResult result; + // pull from remote + result = this.processContext.addArg("--no-pager").addArg("pull").run(ProcessMode.DEFAULT_CAPTURE); + + if (!result.isSuccessful()) { + Map remoteAndBranchName = retrieveRemoteAndBranchName(); + context.warning("Git pull for {}/{} failed for repository {}.", remoteAndBranchName.get("remote"), + remoteAndBranchName.get("branch"), targetRepository); + handleErrors(targetRepository, result); + } + } + + private Map retrieveRemoteAndBranchName() { + + Map remoteAndBranchName = new HashMap<>(); + ProcessResult remoteResult = this.processContext.addArg("branch").addArg("-vv").run(ProcessMode.DEFAULT_CAPTURE); + List remotes = remoteResult.getOut(); + if (!remotes.isEmpty()) { + for (String remote : remotes) { + if (remote.startsWith("*")) { + String checkedOutBranch = remote.substring(remote.indexOf("[") + 1, remote.indexOf("]")); + remoteAndBranchName.put("remote", checkedOutBranch.substring(0, checkedOutBranch.indexOf("/"))); + // check if current repo is behind remote and omit message + if (checkedOutBranch.contains(":")) { + remoteAndBranchName.put("branch", + checkedOutBranch.substring(checkedOutBranch.indexOf("/") + 1, checkedOutBranch.indexOf(":"))); + } else { + remoteAndBranchName.put("branch", checkedOutBranch.substring(checkedOutBranch.indexOf("/") + 1)); + } + + } + } + } else { + return Map.ofEntries(new AbstractMap.SimpleEntry<>("remote", "unknown"), + new AbstractMap.SimpleEntry<>("branch", "unknown")); + } + + return remoteAndBranchName; + } + + @Override + public void reset(Path targetRepository, String remoteName, String branchName) { + + initializeProcessContext(targetRepository); + ProcessResult result; + // check for changed files + result = this.processContext.addArg("diff-index").addArg("--quiet").addArg("HEAD").run(ProcessMode.DEFAULT_CAPTURE); + + if (!result.isSuccessful()) { + // reset to origin/master + context.warning("Git has detected modified files -- attempting to reset {} to '{}/{}'.", targetRepository, + remoteName, branchName); + result = this.processContext.addArg("reset").addArg("--hard").addArg(remoteName + "/" + branchName) + .run(ProcessMode.DEFAULT_CAPTURE); + + if (!result.isSuccessful()) { + context.warning("Git failed to reset {} to '{}/{}'.", remoteName, branchName, targetRepository); + handleErrors(targetRepository, result); + } + } + } + + @Override + public void cleanup(Path targetRepository) { + + initializeProcessContext(targetRepository); + ProcessResult result; + // check for untracked files + result = this.processContext.addArg("ls-files").addArg("--other").addArg("--directory").addArg("--exclude-standard") + .run(ProcessMode.DEFAULT_CAPTURE); + + if (!result.getOut().isEmpty()) { + // delete untracked files + context.warning("Git detected untracked files in {} and is attempting a cleanup.", targetRepository); + result = this.processContext.addArg("clean").addArg("-df").run(ProcessMode.DEFAULT_CAPTURE); + + if (!result.isSuccessful()) { + context.warning("Git failed to clean the repository {}.", targetRepository); + } + } + } + + @Override + public IdeSubLogger level(IdeLogLevel level) { + + return null; + } +} From 6a8bf0e891d15217faddf72e206fa6721e1c4e60 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Wed, 13 Mar 2024 22:43:18 +0100 Subject: [PATCH 88/93] Update GitContextImpl.java --- .../tools/ide/context/GitContextImpl.java | 574 +++++++++--------- 1 file changed, 287 insertions(+), 287 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/context/GitContextImpl.java b/cli/src/main/java/com/devonfw/tools/ide/context/GitContextImpl.java index 91022a728..9813bdd5e 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/context/GitContextImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/context/GitContextImpl.java @@ -1,287 +1,287 @@ -package com.devonfw.tools.ide.context; - -import java.io.IOException; -import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.attribute.FileTime; -import java.time.Duration; -import java.util.AbstractMap; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; - -import com.devonfw.tools.ide.cli.CliException; -import com.devonfw.tools.ide.log.IdeLogLevel; -import com.devonfw.tools.ide.log.IdeSubLogger; -import com.devonfw.tools.ide.process.ProcessContext; -import com.devonfw.tools.ide.process.ProcessErrorHandling; -import com.devonfw.tools.ide.process.ProcessMode; -import com.devonfw.tools.ide.process.ProcessResult; - -/** - * Implements the {@link GitContext}. - */ -public class GitContextImpl implements GitContext { - private static final Duration GIT_PULL_CACHE_DELAY_MILLIS = Duration.ofMillis(30 * 60 * 1000); - - ; - - private final IdeContext context; - - private ProcessContext processContext; - - /** - * @param context the {@link IdeContext context}. - */ - public GitContextImpl(IdeContext context) { - - this.context = context; - - } - - @Override - public void pullOrCloneIfNeeded(String repoUrl, String branch, Path targetRepository) { - - Path gitDirectory = targetRepository.resolve(".git"); - - // Check if the .git directory exists - if (Files.isDirectory(gitDirectory)) { - Path magicFilePath = gitDirectory.resolve("HEAD"); - long currentTime = System.currentTimeMillis(); - // Get the modification time of the magic file - long fileMTime; - try { - fileMTime = Files.getLastModifiedTime(magicFilePath).toMillis(); - } catch (IOException e) { - throw new IllegalStateException("Could not read " + magicFilePath, e); - } - - // Check if the file modification time is older than the delta threshold - if ((currentTime - fileMTime > GIT_PULL_CACHE_DELAY_MILLIS.toMillis()) || context.isForceMode()) { - pullOrClone(repoUrl, "", targetRepository); - try { - Files.setLastModifiedTime(magicFilePath, FileTime.fromMillis(currentTime)); - } catch (IOException e) { - throw new IllegalStateException("Could not read or write in " + magicFilePath, e); - } - } - } else { - // If the .git directory does not exist, perform git clone - pullOrClone(repoUrl, branch, targetRepository); - } - } - - public void pullOrFetchAndResetIfNeeded(String repoUrl, String branch, Path targetRepository, String remoteName) { - - pullOrCloneIfNeeded(repoUrl, branch, targetRepository); - - if (remoteName.isEmpty()) { - reset(targetRepository, "origin", "master"); - } else { - reset(targetRepository, remoteName, "master"); - } - - cleanup(targetRepository); - } - - @Override - public void pullOrClone(String gitRepoUrl, String branch, Path targetRepository) { - - Objects.requireNonNull(targetRepository); - Objects.requireNonNull(gitRepoUrl); - - if (!gitRepoUrl.startsWith("http")) { - throw new IllegalArgumentException("Invalid git URL '" + gitRepoUrl + "'!"); - } - - initializeProcessContext(targetRepository); - if (Files.isDirectory(targetRepository.resolve(".git"))) { - // checks for remotes - ProcessResult result = this.processContext.addArg("remote").run(ProcessMode.DEFAULT_CAPTURE); - List remotes = result.getOut(); - if (remotes.isEmpty()) { - String message = targetRepository - + " is a local git repository with no remote - if you did this for testing, you may continue...\n" - + "Do you want to ignore the problem and continue anyhow?"; - this.context.askToContinue(message); - } else { - this.processContext.errorHandling(ProcessErrorHandling.WARNING); - - if (!this.context.isOffline()) { - pull(targetRepository); - } - } - } else { - clone(new GitUrl(gitRepoUrl, branch), targetRepository); - } - } - - /** - * Handles errors which occurred during 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. - * @param result the {@link ProcessResult} to evaluate. - */ - private void handleErrors(Path targetRepository, ProcessResult result) { - - if (!result.isSuccessful()) { - String message = "Failed to update git repository at " + targetRepository; - if (this.context.isOffline()) { - this.context.warning(message); - this.context.interaction("Continuing as we are in offline mode - results may be outdated!"); - } else { - this.context.error(message); - if (this.context.isOnline()) { - this.context.error( - "See above error for details. If you have local changes, please stash or revert and retry."); - } else { - this.context.error( - "It seems you are offline - please ensure Internet connectivity and retry or activate offline mode (-o or --offline)."); - } - this.context.askToContinue("Typically you should abort and fix the problem. Do you want to continue anyways?"); - } - } - } - - /** - * Lazily initializes the {@link ProcessContext}. - * - * @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. - */ - private void initializeProcessContext(Path targetRepository) { - - if (this.processContext == null) { - this.processContext = this.context.newProcess().directory(targetRepository).executable("git") - .withEnvVar("GIT_TERMINAL_PROMPT", "0"); - } - } - - @Override - public void clone(GitUrl gitRepoUrl, Path targetRepository) { - - URL parsedUrl = gitRepoUrl.parseUrl(); - initializeProcessContext(targetRepository); - ProcessResult result; - if (!this.context.isOffline()) { - this.context.getFileAccess().mkdirs(targetRepository); - this.context.requireOnline("git clone of " + parsedUrl); - this.processContext.addArg("clone"); - if (this.context.isQuietMode()) { - this.processContext.addArg("-q"); - } - this.processContext.addArgs("--recursive", gitRepoUrl.url(), "--config", "core.autocrlf=false", "."); - result = this.processContext.run(ProcessMode.DEFAULT_CAPTURE); - if (!result.isSuccessful()) { - this.context.warning("Git failed to clone {} into {}.", parsedUrl, targetRepository); - } - String branch = gitRepoUrl.branch(); - if (branch != null) { - this.processContext.addArgs("checkout", branch); - result = this.processContext.run(ProcessMode.DEFAULT_CAPTURE); - if (!result.isSuccessful()) { - this.context.warning("Git failed to checkout to branch {}", branch); - } - } - } else { - throw new CliException("Could not clone " + parsedUrl + " to " + targetRepository + " because you are offline."); - } - } - - @Override - public void pull(Path targetRepository) { - - initializeProcessContext(targetRepository); - ProcessResult result; - // pull from remote - result = this.processContext.addArg("--no-pager").addArg("pull").run(ProcessMode.DEFAULT_CAPTURE); - - if (!result.isSuccessful()) { - Map remoteAndBranchName = retrieveRemoteAndBranchName(); - context.warning("Git pull for {}/{} failed for repository {}.", remoteAndBranchName.get("remote"), - remoteAndBranchName.get("branch"), targetRepository); - handleErrors(targetRepository, result); - } - } - - private Map retrieveRemoteAndBranchName() { - - Map remoteAndBranchName = new HashMap<>(); - ProcessResult remoteResult = this.processContext.addArg("branch").addArg("-vv").run(ProcessMode.DEFAULT_CAPTURE); - List remotes = remoteResult.getOut(); - if (!remotes.isEmpty()) { - for (String remote : remotes) { - if (remote.startsWith("*")) { - String checkedOutBranch = remote.substring(remote.indexOf("[") + 1, remote.indexOf("]")); - remoteAndBranchName.put("remote", checkedOutBranch.substring(0, checkedOutBranch.indexOf("/"))); - // check if current repo is behind remote and omit message - if (checkedOutBranch.contains(":")) { - remoteAndBranchName.put("branch", - checkedOutBranch.substring(checkedOutBranch.indexOf("/") + 1, checkedOutBranch.indexOf(":"))); - } else { - remoteAndBranchName.put("branch", checkedOutBranch.substring(checkedOutBranch.indexOf("/") + 1)); - } - - } - } - } else { - return Map.ofEntries(new AbstractMap.SimpleEntry<>("remote", "unknown"), - new AbstractMap.SimpleEntry<>("branch", "unknown")); - } - - return remoteAndBranchName; - } - - @Override - public void reset(Path targetRepository, String remoteName, String branchName) { - - initializeProcessContext(targetRepository); - ProcessResult result; - // check for changed files - result = this.processContext.addArg("diff-index").addArg("--quiet").addArg("HEAD").run(ProcessMode.DEFAULT_CAPTURE); - - if (!result.isSuccessful()) { - // reset to origin/master - context.warning("Git has detected modified files -- attempting to reset {} to '{}/{}'.", targetRepository, - remoteName, branchName); - result = this.processContext.addArg("reset").addArg("--hard").addArg(remoteName + "/" + branchName) - .run(ProcessMode.DEFAULT_CAPTURE); - - if (!result.isSuccessful()) { - context.warning("Git failed to reset {} to '{}/{}'.", remoteName, branchName, targetRepository); - handleErrors(targetRepository, result); - } - } - } - - @Override - public void cleanup(Path targetRepository) { - - initializeProcessContext(targetRepository); - ProcessResult result; - // check for untracked files - result = this.processContext.addArg("ls-files").addArg("--other").addArg("--directory").addArg("--exclude-standard") - .run(ProcessMode.DEFAULT_CAPTURE); - - if (!result.getOut().isEmpty()) { - // delete untracked files - context.warning("Git detected untracked files in {} and is attempting a cleanup.", targetRepository); - result = this.processContext.addArg("clean").addArg("-df").run(ProcessMode.DEFAULT_CAPTURE); - - if (!result.isSuccessful()) { - context.warning("Git failed to clean the repository {}.", targetRepository); - } - } - } - - @Override - public IdeSubLogger level(IdeLogLevel level) { - - return null; - } -} +package com.devonfw.tools.ide.context; + +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.FileTime; +import java.time.Duration; +import java.util.AbstractMap; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import com.devonfw.tools.ide.cli.CliException; +import com.devonfw.tools.ide.log.IdeLogLevel; +import com.devonfw.tools.ide.log.IdeSubLogger; +import com.devonfw.tools.ide.process.ProcessContext; +import com.devonfw.tools.ide.process.ProcessErrorHandling; +import com.devonfw.tools.ide.process.ProcessMode; +import com.devonfw.tools.ide.process.ProcessResult; + +/** + * Implements the {@link GitContext}. + */ +public class GitContextImpl implements GitContext { + private static final Duration GIT_PULL_CACHE_DELAY_MILLIS = Duration.ofMillis(30 * 60 * 1000); + + ; + + private final IdeContext context; + + private ProcessContext processContext; + + /** + * @param context the {@link IdeContext context}. + */ + public GitContextImpl(IdeContext context) { + + this.context = context; + + } + + @Override + public void pullOrCloneIfNeeded(String repoUrl, String branch, Path targetRepository) { + + Path gitDirectory = targetRepository.resolve(".git"); + + // Check if the .git directory exists + if (Files.isDirectory(gitDirectory)) { + Path magicFilePath = gitDirectory.resolve("HEAD"); + long currentTime = System.currentTimeMillis(); + // Get the modification time of the magic file + long fileMTime; + try { + fileMTime = Files.getLastModifiedTime(magicFilePath).toMillis(); + } catch (IOException e) { + throw new IllegalStateException("Could not read " + magicFilePath, e); + } + + // Check if the file modification time is older than the delta threshold + if ((currentTime - fileMTime > GIT_PULL_CACHE_DELAY_MILLIS.toMillis()) || context.isForceMode()) { + pullOrClone(repoUrl, "", targetRepository); + try { + Files.setLastModifiedTime(magicFilePath, FileTime.fromMillis(currentTime)); + } catch (IOException e) { + throw new IllegalStateException("Could not read or write in " + magicFilePath, e); + } + } + } else { + // If the .git directory does not exist, perform git clone + pullOrClone(repoUrl, branch, targetRepository); + } + } + + public void pullOrFetchAndResetIfNeeded(String repoUrl, String branch, Path targetRepository, String remoteName) { + + pullOrCloneIfNeeded(repoUrl, branch, targetRepository); + + if (remoteName.isEmpty()) { + reset(targetRepository, "origin", "master"); + } else { + reset(targetRepository, remoteName, "master"); + } + + cleanup(targetRepository); + } + + @Override + public void pullOrClone(String gitRepoUrl, String branch, Path targetRepository) { + + Objects.requireNonNull(targetRepository); + Objects.requireNonNull(gitRepoUrl); + + if (!gitRepoUrl.startsWith("http")) { + throw new IllegalArgumentException("Invalid git URL '" + gitRepoUrl + "'!"); + } + + initializeProcessContext(targetRepository); + if (Files.isDirectory(targetRepository.resolve(".git"))) { + // checks for remotes + ProcessResult result = this.processContext.addArg("remote").run(ProcessMode.DEFAULT_CAPTURE); + List remotes = result.getOut(); + if (remotes.isEmpty()) { + String message = targetRepository + + " is a local git repository with no remote - if you did this for testing, you may continue...\n" + + "Do you want to ignore the problem and continue anyhow?"; + this.context.askToContinue(message); + } else { + this.processContext.errorHandling(ProcessErrorHandling.WARNING); + + if (!this.context.isOffline()) { + pull(targetRepository); + } + } + } else { + clone(new GitUrl(gitRepoUrl, branch), targetRepository); + } + } + + /** + * Handles errors which occurred during 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. + * @param result the {@link ProcessResult} to evaluate. + */ + private void handleErrors(Path targetRepository, ProcessResult result) { + + if (!result.isSuccessful()) { + String message = "Failed to update git repository at " + targetRepository; + if (this.context.isOffline()) { + this.context.warning(message); + this.context.interaction("Continuing as we are in offline mode - results may be outdated!"); + } else { + this.context.error(message); + if (this.context.isOnline()) { + this.context.error( + "See above error for details. If you have local changes, please stash or revert and retry."); + } else { + this.context.error( + "It seems you are offline - please ensure Internet connectivity and retry or activate offline mode (-o or --offline)."); + } + this.context.askToContinue("Typically you should abort and fix the problem. Do you want to continue anyways?"); + } + } + } + + /** + * Lazily initializes the {@link ProcessContext}. + * + * @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. + */ + private void initializeProcessContext(Path targetRepository) { + + if (this.processContext == null) { + this.processContext = this.context.newProcess().directory(targetRepository).executable("git") + .withEnvVar("GIT_TERMINAL_PROMPT", "0"); + } + } + + @Override + public void clone(GitUrl gitRepoUrl, Path targetRepository) { + + URL parsedUrl = gitRepoUrl.parseUrl(); + initializeProcessContext(targetRepository); + ProcessResult result; + if (!this.context.isOffline()) { + this.context.getFileAccess().mkdirs(targetRepository); + this.context.requireOnline("git clone of " + parsedUrl); + this.processContext.addArg("clone"); + if (this.context.isQuietMode()) { + this.processContext.addArg("-q"); + } + this.processContext.addArgs("--recursive", gitRepoUrl.url(), "--config", "core.autocrlf=false", "."); + result = this.processContext.run(ProcessMode.DEFAULT_CAPTURE); + if (!result.isSuccessful()) { + this.context.warning("Git failed to clone {} into {}.", parsedUrl, targetRepository); + } + String branch = gitRepoUrl.branch(); + if (branch != null) { + this.processContext.addArgs("checkout", branch); + result = this.processContext.run(ProcessMode.DEFAULT_CAPTURE); + if (!result.isSuccessful()) { + this.context.warning("Git failed to checkout to branch {}", branch); + } + } + } else { + throw new CliException("Could not clone " + parsedUrl + " to " + targetRepository + " because you are offline."); + } + } + + @Override + public void pull(Path targetRepository) { + + initializeProcessContext(targetRepository); + ProcessResult result; + // pull from remote + result = this.processContext.addArg("--no-pager").addArg("pull").run(ProcessMode.DEFAULT_CAPTURE); + + if (!result.isSuccessful()) { + Map remoteAndBranchName = retrieveRemoteAndBranchName(); + context.warning("Git pull for {}/{} failed for repository {}.", remoteAndBranchName.get("remote"), + remoteAndBranchName.get("branch"), targetRepository); + handleErrors(targetRepository, result); + } + } + + private Map retrieveRemoteAndBranchName() { + + Map remoteAndBranchName = new HashMap<>(); + ProcessResult remoteResult = this.processContext.addArg("branch").addArg("-vv").run(ProcessMode.DEFAULT_CAPTURE); + List remotes = remoteResult.getOut(); + if (!remotes.isEmpty()) { + for (String remote : remotes) { + if (remote.startsWith("*")) { + String checkedOutBranch = remote.substring(remote.indexOf("[") + 1, remote.indexOf("]")); + remoteAndBranchName.put("remote", checkedOutBranch.substring(0, checkedOutBranch.indexOf("/"))); + // check if current repo is behind remote and omit message + if (checkedOutBranch.contains(":")) { + remoteAndBranchName.put("branch", + checkedOutBranch.substring(checkedOutBranch.indexOf("/") + 1, checkedOutBranch.indexOf(":"))); + } else { + remoteAndBranchName.put("branch", checkedOutBranch.substring(checkedOutBranch.indexOf("/") + 1)); + } + + } + } + } else { + return Map.ofEntries(new AbstractMap.SimpleEntry<>("remote", "unknown"), + new AbstractMap.SimpleEntry<>("branch", "unknown")); + } + + return remoteAndBranchName; + } + + @Override + public void reset(Path targetRepository, String remoteName, String branchName) { + + initializeProcessContext(targetRepository); + ProcessResult result; + // check for changed files + result = this.processContext.addArg("diff-index").addArg("--quiet").addArg("HEAD").run(ProcessMode.DEFAULT_CAPTURE); + + if (!result.isSuccessful()) { + // reset to origin/master + context.warning("Git has detected modified files -- attempting to reset {} to '{}/{}'.", targetRepository, + remoteName, branchName); + result = this.processContext.addArg("reset").addArg("--hard").addArg(remoteName + "/" + branchName) + .run(ProcessMode.DEFAULT_CAPTURE); + + if (!result.isSuccessful()) { + context.warning("Git failed to reset {} to '{}/{}'.", remoteName, branchName, targetRepository); + handleErrors(targetRepository, result); + } + } + } + + @Override + public void cleanup(Path targetRepository) { + + initializeProcessContext(targetRepository); + ProcessResult result; + // check for untracked files + result = this.processContext.addArg("ls-files").addArg("--other").addArg("--directory").addArg("--exclude-standard") + .run(ProcessMode.DEFAULT_CAPTURE); + + if (!result.getOut().isEmpty()) { + // delete untracked files + context.warning("Git detected untracked files in {} and is attempting a cleanup.", targetRepository); + result = this.processContext.addArg("clean").addArg("-df").run(ProcessMode.DEFAULT_CAPTURE); + + if (!result.isSuccessful()) { + context.warning("Git failed to clean the repository {}.", targetRepository); + } + } + } + + @Override + public IdeSubLogger level(IdeLogLevel level) { + + return null; + } +} From 7b4bc5712e93ad8ad1d83a446019d526692bedb8 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Wed, 13 Mar 2024 22:44:00 +0100 Subject: [PATCH 89/93] Update GitContextImpl.java --- .../tools/ide/context/GitContextImpl.java | 574 +++++++++--------- 1 file changed, 287 insertions(+), 287 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/context/GitContextImpl.java b/cli/src/main/java/com/devonfw/tools/ide/context/GitContextImpl.java index 9813bdd5e..91022a728 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/context/GitContextImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/context/GitContextImpl.java @@ -1,287 +1,287 @@ -package com.devonfw.tools.ide.context; - -import java.io.IOException; -import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.attribute.FileTime; -import java.time.Duration; -import java.util.AbstractMap; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; - -import com.devonfw.tools.ide.cli.CliException; -import com.devonfw.tools.ide.log.IdeLogLevel; -import com.devonfw.tools.ide.log.IdeSubLogger; -import com.devonfw.tools.ide.process.ProcessContext; -import com.devonfw.tools.ide.process.ProcessErrorHandling; -import com.devonfw.tools.ide.process.ProcessMode; -import com.devonfw.tools.ide.process.ProcessResult; - -/** - * Implements the {@link GitContext}. - */ -public class GitContextImpl implements GitContext { - private static final Duration GIT_PULL_CACHE_DELAY_MILLIS = Duration.ofMillis(30 * 60 * 1000); - - ; - - private final IdeContext context; - - private ProcessContext processContext; - - /** - * @param context the {@link IdeContext context}. - */ - public GitContextImpl(IdeContext context) { - - this.context = context; - - } - - @Override - public void pullOrCloneIfNeeded(String repoUrl, String branch, Path targetRepository) { - - Path gitDirectory = targetRepository.resolve(".git"); - - // Check if the .git directory exists - if (Files.isDirectory(gitDirectory)) { - Path magicFilePath = gitDirectory.resolve("HEAD"); - long currentTime = System.currentTimeMillis(); - // Get the modification time of the magic file - long fileMTime; - try { - fileMTime = Files.getLastModifiedTime(magicFilePath).toMillis(); - } catch (IOException e) { - throw new IllegalStateException("Could not read " + magicFilePath, e); - } - - // Check if the file modification time is older than the delta threshold - if ((currentTime - fileMTime > GIT_PULL_CACHE_DELAY_MILLIS.toMillis()) || context.isForceMode()) { - pullOrClone(repoUrl, "", targetRepository); - try { - Files.setLastModifiedTime(magicFilePath, FileTime.fromMillis(currentTime)); - } catch (IOException e) { - throw new IllegalStateException("Could not read or write in " + magicFilePath, e); - } - } - } else { - // If the .git directory does not exist, perform git clone - pullOrClone(repoUrl, branch, targetRepository); - } - } - - public void pullOrFetchAndResetIfNeeded(String repoUrl, String branch, Path targetRepository, String remoteName) { - - pullOrCloneIfNeeded(repoUrl, branch, targetRepository); - - if (remoteName.isEmpty()) { - reset(targetRepository, "origin", "master"); - } else { - reset(targetRepository, remoteName, "master"); - } - - cleanup(targetRepository); - } - - @Override - public void pullOrClone(String gitRepoUrl, String branch, Path targetRepository) { - - Objects.requireNonNull(targetRepository); - Objects.requireNonNull(gitRepoUrl); - - if (!gitRepoUrl.startsWith("http")) { - throw new IllegalArgumentException("Invalid git URL '" + gitRepoUrl + "'!"); - } - - initializeProcessContext(targetRepository); - if (Files.isDirectory(targetRepository.resolve(".git"))) { - // checks for remotes - ProcessResult result = this.processContext.addArg("remote").run(ProcessMode.DEFAULT_CAPTURE); - List remotes = result.getOut(); - if (remotes.isEmpty()) { - String message = targetRepository - + " is a local git repository with no remote - if you did this for testing, you may continue...\n" - + "Do you want to ignore the problem and continue anyhow?"; - this.context.askToContinue(message); - } else { - this.processContext.errorHandling(ProcessErrorHandling.WARNING); - - if (!this.context.isOffline()) { - pull(targetRepository); - } - } - } else { - clone(new GitUrl(gitRepoUrl, branch), targetRepository); - } - } - - /** - * Handles errors which occurred during 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. - * @param result the {@link ProcessResult} to evaluate. - */ - private void handleErrors(Path targetRepository, ProcessResult result) { - - if (!result.isSuccessful()) { - String message = "Failed to update git repository at " + targetRepository; - if (this.context.isOffline()) { - this.context.warning(message); - this.context.interaction("Continuing as we are in offline mode - results may be outdated!"); - } else { - this.context.error(message); - if (this.context.isOnline()) { - this.context.error( - "See above error for details. If you have local changes, please stash or revert and retry."); - } else { - this.context.error( - "It seems you are offline - please ensure Internet connectivity and retry or activate offline mode (-o or --offline)."); - } - this.context.askToContinue("Typically you should abort and fix the problem. Do you want to continue anyways?"); - } - } - } - - /** - * Lazily initializes the {@link ProcessContext}. - * - * @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. - */ - private void initializeProcessContext(Path targetRepository) { - - if (this.processContext == null) { - this.processContext = this.context.newProcess().directory(targetRepository).executable("git") - .withEnvVar("GIT_TERMINAL_PROMPT", "0"); - } - } - - @Override - public void clone(GitUrl gitRepoUrl, Path targetRepository) { - - URL parsedUrl = gitRepoUrl.parseUrl(); - initializeProcessContext(targetRepository); - ProcessResult result; - if (!this.context.isOffline()) { - this.context.getFileAccess().mkdirs(targetRepository); - this.context.requireOnline("git clone of " + parsedUrl); - this.processContext.addArg("clone"); - if (this.context.isQuietMode()) { - this.processContext.addArg("-q"); - } - this.processContext.addArgs("--recursive", gitRepoUrl.url(), "--config", "core.autocrlf=false", "."); - result = this.processContext.run(ProcessMode.DEFAULT_CAPTURE); - if (!result.isSuccessful()) { - this.context.warning("Git failed to clone {} into {}.", parsedUrl, targetRepository); - } - String branch = gitRepoUrl.branch(); - if (branch != null) { - this.processContext.addArgs("checkout", branch); - result = this.processContext.run(ProcessMode.DEFAULT_CAPTURE); - if (!result.isSuccessful()) { - this.context.warning("Git failed to checkout to branch {}", branch); - } - } - } else { - throw new CliException("Could not clone " + parsedUrl + " to " + targetRepository + " because you are offline."); - } - } - - @Override - public void pull(Path targetRepository) { - - initializeProcessContext(targetRepository); - ProcessResult result; - // pull from remote - result = this.processContext.addArg("--no-pager").addArg("pull").run(ProcessMode.DEFAULT_CAPTURE); - - if (!result.isSuccessful()) { - Map remoteAndBranchName = retrieveRemoteAndBranchName(); - context.warning("Git pull for {}/{} failed for repository {}.", remoteAndBranchName.get("remote"), - remoteAndBranchName.get("branch"), targetRepository); - handleErrors(targetRepository, result); - } - } - - private Map retrieveRemoteAndBranchName() { - - Map remoteAndBranchName = new HashMap<>(); - ProcessResult remoteResult = this.processContext.addArg("branch").addArg("-vv").run(ProcessMode.DEFAULT_CAPTURE); - List remotes = remoteResult.getOut(); - if (!remotes.isEmpty()) { - for (String remote : remotes) { - if (remote.startsWith("*")) { - String checkedOutBranch = remote.substring(remote.indexOf("[") + 1, remote.indexOf("]")); - remoteAndBranchName.put("remote", checkedOutBranch.substring(0, checkedOutBranch.indexOf("/"))); - // check if current repo is behind remote and omit message - if (checkedOutBranch.contains(":")) { - remoteAndBranchName.put("branch", - checkedOutBranch.substring(checkedOutBranch.indexOf("/") + 1, checkedOutBranch.indexOf(":"))); - } else { - remoteAndBranchName.put("branch", checkedOutBranch.substring(checkedOutBranch.indexOf("/") + 1)); - } - - } - } - } else { - return Map.ofEntries(new AbstractMap.SimpleEntry<>("remote", "unknown"), - new AbstractMap.SimpleEntry<>("branch", "unknown")); - } - - return remoteAndBranchName; - } - - @Override - public void reset(Path targetRepository, String remoteName, String branchName) { - - initializeProcessContext(targetRepository); - ProcessResult result; - // check for changed files - result = this.processContext.addArg("diff-index").addArg("--quiet").addArg("HEAD").run(ProcessMode.DEFAULT_CAPTURE); - - if (!result.isSuccessful()) { - // reset to origin/master - context.warning("Git has detected modified files -- attempting to reset {} to '{}/{}'.", targetRepository, - remoteName, branchName); - result = this.processContext.addArg("reset").addArg("--hard").addArg(remoteName + "/" + branchName) - .run(ProcessMode.DEFAULT_CAPTURE); - - if (!result.isSuccessful()) { - context.warning("Git failed to reset {} to '{}/{}'.", remoteName, branchName, targetRepository); - handleErrors(targetRepository, result); - } - } - } - - @Override - public void cleanup(Path targetRepository) { - - initializeProcessContext(targetRepository); - ProcessResult result; - // check for untracked files - result = this.processContext.addArg("ls-files").addArg("--other").addArg("--directory").addArg("--exclude-standard") - .run(ProcessMode.DEFAULT_CAPTURE); - - if (!result.getOut().isEmpty()) { - // delete untracked files - context.warning("Git detected untracked files in {} and is attempting a cleanup.", targetRepository); - result = this.processContext.addArg("clean").addArg("-df").run(ProcessMode.DEFAULT_CAPTURE); - - if (!result.isSuccessful()) { - context.warning("Git failed to clean the repository {}.", targetRepository); - } - } - } - - @Override - public IdeSubLogger level(IdeLogLevel level) { - - return null; - } -} +package com.devonfw.tools.ide.context; + +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.FileTime; +import java.time.Duration; +import java.util.AbstractMap; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import com.devonfw.tools.ide.cli.CliException; +import com.devonfw.tools.ide.log.IdeLogLevel; +import com.devonfw.tools.ide.log.IdeSubLogger; +import com.devonfw.tools.ide.process.ProcessContext; +import com.devonfw.tools.ide.process.ProcessErrorHandling; +import com.devonfw.tools.ide.process.ProcessMode; +import com.devonfw.tools.ide.process.ProcessResult; + +/** + * Implements the {@link GitContext}. + */ +public class GitContextImpl implements GitContext { + private static final Duration GIT_PULL_CACHE_DELAY_MILLIS = Duration.ofMillis(30 * 60 * 1000); + + ; + + private final IdeContext context; + + private ProcessContext processContext; + + /** + * @param context the {@link IdeContext context}. + */ + public GitContextImpl(IdeContext context) { + + this.context = context; + + } + + @Override + public void pullOrCloneIfNeeded(String repoUrl, String branch, Path targetRepository) { + + Path gitDirectory = targetRepository.resolve(".git"); + + // Check if the .git directory exists + if (Files.isDirectory(gitDirectory)) { + Path magicFilePath = gitDirectory.resolve("HEAD"); + long currentTime = System.currentTimeMillis(); + // Get the modification time of the magic file + long fileMTime; + try { + fileMTime = Files.getLastModifiedTime(magicFilePath).toMillis(); + } catch (IOException e) { + throw new IllegalStateException("Could not read " + magicFilePath, e); + } + + // Check if the file modification time is older than the delta threshold + if ((currentTime - fileMTime > GIT_PULL_CACHE_DELAY_MILLIS.toMillis()) || context.isForceMode()) { + pullOrClone(repoUrl, "", targetRepository); + try { + Files.setLastModifiedTime(magicFilePath, FileTime.fromMillis(currentTime)); + } catch (IOException e) { + throw new IllegalStateException("Could not read or write in " + magicFilePath, e); + } + } + } else { + // If the .git directory does not exist, perform git clone + pullOrClone(repoUrl, branch, targetRepository); + } + } + + public void pullOrFetchAndResetIfNeeded(String repoUrl, String branch, Path targetRepository, String remoteName) { + + pullOrCloneIfNeeded(repoUrl, branch, targetRepository); + + if (remoteName.isEmpty()) { + reset(targetRepository, "origin", "master"); + } else { + reset(targetRepository, remoteName, "master"); + } + + cleanup(targetRepository); + } + + @Override + public void pullOrClone(String gitRepoUrl, String branch, Path targetRepository) { + + Objects.requireNonNull(targetRepository); + Objects.requireNonNull(gitRepoUrl); + + if (!gitRepoUrl.startsWith("http")) { + throw new IllegalArgumentException("Invalid git URL '" + gitRepoUrl + "'!"); + } + + initializeProcessContext(targetRepository); + if (Files.isDirectory(targetRepository.resolve(".git"))) { + // checks for remotes + ProcessResult result = this.processContext.addArg("remote").run(ProcessMode.DEFAULT_CAPTURE); + List remotes = result.getOut(); + if (remotes.isEmpty()) { + String message = targetRepository + + " is a local git repository with no remote - if you did this for testing, you may continue...\n" + + "Do you want to ignore the problem and continue anyhow?"; + this.context.askToContinue(message); + } else { + this.processContext.errorHandling(ProcessErrorHandling.WARNING); + + if (!this.context.isOffline()) { + pull(targetRepository); + } + } + } else { + clone(new GitUrl(gitRepoUrl, branch), targetRepository); + } + } + + /** + * Handles errors which occurred during 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. + * @param result the {@link ProcessResult} to evaluate. + */ + private void handleErrors(Path targetRepository, ProcessResult result) { + + if (!result.isSuccessful()) { + String message = "Failed to update git repository at " + targetRepository; + if (this.context.isOffline()) { + this.context.warning(message); + this.context.interaction("Continuing as we are in offline mode - results may be outdated!"); + } else { + this.context.error(message); + if (this.context.isOnline()) { + this.context.error( + "See above error for details. If you have local changes, please stash or revert and retry."); + } else { + this.context.error( + "It seems you are offline - please ensure Internet connectivity and retry or activate offline mode (-o or --offline)."); + } + this.context.askToContinue("Typically you should abort and fix the problem. Do you want to continue anyways?"); + } + } + } + + /** + * Lazily initializes the {@link ProcessContext}. + * + * @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. + */ + private void initializeProcessContext(Path targetRepository) { + + if (this.processContext == null) { + this.processContext = this.context.newProcess().directory(targetRepository).executable("git") + .withEnvVar("GIT_TERMINAL_PROMPT", "0"); + } + } + + @Override + public void clone(GitUrl gitRepoUrl, Path targetRepository) { + + URL parsedUrl = gitRepoUrl.parseUrl(); + initializeProcessContext(targetRepository); + ProcessResult result; + if (!this.context.isOffline()) { + this.context.getFileAccess().mkdirs(targetRepository); + this.context.requireOnline("git clone of " + parsedUrl); + this.processContext.addArg("clone"); + if (this.context.isQuietMode()) { + this.processContext.addArg("-q"); + } + this.processContext.addArgs("--recursive", gitRepoUrl.url(), "--config", "core.autocrlf=false", "."); + result = this.processContext.run(ProcessMode.DEFAULT_CAPTURE); + if (!result.isSuccessful()) { + this.context.warning("Git failed to clone {} into {}.", parsedUrl, targetRepository); + } + String branch = gitRepoUrl.branch(); + if (branch != null) { + this.processContext.addArgs("checkout", branch); + result = this.processContext.run(ProcessMode.DEFAULT_CAPTURE); + if (!result.isSuccessful()) { + this.context.warning("Git failed to checkout to branch {}", branch); + } + } + } else { + throw new CliException("Could not clone " + parsedUrl + " to " + targetRepository + " because you are offline."); + } + } + + @Override + public void pull(Path targetRepository) { + + initializeProcessContext(targetRepository); + ProcessResult result; + // pull from remote + result = this.processContext.addArg("--no-pager").addArg("pull").run(ProcessMode.DEFAULT_CAPTURE); + + if (!result.isSuccessful()) { + Map remoteAndBranchName = retrieveRemoteAndBranchName(); + context.warning("Git pull for {}/{} failed for repository {}.", remoteAndBranchName.get("remote"), + remoteAndBranchName.get("branch"), targetRepository); + handleErrors(targetRepository, result); + } + } + + private Map retrieveRemoteAndBranchName() { + + Map remoteAndBranchName = new HashMap<>(); + ProcessResult remoteResult = this.processContext.addArg("branch").addArg("-vv").run(ProcessMode.DEFAULT_CAPTURE); + List remotes = remoteResult.getOut(); + if (!remotes.isEmpty()) { + for (String remote : remotes) { + if (remote.startsWith("*")) { + String checkedOutBranch = remote.substring(remote.indexOf("[") + 1, remote.indexOf("]")); + remoteAndBranchName.put("remote", checkedOutBranch.substring(0, checkedOutBranch.indexOf("/"))); + // check if current repo is behind remote and omit message + if (checkedOutBranch.contains(":")) { + remoteAndBranchName.put("branch", + checkedOutBranch.substring(checkedOutBranch.indexOf("/") + 1, checkedOutBranch.indexOf(":"))); + } else { + remoteAndBranchName.put("branch", checkedOutBranch.substring(checkedOutBranch.indexOf("/") + 1)); + } + + } + } + } else { + return Map.ofEntries(new AbstractMap.SimpleEntry<>("remote", "unknown"), + new AbstractMap.SimpleEntry<>("branch", "unknown")); + } + + return remoteAndBranchName; + } + + @Override + public void reset(Path targetRepository, String remoteName, String branchName) { + + initializeProcessContext(targetRepository); + ProcessResult result; + // check for changed files + result = this.processContext.addArg("diff-index").addArg("--quiet").addArg("HEAD").run(ProcessMode.DEFAULT_CAPTURE); + + if (!result.isSuccessful()) { + // reset to origin/master + context.warning("Git has detected modified files -- attempting to reset {} to '{}/{}'.", targetRepository, + remoteName, branchName); + result = this.processContext.addArg("reset").addArg("--hard").addArg(remoteName + "/" + branchName) + .run(ProcessMode.DEFAULT_CAPTURE); + + if (!result.isSuccessful()) { + context.warning("Git failed to reset {} to '{}/{}'.", remoteName, branchName, targetRepository); + handleErrors(targetRepository, result); + } + } + } + + @Override + public void cleanup(Path targetRepository) { + + initializeProcessContext(targetRepository); + ProcessResult result; + // check for untracked files + result = this.processContext.addArg("ls-files").addArg("--other").addArg("--directory").addArg("--exclude-standard") + .run(ProcessMode.DEFAULT_CAPTURE); + + if (!result.getOut().isEmpty()) { + // delete untracked files + context.warning("Git detected untracked files in {} and is attempting a cleanup.", targetRepository); + result = this.processContext.addArg("clean").addArg("-df").run(ProcessMode.DEFAULT_CAPTURE); + + if (!result.isSuccessful()) { + context.warning("Git failed to clean the repository {}.", targetRepository); + } + } + } + + @Override + public IdeSubLogger level(IdeLogLevel level) { + + return null; + } +} From b73e83d98fae2846019e4bf59861c7ccfb7a9d5f Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Fri, 15 Mar 2024 11:17:24 +0100 Subject: [PATCH 90/93] small git api improvements --- .../ide/commandlet/RepositoryCommandlet.java | 6 +----- .../devonfw/tools/ide/context/GitContext.java | 19 +++++++++++++------ .../tools/ide/context/GitContextImpl.java | 6 ++++++ .../com/devonfw/tools/ide/context/GitUrl.java | 2 +- .../tools/ide/context/GitContextMock.java | 5 +++++ .../tools/ide/context/GitContextTest.java | 4 ++-- 6 files changed, 28 insertions(+), 14 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java index d85cdcc9a..f216099a4 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java @@ -105,11 +105,7 @@ private void doImportRepository(Path repositoryFile, boolean forceMode) { this.context.getFileAccess().mkdirs(workspacePath); Path repositoryPath = workspacePath.resolve(repository); - String branch = ""; - if (repositoryConfig.gitBranch() != null) { - branch = repositoryConfig.gitBranch(); - } - this.context.getGitContext().pullOrClone(gitUrl, branch, repositoryPath); + this.context.getGitContext().pullOrClone(gitUrl, repositoryConfig.gitBranch(), repositoryPath); String buildCmd = repositoryConfig.buildCmd(); this.context.debug("Building repository with ide command: {}", buildCmd); diff --git a/cli/src/main/java/com/devonfw/tools/ide/context/GitContext.java b/cli/src/main/java/com/devonfw/tools/ide/context/GitContext.java index c79c7a7ea..fa3a21bff 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/context/GitContext.java +++ b/cli/src/main/java/com/devonfw/tools/ide/context/GitContext.java @@ -13,8 +13,7 @@ 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. @@ -24,8 +23,7 @@ public interface GitContext extends IdeLogger { /** * 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 @@ -37,8 +35,17 @@ public interface GitContext extends IdeLogger { /** * 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. + */ + 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 diff --git a/cli/src/main/java/com/devonfw/tools/ide/context/GitContextImpl.java b/cli/src/main/java/com/devonfw/tools/ide/context/GitContextImpl.java index 91022a728..b1f8d3232 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/context/GitContextImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/context/GitContextImpl.java @@ -86,6 +86,12 @@ public void pullOrFetchAndResetIfNeeded(String repoUrl, String branch, Path targ cleanup(targetRepository); } + @Override + public void pullOrClone(String gitRepoUrl, Path targetRepository) { + + pullOrClone(gitRepoUrl, null, targetRepository); + } + @Override public void pullOrClone(String gitRepoUrl, String branch, Path targetRepository) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/context/GitUrl.java b/cli/src/main/java/com/devonfw/tools/ide/context/GitUrl.java index daee4352f..aa678cf2f 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/context/GitUrl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/context/GitUrl.java @@ -19,7 +19,7 @@ public record GitUrl(String url, String branch) { public URL parseUrl() { String parsedUrl = url; - if (!branch.isEmpty()) { + if (branch != null && !branch.isEmpty()) { parsedUrl += "#" + branch; } URL validUrl; diff --git a/cli/src/test/java/com/devonfw/tools/ide/context/GitContextMock.java b/cli/src/test/java/com/devonfw/tools/ide/context/GitContextMock.java index 15681af20..7cb12d19d 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/context/GitContextMock.java +++ b/cli/src/test/java/com/devonfw/tools/ide/context/GitContextMock.java @@ -16,6 +16,11 @@ public void pullOrFetchAndResetIfNeeded(String repoUrl, String branch, Path targ } + @Override + public void pullOrClone(String gitRepoUrl, Path targetRepository) { + + } + @Override public void pullOrClone(String gitRepoUrl, String branch, Path targetRepository) { diff --git a/cli/src/test/java/com/devonfw/tools/ide/context/GitContextTest.java b/cli/src/test/java/com/devonfw/tools/ide/context/GitContextTest.java index 090f0445b..527466dbc 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/context/GitContextTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/context/GitContextTest.java @@ -55,7 +55,7 @@ public void testRunGitClone(@TempDir Path tempDir) { IdeContext context = newGitContext(tempDir, errors, outs, 0, true); GitContext gitContext = new GitContextImpl(context); // act - gitContext.pullOrClone(gitRepoUrl, "", tempDir); + gitContext.pullOrClone(gitRepoUrl, tempDir); // assert assertThat(tempDir.resolve(".git").resolve("url")).hasContent(gitRepoUrl); } @@ -78,7 +78,7 @@ public void testRunGitPullWithoutForce(@TempDir Path tempDir) { Path gitFolderPath = tempDir.resolve(".git"); fileAccess.mkdirs(gitFolderPath); // act - gitContext.pullOrClone(gitRepoUrl, "", tempDir); + gitContext.pullOrClone(gitRepoUrl, tempDir); // assert assertThat(tempDir.resolve(".git").resolve("update")).hasContent(currentDate.toString()); } From f513dfa4dc9f882f0c8ab3eac032085891571d6e Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Fri, 15 Mar 2024 11:22:53 +0100 Subject: [PATCH 91/93] Update GitContextImpl.java --- .../main/java/com/devonfw/tools/ide/context/GitContextImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/context/GitContextImpl.java b/cli/src/main/java/com/devonfw/tools/ide/context/GitContextImpl.java index b1f8d3232..1203816dc 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/context/GitContextImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/context/GitContextImpl.java @@ -60,7 +60,7 @@ public void pullOrCloneIfNeeded(String repoUrl, String branch, Path targetReposi // Check if the file modification time is older than the delta threshold if ((currentTime - fileMTime > GIT_PULL_CACHE_DELAY_MILLIS.toMillis()) || context.isForceMode()) { - pullOrClone(repoUrl, "", targetRepository); + pullOrClone(repoUrl, targetRepository); try { Files.setLastModifiedTime(magicFilePath, FileTime.fromMillis(currentTime)); } catch (IOException e) { From 6315f7d1495924bb08e3e138b2dbb17b1dc7563e Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Mon, 18 Mar 2024 20:14:46 +0100 Subject: [PATCH 92/93] reviewed changes --- .../ide/commandlet/UpdateCommandlet.java | 58 ++++++++++--------- .../devonfw/tools/ide/context/IdeContext.java | 4 ++ 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java index c24b348dd..a80287dcc 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java @@ -46,43 +46,45 @@ public void run() { updateSettings(); this.context.getFileAccess().mkdirs(this.context.getWorkspacePath()); - setupConf(this.context.getSettingsPath().resolve("devon"), this.context.getIdeHome()); + Path templatesFolder = this.context.getSettingsPath().resolve(IdeContext.FOLDER_TEMPLATES); + if (!Files.exists(templatesFolder)) { + Path legacyTemplatesFolder = this.context.getSettingsPath().resolve(IdeContext.FOLDER_LEGACY_TEMPLATES); + if (Files.exists(legacyTemplatesFolder)) { + templatesFolder = legacyTemplatesFolder; + } else { + this.context.warning("Templates folder is missing in settings folder."); + return; + } + } + setupConf(templatesFolder, this.context.getIdeHome()); updateSoftware(); updateRepositories(); } private void setupConf(Path template, Path conf) { - try (Stream childStream = Files.list(template)) { - Iterator iterator = childStream.iterator(); - while (iterator.hasNext()) { - Path child = iterator.next(); - String basename = child.getFileName().toString(); - - if (!basename.equals(".") && !basename.equals("..") && !basename.equals("*")) { - Path confPath = conf.resolve(basename); - - if (Files.isDirectory(child)) { - if (!Files.exists(confPath) || !Files.isDirectory(confPath)) { - this.context.getFileAccess().mkdirs(confPath); - } - setupConf(child, confPath); - } else if (Files.isRegularFile(child)) { - if (Files.isRegularFile(confPath)) { - this.context.debug("Configuration {} already exists - skipping to copy from {}", confPath, child); - } else { - if (!basename.equals("settings.xml")) { - this.context.info("Copying template {} to {}.", child, confPath); - this.context.getFileAccess().copy(child, confPath); - } - } + List children = this.context.getFileAccess().listChildren(template, f -> true); + for (Path child : children) { + + String basename = child.getFileName().toString(); + Path confPath = conf.resolve(basename); + + if (Files.isDirectory(child)) { + if (!Files.isDirectory(confPath)) { + this.context.getFileAccess().mkdirs(confPath); + } + setupConf(child, confPath); + } else if (Files.isRegularFile(child)) { + if (Files.isRegularFile(confPath)) { + this.context.debug("Configuration {} already exists - skipping to copy from {}", confPath, child); + } else { + if (!basename.equals("settings.xml")) { + this.context.info("Copying template {} to {}.", child, confPath); + this.context.getFileAccess().copy(child, confPath); } } } - } catch (IOException e) { - throw new IllegalStateException("Could not setup the conf folder.", e); } - } private void updateSettings() { @@ -123,7 +125,7 @@ private void updateSoftware() { Set toolCommandlets = new HashSet<>(); // installed tools in IDE_HOME/software - List softwares = this.context.getFileAccess().getChildrenInDir(this.context.getSoftwarePath(), Files::isDirectory); + List softwares = this.context.getFileAccess().listChildren(this.context.getSoftwarePath(), Files::isDirectory); for (Path software : softwares) { String toolName = software.getFileName().toString(); ToolCommandlet toolCommandlet = this.context.getCommandletManager().getToolCommandletOrNull(toolName); diff --git a/cli/src/main/java/com/devonfw/tools/ide/context/IdeContext.java b/cli/src/main/java/com/devonfw/tools/ide/context/IdeContext.java index c462495b4..21415c626 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/context/IdeContext.java +++ b/cli/src/main/java/com/devonfw/tools/ide/context/IdeContext.java @@ -118,6 +118,10 @@ public interface IdeContext extends IdeLogger { /** The default for {@link #getWorkspaceName()}. */ String WORKSPACE_MAIN = "main"; + String FOLDER_TEMPLATES = "templates"; + + String FOLDER_LEGACY_TEMPLATES = "devon"; + /** * @return {@code true} in case of quiet mode (reduced output), {@code false} otherwise. */ From caa0be619df9eb84df49dfb465c84d30e56dc59c Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Fri, 22 Mar 2024 06:02:47 +0100 Subject: [PATCH 93/93] adjusting to changes to Update cmdlt --- .../ide/commandlet/CreateCommandlet.java | 19 +++++++------------ .../ide/commandlet/UpdateCommandlet.java | 2 +- .../com/devonfw/tools/ide/io/FileAccess.java | 6 ++++++ .../devonfw/tools/ide/io/FileAccessImpl.java | 8 +------- 4 files changed, 15 insertions(+), 20 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/CreateCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CreateCommandlet.java index c4c9cda89..82f52b553 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/CreateCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CreateCommandlet.java @@ -66,17 +66,12 @@ public void run() { private void initializeInstance(Path newInstancePath) { - try { - FileAccess fileAccess = this.context.getFileAccess(); - fileAccess.mkdirs(newInstancePath.resolve(IdeContext.FOLDER_SOFTWARE)); - fileAccess.mkdirs(newInstancePath.resolve(IdeContext.FOLDER_UPDATES)); - fileAccess.mkdirs(newInstancePath.resolve(IdeContext.FOLDER_PLUGINS)); - fileAccess.mkdirs(newInstancePath.resolve("scripts")); // to be removed after isIdeHome is changed - if (!Files.exists(newInstancePath.resolve("setup"))) { - Files.createFile(newInstancePath.resolve("setup")); // to be removed after isIdeHome is changed - } - } catch(IOException e) { - throw new IllegalStateException("Could not initialize " + newInstancePath, e); - } + FileAccess fileAccess = this.context.getFileAccess(); + fileAccess.mkdirs(newInstancePath.resolve(IdeContext.FOLDER_SOFTWARE)); + fileAccess.mkdirs(newInstancePath.resolve(IdeContext.FOLDER_UPDATES)); + fileAccess.mkdirs(newInstancePath.resolve(IdeContext.FOLDER_PLUGINS)); + fileAccess.mkdirs(newInstancePath.resolve(IdeContext.FOLDER_WORKSPACES)); + fileAccess.mkdirs(newInstancePath.resolve(IdeContext.FOLDER_SETTINGS)); + } } diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java index 63208b2da..9e82ff29a 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java @@ -91,7 +91,7 @@ private void updateSettings() { this.context.info("Updating settings repository ..."); Path settingsPath = this.context.getSettingsPath(); - if (Files.isDirectory(settingsPath)) { + if (Files.isDirectory(settingsPath) && !this.context.getFileAccess().isEmptyDir(settingsPath)) { // perform git pull on the settings repo this.context.getGitContext().pull(settingsPath); this.context.success("Successfully updated settings repository."); diff --git a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java index 8b1260083..edcbe652d 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java +++ b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java @@ -232,4 +232,10 @@ default void extract(Path archiveFile, Path targetDir, Consumer postExtrac */ Path findExistingFile(String fileName, List searchDirs); + /** + * Checks if the given directory is empty. + * @param dir The {@link Path} object representing the directory to check. + * @return {@code true} if the directory is empty, {@code false} otherwise. + */ + boolean isEmptyDir(Path dir); } diff --git a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java index 8c67d8d64..9e3ba0f1b 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java @@ -794,16 +794,10 @@ public List listChildren(Path dir, Predicate filter) { return children; } - @Override - public List getChildrenInDir(Path dir) { - - return getChildrenInDir(dir, path -> true); - } - @Override public boolean isEmptyDir(Path dir) { - return getChildrenInDir(dir).isEmpty(); + return listChildren(dir, f -> true).isEmpty(); }