diff --git a/.editorconfig b/.editorconfig index ec98d21e1..396e79e72 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,6 +1,8 @@ +root = true + [*] charset = utf-8 -end_of_line = crlf +end_of_line = lf indent_size = 2 indent_style = space insert_final_newline = true @@ -95,7 +97,7 @@ ij_java_doc_align_param_comments = false ij_java_doc_do_not_wrap_if_one_line = true ij_java_doc_enable_formatting = true ij_java_doc_enable_leading_asterisks = true -ij_java_doc_indent_on_continuation = false +ij_java_doc_indent_on_continuation = true ij_java_doc_keep_empty_lines = true ij_java_doc_keep_empty_parameter_tag = true ij_java_doc_keep_empty_return_tag = true diff --git a/.github/workflows/check-for-updates.yml b/.github/workflows/check-for-updates.yml index b16341bad..c9bfa93d9 100644 --- a/.github/workflows/check-for-updates.yml +++ b/.github/workflows/check-for-updates.yml @@ -6,6 +6,7 @@ on: - cron: '0 2 * * *' jobs: + # Checks if new commits since 24 hours of the last call of this workflow were found and adjusts the GITHUB_OUTPUT accordingly verify_commit: runs-on: ubuntu-latest outputs: @@ -15,11 +16,12 @@ jobs: - id: verify_commit run: | if git log --since='24 hours ago' --oneline | grep '.'; then - echo "::set-output name=RUN_BUILD::true" + echo "RUN_BUILD=true" >> $GITHUB_OUTPUT else - echo "::set-output name=RUN_BUILD::false" + echo "RUN_BUILD=false" >> $GITHUB_OUTPUT fi + # Starts nightly_build workflow when new commits were found trigger_build: runs-on: ubuntu-latest needs: verify_commit diff --git a/.github/workflows/nightly-build.yml b/.github/workflows/nightly-build.yml index efb712c0d..15bf11f65 100644 --- a/.github/workflows/nightly-build.yml +++ b/.github/workflows/nightly-build.yml @@ -88,25 +88,3 @@ jobs: SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} run: mvn --settings .mvn/settings.xml -DskipTests=true -Darchetype.test.skip=true -Dmaven.install.skip=true -Dgpg.skip=true -Dstyle.color=always -B -ntp -Pdeploy deploy - - check_status: - runs-on: ubuntu-latest - steps: - - name: Check last workflow status - id: check_status - run: | - workflow_filename="nightly-build.yml" - last_workflow=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ - "https://api.github.com/repos/${{ github.repository }}/actions/workflows/$workflow_filename/runs?per_page=1" | jq -r '.workflow_runs[0]') - conclusion=$(echo $last_workflow | jq -r '.conclusion') - echo "conclusion=$conclusion" >> $GITHUB_ENV - - - name: Print and handle the status - run: | - echo "The status of the last workflow run is: ${{ env.conclusion }}" - if [ "${{ env.conclusion }}" != "success" ]; then - echo "The last workflow did not succeed. Failing this workflow." - exit 1 - else - echo "The last workflow succeeded. This workflow will succeed." - fi diff --git a/cli/src/main/java/com/devonfw/tools/ide/cli/CliArgument.java b/cli/src/main/java/com/devonfw/tools/ide/cli/CliArgument.java index 43789a27b..ed72699c0 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/cli/CliArgument.java +++ b/cli/src/main/java/com/devonfw/tools/ide/cli/CliArgument.java @@ -224,7 +224,7 @@ private void initKeyValue() { /** * @return a {@link String} representing all arguments from this {@link CliArgument} recursively along is {@link #getNext(boolean) next} arguments to the - * {@link #isEnd() end}. + * {@link #isEnd() end}. */ public String getArgs() { diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/Commandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/Commandlet.java index 3e25df1e3..037c8c5b0 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/Commandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/Commandlet.java @@ -203,9 +203,9 @@ public boolean isSuppressStepSuccess() { /** * @return {@code true} if the output of this commandlet is (potentially) processed automatically from outside, {@code false} otherwise. For example - * {@link CompleteCommandlet} logs the suggestions for auto-completion to a bash script. Also the {@link EnvironmentCommandlet} logs the environment variables - * for the {@code ide} wrapper script. In such scenarios these logs shall not be spammed with warnings like "IDE_ROOT is not set" that would break the - * processing of the output. + * {@link CompleteCommandlet} logs the suggestions for auto-completion to a bash script. Also the {@link EnvironmentCommandlet} logs the environment + * variables for the {@code ide} wrapper script. In such scenarios these logs shall not be spammed with warnings like "IDE_ROOT is not set" that would + * break the processing of the output. */ public boolean isProcessableOutput() { @@ -252,7 +252,7 @@ public String toString() { /** * @return the {@link ToolCommandlet} set in a {@link Property} of this commandlet used for auto-completion of a {@link VersionIdentifier} or {@code null} if - * not exists or not configured. + * not exists or not configured. */ public ToolCommandlet getToolForVersionCompletion() { diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManager.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManager.java index 0fde18d73..b0bf158d2 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManager.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManager.java @@ -27,7 +27,7 @@ public interface CommandletManager { /** * @param keyword the first keyword argument. * @return a {@link Commandlet} having the first {@link Property} {@link Property#isRequired() required} and a {@link KeywordProperty} with the given - * {@link Property#getName() name} or {@code null} if no such {@link Commandlet} is registered. + * {@link Property#getName() name} or {@code null} if no such {@link Commandlet} is registered. */ Commandlet getCommandletByFirstKeyword(String keyword); diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/EnvironmentCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/EnvironmentCommandlet.java index dd7fa6491..fb47a3786 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/EnvironmentCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/EnvironmentCommandlet.java @@ -1,10 +1,13 @@ package com.devonfw.tools.ide.commandlet; -import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; import java.util.stream.Collectors; import com.devonfw.tools.ide.context.AbstractIdeContext; import com.devonfw.tools.ide.context.IdeContext; +import com.devonfw.tools.ide.environment.EnvironmentVariablesType; import com.devonfw.tools.ide.environment.VariableLine; import com.devonfw.tools.ide.os.WindowsPathSyntax; import com.devonfw.tools.ide.property.FlagProperty; @@ -50,32 +53,51 @@ public boolean isProcessableOutput() { @Override public void run() { + boolean winCmd = false; WindowsPathSyntax pathSyntax = null; if (this.context.getSystemInfo().isWindows()) { if (this.bash.isTrue()) { pathSyntax = WindowsPathSyntax.MSYS; } else { + winCmd = true; pathSyntax = WindowsPathSyntax.WINDOWS; } } ((AbstractIdeContext) this.context).setPathSyntax(pathSyntax); - Collection variables = this.context.getVariables().collectVariables(); + List variables = this.context.getVariables().collectVariables(); if (this.context.debug().isEnabled()) { - for (String source : variables.stream().map(VariableLine::getSource).collect(Collectors.toSet())) { - this.context.debug("from {}:", source); - for (VariableLine line : variables) { - if (line.getSource().equals(source)) { + Map> type2lines = variables.stream().collect(Collectors.groupingBy(l -> l.getSource().type())); + for (EnvironmentVariablesType type : EnvironmentVariablesType.values()) { + List lines = type2lines.get(type); + if (lines != null) { + boolean sourcePrinted = false; + sortVariables(lines); + for (VariableLine line : lines) { + if (!sourcePrinted) { + this.context.debug("from {}:", line.getSource()); + sourcePrinted = true; + } printEnvLine(line); } } } } else { + sortVariables(variables); for (VariableLine line : variables) { - printEnvLine(line); + if (winCmd) { + // MS-Dos (aka CMD) has no concept of exported variables + this.context.info(line.getName() + "=" + line.getValue() + ""); + } else { + printEnvLine(line); + } } } } + private static void sortVariables(List lines) { + Collections.sort(lines, (c1, c2) -> c1.getName().compareTo(c2.getName())); + } + private void printEnvLine(VariableLine line) { String lineValue = line.getValue(); lineValue = "\"" + lineValue + "\""; diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/ShellCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/ShellCommandlet.java index acec62eaf..a20a08743 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/ShellCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/ShellCommandlet.java @@ -131,7 +131,7 @@ private int runCommand(String args) { * @param argument the current {@link CliArgument} (position) to match. * @param commandlet the potential {@link Commandlet} to match. * @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). + * and {@link Commandlet#validate() validated}), {@code false} otherwise (the {@link Commandlet} did not match and we have to try a different candidate). */ private boolean apply(CliArgument argument, Commandlet commandlet) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/VersionGetCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/VersionGetCommandlet.java index a4ee1185a..533054c89 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/VersionGetCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/VersionGetCommandlet.java @@ -60,8 +60,9 @@ public void run() { if (installedVersion == null) { this.context.info("No installation of tool {} was found.", commandlet.getName()); toolInstallInfo(commandlet.getName(), configuredVersion); + } else { + this.context.info(installedVersion.toString()); } - this.context.info(installedVersion.toString()); } else if (!this.installed.isTrue() && this.configured.isTrue()) {// get configured version diff --git a/cli/src/main/java/com/devonfw/tools/ide/common/JsonVersionItem.java b/cli/src/main/java/com/devonfw/tools/ide/common/JsonVersionItem.java index 4d9bee921..6b06fc7ca 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/common/JsonVersionItem.java +++ b/cli/src/main/java/com/devonfw/tools/ide/common/JsonVersionItem.java @@ -5,6 +5,9 @@ */ public interface JsonVersionItem { + /** + * @return the {@link com.devonfw.tools.ide.version.VersionIdentifier version}. + */ String version(); } 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 e8c9b7e61..53018396a 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 @@ -137,9 +137,12 @@ private static String getTool(Path path, Path ideRoot) { return null; } if (path.startsWith(ideRoot)) { - int i = ideRoot.getNameCount(); - if (path.getNameCount() > i) { - return path.getName(i).toString(); + Path relativized = ideRoot.relativize(path); + int count = relativized.getNameCount(); + if (count >= 3) { + if (relativized.getName(1).toString().equals("software")) { + return relativized.getName(2).toString(); + } } } return null; diff --git a/cli/src/main/java/com/devonfw/tools/ide/common/Tag.java b/cli/src/main/java/com/devonfw/tools/ide/common/Tag.java index bd89ac14e..4d138eef0 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/common/Tag.java +++ b/cli/src/main/java/com/devonfw/tools/ide/common/Tag.java @@ -366,7 +366,7 @@ public Tag getParent() { /** * @param i the index of the requested parent. Should be in the range from {@code 0} to - * {@link #getParentCount()}-1. + * {@link #getParentCount()}-1. * @return the requested {@link Tag}. */ public Tag getParent(int i) { @@ -404,9 +404,9 @@ public boolean isAncestorOf(Tag tag) { /** * @param tag the {@link Tag} to check. * @param includeAdditionalParents - {@code true} if {@link #getParent(int) additional parents} should be included, {@code false} otherwise (only consider - * {@link #getParent() primary parent}). + * {@link #getParent() primary parent}). * @return {@code true} if the given {@link Tag} is an ancestor of this tag, {@code false} otherwise. An ancestor is a direct or indirect - * {@link #getParent() parent}. Therefore, if {@link #ROOT} is given as {@link Tag} parameter, this method should always return {@code true}. + * {@link #getParent() parent}. Therefore, if {@link #ROOT} is given as {@link Tag} parameter, this method should always return {@code true}. */ public boolean isAncestorOf(Tag tag, boolean includeAdditionalParents) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/common/Tags.java b/cli/src/main/java/com/devonfw/tools/ide/common/Tags.java index 08b96b029..9ce58a40e 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/common/Tags.java +++ b/cli/src/main/java/com/devonfw/tools/ide/common/Tags.java @@ -9,7 +9,7 @@ public interface Tags { /** * @return a {@link Set} with the tags classifying this object. E.g. for mvn (maven) the tags {@link Tag#JAVA java} and {@link Tag#BUILD build} could be - * associated. + * associated. */ Set getTags(); 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 6f4814c5c..b2d2c93ba 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 @@ -140,8 +140,8 @@ public abstract class AbstractIdeContext implements IdeContext { * @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. + * @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) { @@ -320,7 +320,7 @@ public boolean isMock() { return false; } - private SystemPath computeSystemPath() { + protected SystemPath computeSystemPath() { return new SystemPath(this); } @@ -352,7 +352,7 @@ private Path getParentPath(Path dir) { private EnvironmentVariables createVariables() { - AbstractEnvironmentVariables system = EnvironmentVariables.ofSystem(this); + AbstractEnvironmentVariables system = createSystemVariables(); 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? @@ -361,7 +361,12 @@ private EnvironmentVariables createVariables() { return conf.resolved(); } - private AbstractEnvironmentVariables extendVariables(AbstractEnvironmentVariables envVariables, Path propertiesPath, EnvironmentVariablesType type) { + protected AbstractEnvironmentVariables createSystemVariables() { + + return EnvironmentVariables.ofSystem(this); + } + + protected AbstractEnvironmentVariables extendVariables(AbstractEnvironmentVariables envVariables, Path propertiesPath, EnvironmentVariablesType type) { Path propertiesFile = null; if (propertiesPath == null) { @@ -894,9 +899,9 @@ public int run(CliArguments arguments) { /** * @param cmd the potential {@link Commandlet} to {@link #apply(CliArguments, Commandlet, CompletionCandidateCollector) apply} and - * {@link Commandlet#run() run}. + * {@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). + * {@link Commandlet} did not match and we have to try a different candidate). */ private boolean applyAndRun(CliArguments arguments, Commandlet cmd) { @@ -966,11 +971,11 @@ public List complete(CliArguments arguments, boolean includ /** * @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. + * {@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). + * 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) { 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 79f0b70f8..15f10944c 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 @@ -20,8 +20,8 @@ public interface GitContext { * * @param repoUrl the git remote URL to clone from. * @param branch the explicit name of the branch to checkout e.g. "main" or {@code null} to use the default branch. - * @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 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. * @throws CliOfflineException if offline and cloning is needed. */ void pullOrCloneIfNeeded(String repoUrl, String branch, Path targetRepository); @@ -30,8 +30,8 @@ public interface GitContext { * Attempts a git pull and reset if required. * * @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. + * @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. * @throws CliOfflineException if offline and cloning is needed. */ default void pullOrCloneAndResetIfNeeded(String repoUrl, Path targetRepository) { @@ -43,8 +43,8 @@ default void pullOrCloneAndResetIfNeeded(String repoUrl, Path targetRepository) * Attempts a git pull and reset if required. * * @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. + * @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 branch the explicit name of the branch to checkout e.g. "main" or {@code null} to use the default branch. * @throws CliOfflineException if offline and cloning is needed. */ @@ -57,8 +57,8 @@ default void pullOrCloneAndResetIfNeeded(String repoUrl, Path targetRepository, * Attempts a git pull and reset if required. * * @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. + * @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 branch the explicit name of the branch to checkout e.g. "main" or {@code null} to use the default branch. * @param remoteName the remote name e.g. origin. * @throws CliOfflineException if offline and cloning is needed. @@ -69,8 +69,8 @@ default void pullOrCloneAndResetIfNeeded(String repoUrl, Path targetRepository, * Runs a git pull or a git clone. * * @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. + * @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. * @throws CliOfflineException if offline and cloning is needed. */ void pullOrClone(String gitRepoUrl, Path targetRepository); @@ -80,8 +80,8 @@ default void pullOrCloneAndResetIfNeeded(String repoUrl, Path targetRepository, * * @param gitRepoUrl the git remote URL to clone from. * @param branch the explicit name of the branch to checkout e.g. "main" or {@code null} to use the default branch. - * @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 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. * @throws CliOfflineException if offline and cloning is needed. */ void pullOrClone(String gitRepoUrl, String branch, Path targetRepository); @@ -90,8 +90,8 @@ default void pullOrCloneAndResetIfNeeded(String repoUrl, Path targetRepository, * Runs a git clone. * * @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. + * @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. * @throws CliOfflineException if offline and cloning is needed. */ void clone(GitUrl gitRepoUrl, Path targetRepository); @@ -99,16 +99,16 @@ default void pullOrCloneAndResetIfNeeded(String repoUrl, Path targetRepository, /** * Runs a git pull. * - * @param targetRepository the {@link Path} to the target folder where the git repository should be cloned or pulled. It is not the parent directory where git - * will by default create a sub-folder by default on clone but the * final folder that will contain the ".git" subfolder. + * @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 pull(Path targetRepository); /** * Runs a git diff-index to detect local changes and if so reverts them via git reset. * - * @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 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. */ default void reset(Path targetRepository) { @@ -118,8 +118,8 @@ default void reset(Path targetRepository) { /** * Runs a git diff-index to detect local changes and if so reverts them via git reset. * - * @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 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 branch the explicit name of the branch to checkout e.g. "main" or {@code null} to use the default branch. */ default void reset(Path targetRepository, String branch) { @@ -130,8 +130,8 @@ default void reset(Path targetRepository, String branch) { /** * Runs a git reset reverting all local changes to the git repository. * - * @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 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 branch the explicit name of the branch to checkout e.g. "main" or {@code null} to use the default branch. * @param remoteName the name of the git remote e.g. "origin". */ @@ -140,8 +140,8 @@ default void reset(Path targetRepository, String branch) { /** * 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. + * @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 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 00d1e503f..b224a37ad 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 @@ -120,8 +120,8 @@ public void pullOrClone(String gitRepoUrl, String branch, 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. + * @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) { 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 d0f174184..f0251e19d 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 @@ -268,7 +268,7 @@ default void requireOnline(String purpose) { /** * @return the {@link Path} to the IDE instance directory. You can have as many IDE instances on the same computer as independent tenants for different - * isolated projects. + * isolated projects. * @see com.devonfw.tools.ide.variable.IdeVariables#IDE_HOME */ Path getIdeHome(); @@ -281,8 +281,8 @@ default void requireOnline(String purpose) { /** * @return the {@link Path} to the IDE installation root directory. This is the top-level folder where the {@link #getIdeHome() IDE instances} are located as - * sub-folder. There is a reserved ".ide" folder where central IDE data is stored such as the {@link #getUrlsPath() download metadata} and the central - * software repository. + * sub-folder. There is a reserved ".ide" folder where central IDE data is stored such as the {@link #getUrlsPath() download metadata} and the central + * software repository. * @see com.devonfw.tools.ide.variable.IdeVariables#IDE_ROOT */ Path getIdeRoot(); @@ -304,7 +304,7 @@ default void requireOnline(String purpose) { /** * @return the {@link Path} to the download metadata (ide-urls). Here a git repository is cloned and updated (pulled) to always have the latest metadata to - * download tools. + * download tools. * @see com.devonfw.tools.ide.url.model.folder.UrlRepository */ Path getUrlsPath(); @@ -316,43 +316,43 @@ default void requireOnline(String purpose) { /** * @return the {@link Path} to the download cache. All downloads will be placed here using a unique naming pattern that allows to reuse these artifacts. So if - * the same artifact is requested again it will be taken from the cache to avoid downloading it again. + * the same artifact is requested again it will be taken from the cache to avoid downloading it again. */ Path getDownloadPath(); /** * @return the {@link Path} to the software folder inside {@link #getIdeHome() IDE_HOME}. All tools for that IDE instance will be linked here from the - * {@link #getSoftwareRepositoryPath() software repository} as sub-folder named after the according tool. + * {@link #getSoftwareRepositoryPath() software repository} as sub-folder named after the according tool. */ Path getSoftwarePath(); /** * @return the {@link Path} to the extra folder inside software folder inside {@link #getIdeHome() IDE_HOME}. All tools for that IDE instance will be linked - * here from the {@link #getSoftwareRepositoryPath() software repository} as sub-folder named after the according tool. + * here from the {@link #getSoftwareRepositoryPath() software repository} as sub-folder named after the according tool. */ Path getSoftwareExtraPath(); /** * @return the {@link Path} to the global software repository. This is the central directory where the tools are extracted physically on the local disc. Those - * are shared among all IDE instances (see {@link #getIdeHome() IDE_HOME}) via symbolic links (see {@link #getSoftwarePath()}). Therefore this repository - * follows the sub-folder structure {@code «repository»/«tool»/«edition»/«version»/}. So multiple versions of the same tool exist here as different folders. - * Further, such software may not be modified so e.g. installation of plugins and other kind of changes to such tool need to happen strictly out of the scope - * of this folders. + * are shared among all IDE instances (see {@link #getIdeHome() IDE_HOME}) via symbolic links (see {@link #getSoftwarePath()}). Therefore this repository + * follows the sub-folder structure {@code «repository»/«tool»/«edition»/«version»/}. So multiple versions of the same tool exist here as different + * folders. Further, such software may not be modified so e.g. installation of plugins and other kind of changes to such tool need to happen strictly out + * of the scope of this folders. */ Path getSoftwareRepositoryPath(); /** * @return the {@link Path} to the {@link #FOLDER_PLUGINS plugins folder} inside {@link #getIdeHome() IDE_HOME}. All plugins of the IDE instance will be - * stored here. For each tool that supports plugins a sub-folder with the tool name will be created where the plugins for that tool get installed. + * stored here. For each tool that supports plugins a sub-folder with the tool name will be created where the plugins for that tool get installed. */ Path getPluginsPath(); /** * @return the {@link Path} to the central tool repository. All tools will be installed in this location using the directory naming schema of - * {@code «repository»/«tool»/«edition»/«version»/}. Actual {@link #getIdeHome() IDE instances} will only contain symbolic links to the physical tool - * installations in this repository. This allows to share and reuse tool installations across multiple {@link #getIdeHome() IDE instances}. The variable - * {@code «repository»} is typically {@code default} for the tools from our standard {@link #getUrlsPath() ide-urls download metadata} but this will differ - * for custom tools from a private repository. + * {@code «repository»/«tool»/«edition»/«version»/}. Actual {@link #getIdeHome() IDE instances} will only contain symbolic links to the physical tool + * installations in this repository. This allows to share and reuse tool installations across multiple {@link #getIdeHome() IDE instances}. The variable + * {@code «repository»} is typically {@code default} for the tools from our standard {@link #getUrlsPath() ide-urls download metadata} but this will + * differ for custom tools from a private repository. */ Path getToolRepositoryPath(); @@ -372,9 +372,28 @@ default void requireOnline(String purpose) { */ Path getSettingsPath(); + /** + * @return the {@link Path} to the templates folder inside the {@link #getSettingsPath() settings}. The relative directory structure in this templates folder + * is to be applied to {@link #getIdeHome() IDE_HOME} when the project is set up. + */ + default Path getSettingsTemplatePath() { + Path settingsFolder = getSettingsPath(); + Path templatesFolder = settingsFolder.resolve(IdeContext.FOLDER_TEMPLATES); + if (!Files.isDirectory(templatesFolder)) { + Path templatesFolderLegacy = settingsFolder.resolve(IdeContext.FOLDER_LEGACY_TEMPLATES); + if (Files.isDirectory(templatesFolderLegacy)) { + templatesFolder = templatesFolderLegacy; + } else { + warning("No templates found in settings git repo neither in {} nor in {} - configuration broken", templatesFolder, templatesFolderLegacy); + return null; + } + } + return templatesFolder; + } + /** * @return the {@link Path} to the {@code conf} folder with instance specific tool configurations and the - * {@link EnvironmentVariablesType#CONF user specific project configuration}. + * {@link EnvironmentVariablesType#CONF user specific project configuration}. */ Path getConfPath(); @@ -391,7 +410,7 @@ default void requireOnline(String purpose) { /** * @return the value of the system {@link IdeVariables#PATH PATH} variable. It is automatically extended according to the tools available in - * {@link #getSoftwarePath() software path} unless {@link #getIdeHome() IDE_HOME} was not found. + * {@link #getSoftwarePath() software path} unless {@link #getIdeHome() IDE_HOME} was not found. */ SystemPath getPath(); @@ -439,13 +458,11 @@ default String getMavenArgs() { if (getIdeHome() == null) { return null; } - Path confFolder = getConfPath(); - Path mvnSettingsFile = confFolder.resolve(Mvn.MVN_CONFIG_FOLDER).resolve(Mvn.SETTINGS_FILE); + Mvn mvn = getCommandletManager().getCommandlet(Mvn.class); + Path mavenConfFolder = mvn.getMavenConfFolder(false); + Path mvnSettingsFile = mavenConfFolder.resolve(Mvn.SETTINGS_FILE); if (!Files.exists(mvnSettingsFile)) { - mvnSettingsFile = confFolder.resolve(Mvn.MVN_CONFIG_LEGACY_FOLDER).resolve(Mvn.SETTINGS_FILE); - if (!Files.exists(mvnSettingsFile)) { - return null; - } + return null; } String settingsPath; WindowsPathSyntax pathSyntax = getPathSyntax(); @@ -458,12 +475,23 @@ default String getMavenArgs() { } /** - * @return the String value for the variable M2_REPO, or null if called outside an IDEasy installation. + * @return the String value for the variable M2_REPO, or falls back to the default USER_HOME/.m2 location if called outside an IDEasy installation. */ - default Path getMavenRepoEnvVariable() { + default Path getMavenRepository() { if (getIdeHome() != null) { - return getConfPath().resolve(Mvn.MVN_CONFIG_LEGACY_FOLDER).resolve("repository"); + Path confPath = getConfPath(); + Path m2Folder = confPath.resolve(Mvn.MVN_CONFIG_FOLDER); + if (!Files.isDirectory(m2Folder)) { + Path m2LegacyFolder = confPath.resolve(Mvn.MVN_CONFIG_LEGACY_FOLDER); + if (Files.isDirectory(m2LegacyFolder)) { + m2Folder = m2LegacyFolder; + } else { + // fallback to USER_HOME/.m2 folder + m2Folder = getUserHome().resolve(Mvn.MVN_CONFIG_LEGACY_FOLDER); + } + } + return m2Folder.resolve("repository"); } return null; } diff --git a/cli/src/main/java/com/devonfw/tools/ide/environment/AbstractEnvironmentVariables.java b/cli/src/main/java/com/devonfw/tools/ide/environment/AbstractEnvironmentVariables.java index dae728e2b..3ba749123 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/environment/AbstractEnvironmentVariables.java +++ b/cli/src/main/java/com/devonfw/tools/ide/environment/AbstractEnvironmentVariables.java @@ -2,7 +2,6 @@ import java.nio.file.Path; import java.util.ArrayList; -import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -39,7 +38,7 @@ public abstract class AbstractEnvironmentVariables implements EnvironmentVariabl */ protected final IdeContext context; - private String source; + private VariableSource source; /** * The constructor. @@ -74,14 +73,10 @@ public Path getPropertiesFilePath() { } @Override - public String getSource() { + public VariableSource getSource() { if (this.source == null) { - this.source = getType().toString(); - Path propertiesPath = getPropertiesFilePath(); - if (propertiesPath != null) { - this.source = this.source + "@" + propertiesPath; - } + this.source = new VariableSource(getType(), getPropertiesFilePath()); } return this.source; } @@ -101,44 +96,46 @@ protected boolean isExported(String name) { } @Override - public final Collection collectVariables() { + public final List collectVariables() { return collectVariables(false); } @Override - public final Collection collectExportedVariables() { + public final List collectExportedVariables() { return collectVariables(true); } - private final Collection collectVariables(boolean onlyExported) { + private final List collectVariables(boolean onlyExported) { - Map variableNames = new HashMap<>(); - collectVariables(variableNames); - List variables = new ArrayList<>(variableNames.size()); - for (String name : variableNames.keySet()) { - boolean export = isExported(name); - if (!onlyExported || export) { - String value = get(name, false); - if (value != null) { - variables.add(VariableLine.of(export, name, value, variableNames.get(name))); - } - } - } - return variables; + Map variables = new HashMap<>(); + collectVariables(variables, onlyExported, this); + return new ArrayList<>(variables.values()); } /** * @param variables the {@link Map} where to add the names of the variables defined here as keys, and their corresponding source as value. */ - protected void collectVariables(Map variables) { + protected void collectVariables(Map variables, boolean onlyExported, AbstractEnvironmentVariables resolver) { if (this.parent != null) { - this.parent.collectVariables(variables); + this.parent.collectVariables(variables, onlyExported, resolver); } } + protected VariableLine createVariableLine(String name, boolean onlyExported, AbstractEnvironmentVariables resolver) { + + boolean export = resolver.isExported(name); + if (!onlyExported || export) { + String value = resolver.get(name, false); + if (value != null) { + return VariableLine.of(export, name, value, getSource()); + } + } + return null; + } + /** * @param propertiesFilePath the {@link #getPropertiesFilePath() propertiesFilePath} of the child {@link EnvironmentVariables}. * @param type the {@link #getType() type}. @@ -171,19 +168,19 @@ public String resolve(String string, Object source, boolean legacySupport) { /** * This method is called recursively. This allows you to resolve variables that are defined by other variables. * - * @param value the {@link String} that potentially contains variables in the syntax "${«variable«}". Those will be resolved by this method and replaced with - * their {@link #get(String) value}. - * @param source the source where the {@link String} to resolve originates from. Should have a reasonable {@link Object#toString() string representation} that - * will be used in error or log messages if a variable could not be resolved. + * @param value the {@link String} that potentially contains variables in the syntax "${«variable«}". Those will be resolved by this method and replaced + * with their {@link #get(String) value}. + * @param source the source where the {@link String} to resolve originates from. Should have a reasonable {@link Object#toString() string representation} + * that will be used in error or log messages if a variable could not be resolved. * @param recursion the current recursion level. This is used to interrupt endless recursion. * @param resolvedVars this is a reference to an object of {@link EnvironmentVariablesResolved} being the lowest level in the - * {@link EnvironmentVariablesType hierarchy} of variables. In case of a self-referencing variable {@code x} the resolving has to continue one level higher in - * the {@link EnvironmentVariablesType hierarchy} to avoid endless recursion. The {@link EnvironmentVariablesResolved} is then used if another variable - * {@code y} must be resolved, since resolving this variable has to again start at the lowest level. For example: For levels {@code l1, l2} with - * {@code l1 < l2} and {@code x=${x} foo} and {@code y=bar} defined at level {@code l1} and {@code x=test ${y}} defined at level {@code l2}, {@code x} is - * first resolved at level {@code l1} and then up the {@link EnvironmentVariablesType hierarchy} at {@code l2} to avoid endless recursion. However, {@code y} - * must be resolved starting from the lowest level in the {@link EnvironmentVariablesType hierarchy} and therefore {@link EnvironmentVariablesResolved} is - * used. + * {@link EnvironmentVariablesType hierarchy} of variables. In case of a self-referencing variable {@code x} the resolving has to continue one level + * higher in the {@link EnvironmentVariablesType hierarchy} to avoid endless recursion. The {@link EnvironmentVariablesResolved} is then used if another + * variable {@code y} must be resolved, since resolving this variable has to again start at the lowest level. For example: For levels {@code l1, l2} with + * {@code l1 < l2} and {@code x=${x} foo} and {@code y=bar} defined at level {@code l1} and {@code x=test ${y}} defined at level {@code l2}, {@code x} is + * first resolved at level {@code l1} and then up the {@link EnvironmentVariablesType hierarchy} at {@code l2} to avoid endless recursion. However, + * {@code y} must be resolved starting from the lowest level in the {@link EnvironmentVariablesType hierarchy} and therefore + * {@link EnvironmentVariablesResolved} is used. * @param context the {@link ResolveContext}. * @return the given {@link String} with the variables resolved. */ @@ -271,7 +268,7 @@ private String resolveWithSyntax(final String value, final Object src, final int * * @param name the name of the variable to get. * @param ignoreDefaultValue - {@code true} if the {@link VariableDefinition#getDefaultValue(IdeContext) default value} of a potential - * {@link VariableDefinition} shall be ignored, {@code false} to return default instead of {@code null}. + * {@link VariableDefinition} shall be ignored, {@code false} to return default instead of {@code null}. * @return the value of the variable. */ protected String getValue(String name, boolean ignoreDefaultValue) { @@ -326,7 +323,7 @@ public String inverseResolve(String string, Object src, VariableSyntax syntax) { @Override public String toString() { - return getSource(); + return getSource().toString(); } /** diff --git a/cli/src/main/java/com/devonfw/tools/ide/environment/EnvironmentVariables.java b/cli/src/main/java/com/devonfw/tools/ide/environment/EnvironmentVariables.java index 9d1c7ff04..2c6dd1b7f 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/environment/EnvironmentVariables.java +++ b/cli/src/main/java/com/devonfw/tools/ide/environment/EnvironmentVariables.java @@ -2,6 +2,7 @@ import java.nio.file.Path; import java.util.Collection; +import java.util.List; import java.util.Locale; import com.devonfw.tools.ide.context.IdeContext; @@ -32,7 +33,7 @@ default String get(String name) { /** * @param name the name of the environment variable to get. * @param ignoreDefaultValue - {@code true} if the {@link VariableDefinition#getDefaultValue(IdeContext) default value} of a potential - * {@link VariableDefinition} shall be ignored, {@code false} to return default instead of {@code null}. + * {@link VariableDefinition} shall be ignored, {@code false} to return default instead of {@code null}. * @return the value of the variable with the given {@code name}. Will be {@code null} if no such variable is defined. */ default String get(String name, boolean ignoreDefaultValue) { @@ -63,7 +64,7 @@ default Path getPath(String name) { /** * @param name the name of the environment variable to get. * @return the value of the variable with the given {@code name} without {@link #getParent() inheritance from parent}. Will be {@code null} if no such - * variable is defined. + * variable is defined. */ String getFlat(String name); @@ -84,7 +85,7 @@ default String getToolEdition(String tool) { /** * @param tool the name of the tool (e.g. "java"). * @return the {@link VersionIdentifier} with the version of the tool to use. May also be a {@link VersionIdentifier#isPattern() version pattern}. Will be - * {@link VersionIdentifier#LATEST} if undefined. + * {@link VersionIdentifier#LATEST} if undefined. */ default VersionIdentifier getToolVersion(String tool) { @@ -104,7 +105,7 @@ default VersionIdentifier getToolVersion(String tool) { /** * @param type the {@link #getType() type} of the requested {@link EnvironmentVariables}. * @return the {@link EnvironmentVariables} with the given {@link #getType() type} from this {@link EnvironmentVariables} along the - * {@link #getParent() parent} hierarchy or {@code null} if not found. + * {@link #getParent() parent} hierarchy or {@code null} if not found. */ default EnvironmentVariables getByType(EnvironmentVariablesType type) { @@ -121,18 +122,18 @@ default EnvironmentVariables getByType(EnvironmentVariablesType type) { /** * @return the {@link Path} to the underlying properties file or {@code null} if not based on such file (e.g. for EVS or - * {@link EnvironmentVariablesResolved}). + * {@link EnvironmentVariablesResolved}). */ Path getPropertiesFilePath(); /** - * @return the source identifier describing this {@link EnvironmentVariables} for debugging. + * @return the {@link VariableSource} of this {@link EnvironmentVariables}. */ - String getSource(); + VariableSource getSource(); /** * @return the parent {@link EnvironmentVariables} to inherit from or {@code null} if this is the {@link EnvironmentVariablesType#SYSTEM root} - * {@link EnvironmentVariables} instance. + * {@link EnvironmentVariables} instance. */ default EnvironmentVariables getParent() { @@ -161,7 +162,7 @@ default void save() { /** * @param name the {@link com.devonfw.tools.ide.variable.VariableDefinition#getName() name} of the variable to search for. * @return the closest {@link EnvironmentVariables} instance that defines the variable with the given {@code name} or {@code null} if the variable is not - * defined. + * defined. */ default EnvironmentVariables findVariable(String name) { @@ -180,19 +181,19 @@ default EnvironmentVariables findVariable(String name) { /** * @return the {@link Collection} of the {@link VariableLine}s defined by this {@link EnvironmentVariables} including inheritance. */ - Collection collectVariables(); + List collectVariables(); /** * @return the {@link Collection} of the {@link VariableLine#isExport() exported} {@link VariableLine}s defined by this {@link EnvironmentVariables} including - * inheritance. + * inheritance. */ - Collection collectExportedVariables(); + List collectExportedVariables(); /** * @param string the {@link String} that potentially contains variables in {@link VariableSyntax#CURLY} ("${«variable«}"). Those will be resolved by this - * method and replaced with their {@link #get(String) value}. - * @param source the source where the {@link String} to resolve originates from. Should have a reasonable {@link Object#toString() string representation} that - * will be used in error or log messages if a variable could not be resolved. + * method and replaced with their {@link #get(String) value}. + * @param source the source where the {@link String} to resolve originates from. Should have a reasonable {@link Object#toString() string representation} + * that will be used in error or log messages if a variable could not be resolved. * @return the given {@link String} with the variables resolved. * @see com.devonfw.tools.ide.tool.ide.IdeToolCommandlet */ @@ -200,9 +201,9 @@ default EnvironmentVariables findVariable(String name) { /** * @param string the {@link String} that potentially contains variables in {@link VariableSyntax}. Those will be resolved by this method and replaced with - * their {@link #get(String) value}. - * @param source the source where the {@link String} to resolve originates from. Should have a reasonable {@link Object#toString() string representation} that - * will be used in error or log messages if a variable could not be resolved. + * their {@link #get(String) value}. + * @param source the source where the {@link String} to resolve originates from. Should have a reasonable {@link Object#toString() string representation} + * that will be used in error or log messages if a variable could not be resolved. * @param legacySupport * @return the given {@link String} with the variables resolved. * @see com.devonfw.tools.ide.tool.ide.IdeToolCommandlet @@ -215,11 +216,11 @@ default EnvironmentVariables findVariable(String name) { * {@link #get(String) variable value}. This method does its best to implement the inverse resolution based on some heuristics. * * @param string the {@link String} where to find {@link #get(String) variable values} and replace them with according - * {@link com.devonfw.tools.ide.variable.VariableSyntax} expressions. + * {@link com.devonfw.tools.ide.variable.VariableSyntax} expressions. * @param source the source where the {@link String} to inverse resolve originates from. Should have a reasonable - * {@link Object#toString() string representation} that will be used in error or log messages if the inverse resolving was not working as expected. + * {@link Object#toString() string representation} that will be used in error or log messages if the inverse resolving was not working as expected. * @return the given {@link String} with {@link #get(String) variable values} replaced with according {@link com.devonfw.tools.ide.variable.VariableSyntax} - * expressions. + * expressions. * @see com.devonfw.tools.ide.tool.ide.IdeToolCommandlet */ default String inverseResolve(String string, Object source) { @@ -229,12 +230,12 @@ default String inverseResolve(String string, Object source) { /** * @param string the {@link String} where to find {@link #get(String) variable values} and replace them with according - * {@link com.devonfw.tools.ide.variable.VariableSyntax} expressions. + * {@link com.devonfw.tools.ide.variable.VariableSyntax} expressions. * @param source the source where the {@link String} to inverse resolve originates from. Should have a reasonable - * {@link Object#toString() string representation} that will be used in error or log messages if the inverse resolving was not working as expected. + * {@link Object#toString() string representation} that will be used in error or log messages if the inverse resolving was not working as expected. * @param syntax the explicit {@link VariableSyntax} to use. * @return the given {@link String} with {@link #get(String) variable values} replaced with according {@link com.devonfw.tools.ide.variable.VariableSyntax} - * expressions. + * expressions. * @see #inverseResolve(String, Object) */ String inverseResolve(String string, Object source, VariableSyntax syntax); diff --git a/cli/src/main/java/com/devonfw/tools/ide/environment/EnvironmentVariablesPropertiesFile.java b/cli/src/main/java/com/devonfw/tools/ide/environment/EnvironmentVariablesPropertiesFile.java index 6413a15bc..a63f50285 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/environment/EnvironmentVariablesPropertiesFile.java +++ b/cli/src/main/java/com/devonfw/tools/ide/environment/EnvironmentVariablesPropertiesFile.java @@ -21,7 +21,7 @@ /** * Implementation of {@link EnvironmentVariables}. */ -final class EnvironmentVariablesPropertiesFile extends EnvironmentVariablesMap { +public final class EnvironmentVariablesPropertiesFile extends EnvironmentVariablesMap { private static final String NEWLINE = "\n"; @@ -40,11 +40,10 @@ final class EnvironmentVariablesPropertiesFile extends EnvironmentVariablesMap { * * @param parent the parent {@link EnvironmentVariables} to inherit from. * @param type the {@link #getType() type}. - * @param source the {@link #getSource() source}. + * @param propertiesFilePath the {@link #getSource() source}. * @param context the {@link IdeContext}. - * @param variables the underlying variables. */ - EnvironmentVariablesPropertiesFile(AbstractEnvironmentVariables parent, EnvironmentVariablesType type, + public EnvironmentVariablesPropertiesFile(AbstractEnvironmentVariables parent, EnvironmentVariablesType type, Path propertiesFilePath, IdeContext context) { super(parent, context); @@ -73,7 +72,7 @@ private void load() { do { line = reader.readLine(); if (line != null) { - VariableLine variableLine = VariableLine.of(line, this.context, this.propertiesFilePath); + VariableLine variableLine = VariableLine.of(line, this.context, getSource()); String name = variableLine.getName(); if (name != null) { variableLine = migrateLine(variableLine, false); @@ -110,7 +109,7 @@ public void save() { do { line = reader.readLine(); if (line != null) { - VariableLine variableLine = VariableLine.of(line, this.context, reader); + VariableLine variableLine = VariableLine.of(line, this.context, getSource()); lines.add(variableLine); } } while (line != null); @@ -189,13 +188,12 @@ protected Map getVariables() { } @Override - protected void collectVariables(Map variableNames) { + protected void collectVariables(Map variables, boolean onlyExported, AbstractEnvironmentVariables resolver) { for (String key : this.variables.keySet()) { - variableNames.put(key, this.propertiesFilePath.toString()); + variables.computeIfAbsent(key, k -> createVariableLine(key, onlyExported, resolver)); } - - super.collectVariables(variableNames); + super.collectVariables(variables, onlyExported, resolver); } @Override diff --git a/cli/src/main/java/com/devonfw/tools/ide/environment/EnvironmentVariablesResolved.java b/cli/src/main/java/com/devonfw/tools/ide/environment/EnvironmentVariablesResolved.java index 5fe2bd426..3b0ca32d8 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/environment/EnvironmentVariablesResolved.java +++ b/cli/src/main/java/com/devonfw/tools/ide/environment/EnvironmentVariablesResolved.java @@ -49,14 +49,14 @@ public EnvironmentVariables resolved() { } @Override - protected void collectVariables(Map variables) { + protected void collectVariables(Map variables, boolean onlyExported, AbstractEnvironmentVariables resolver) { for (VariableDefinition var : IdeVariables.VARIABLES) { if (var.isExport() || var.isForceDefaultValue()) { - variables.put(var.getName(), "defaults"); + variables.computeIfAbsent(var.getName(), k -> createVariableLine(k, onlyExported, resolver)); } } - super.collectVariables(variables); + super.collectVariables(variables, onlyExported, resolver); } @Override diff --git a/cli/src/main/java/com/devonfw/tools/ide/environment/VariableLine.java b/cli/src/main/java/com/devonfw/tools/ide/environment/VariableLine.java index cfc99daf2..6ae0b19dc 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/environment/VariableLine.java +++ b/cli/src/main/java/com/devonfw/tools/ide/environment/VariableLine.java @@ -41,9 +41,9 @@ public String getComment() { } /** - * @return the source of the variable. + * @return the {@link VariableSource} of the variable. */ - public String getSource() { + public VariableSource getSource() { return null; } @@ -85,14 +85,14 @@ static final class Variable extends VariableLine { private final String line; - private final Object source; + private final VariableSource source; Variable(boolean export, String name, String value) { this(export, name, value, null, null); } - private Variable(boolean export, String name, String value, String line, Object source) { + private Variable(boolean export, String name, String value, String line, VariableSource source) { super(); this.export = export; @@ -132,8 +132,8 @@ public String getValue() { } @Override - public String getSource() { - return source.toString(); + public VariableSource getSource() { + return source; } @Override @@ -239,7 +239,7 @@ public String toString() { * @param source the source where the given {@link String} to parse is from (e.g. the file path). * @return the parsed {@link VariableLine}. */ - public static VariableLine of(String line, IdeLogger logger, Object source) { + public static VariableLine of(String line, IdeLogger logger, VariableSource source) { int len = line.length(); int start = 0; @@ -311,7 +311,7 @@ public static VariableLine of(boolean export, String name, String value) { * @param source the {@link #getSource() source} of the variable. * @return the {@link VariableLine} for the given values. */ - public static VariableLine of(boolean export, String name, String value, Object source) { + public static VariableLine of(boolean export, String name, String value, VariableSource source) { return new Variable(export, name, value, null, source); } diff --git a/cli/src/main/java/com/devonfw/tools/ide/environment/VariableSource.java b/cli/src/main/java/com/devonfw/tools/ide/environment/VariableSource.java new file mode 100644 index 000000000..d88051bf5 --- /dev/null +++ b/cli/src/main/java/com/devonfw/tools/ide/environment/VariableSource.java @@ -0,0 +1,22 @@ +package com.devonfw.tools.ide.environment; + +import java.nio.file.Path; + +/** + * {@link Record} for the {@link EnvironmentVariables#getSource() source} of {@link EnvironmentVariables} or {@link VariableLine}. + * + * @param type the {@link EnvironmentVariablesType}. + * @param properties the optional {@link Path} to the properties file in case the {@link EnvironmentVariables#getSource() source} is from a properties + * file. + */ +public record VariableSource(EnvironmentVariablesType type, Path properties) { + + @Override + public String toString() { + if (this.properties != null) { + return this.type + "@" + this.properties; + } + return this.type.toString(); + } + +} 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 0d4f965c5..959351c42 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 @@ -22,7 +22,8 @@ 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. + * @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); @@ -36,7 +37,7 @@ public interface FileAccess { /** * @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). + * directory). */ boolean isFile(Path file); @@ -91,8 +92,8 @@ default void symlink(Path source, Path targetLink) { /** * @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}. + * @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) { @@ -101,13 +102,13 @@ default void copy(Path source, Path target) { /** * @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 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). + * otherwise (copy recursively). */ void copy(Path source, Path target, FileCopyMode fileOnly); @@ -222,7 +223,7 @@ default void extract(Path archiveFile, Path targetDir, Consumer postExtrac * @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. + * directory. */ List listChildren(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 537e25b3f..3e7e60780 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 @@ -576,7 +576,7 @@ private void postExtractHook(Consumer postExtractHook, Path properInstallD /** * @param path the {@link Path} to start the recursive search from. * @return the deepest subdir {@code s} of the passed path such that all directories between {@code s} and the passed path (including {@code s}) are the sole - * item in their respective directory and {@code s} is not named "bin". + * item in their respective directory and {@code s} is not named "bin". */ private Path getProperInstallationSubDirOf(Path path, Path archiveFile) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/io/FileCopyMode.java b/cli/src/main/java/com/devonfw/tools/ide/io/FileCopyMode.java index b4d4d8b41..cbf8d32bf 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/io/FileCopyMode.java +++ b/cli/src/main/java/com/devonfw/tools/ide/io/FileCopyMode.java @@ -31,7 +31,7 @@ public enum FileCopyMode { /** * @return {@code true} if only a single file shall be copied. Will fail if a directory is given to copy, {@code false} otherwise (to copy folders - * recursively). + * recursively). */ public boolean isFileOnly() { diff --git a/cli/src/main/java/com/devonfw/tools/ide/log/IdeSubLogger.java b/cli/src/main/java/com/devonfw/tools/ide/log/IdeSubLogger.java index 4aac12580..a3caf808c 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/log/IdeSubLogger.java +++ b/cli/src/main/java/com/devonfw/tools/ide/log/IdeSubLogger.java @@ -43,7 +43,7 @@ default String log(Throwable error, String message) { /** * @return {@code true} if this logger is enabled, {@code false} otherwise (this logger does nothing and all {@link #log(String) logged messages} with be - * ignored). + * ignored). */ boolean isEnabled(); diff --git a/cli/src/main/java/com/devonfw/tools/ide/merge/xmlmerger/XmlMerger.java b/cli/src/main/java/com/devonfw/tools/ide/merge/xmlmerger/XmlMerger.java index 78576be45..c4205c6de 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/merge/xmlmerger/XmlMerger.java +++ b/cli/src/main/java/com/devonfw/tools/ide/merge/xmlmerger/XmlMerger.java @@ -26,6 +26,9 @@ import com.devonfw.tools.ide.merge.xmlmerger.matcher.ElementMatcher; import com.devonfw.tools.ide.merge.xmlmerger.model.MergeElement; +/** + * {@link FileMerger} for XML files. + */ public class XmlMerger extends FileMerger { private static final DocumentBuilder DOCUMENT_BUILDER; diff --git a/cli/src/main/java/com/devonfw/tools/ide/process/ProcessContext.java b/cli/src/main/java/com/devonfw/tools/ide/process/ProcessContext.java index 9393b2534..982e9678b 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/process/ProcessContext.java +++ b/cli/src/main/java/com/devonfw/tools/ide/process/ProcessContext.java @@ -24,8 +24,9 @@ public interface ProcessContext { /** * Sets the executable command to be {@link #run()}. * - * @param executable the {@link Path} to the command to be executed by {@link #run()}. Depending on your operating system and the extension of the executable - * or OS specific conventions. So e.g. a *.cmd or *.bat file will be called via CMD shell on windows while a *.sh file will be called via Bash, etc. + * @param executable the {@link Path} to the command to be executed by {@link #run()}. Depending on your operating system and the extension of the + * executable or OS specific conventions. So e.g. a *.cmd or *.bat file will be called via CMD shell on windows while a *.sh file will be called via Bash, + * etc. * @return this {@link ProcessContext} for fluent API calls. */ ProcessContext executable(Path executable); diff --git a/cli/src/main/java/com/devonfw/tools/ide/repo/AbstractToolRepository.java b/cli/src/main/java/com/devonfw/tools/ide/repo/AbstractToolRepository.java index e86ff2fa1..a5718d6b9 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/repo/AbstractToolRepository.java +++ b/cli/src/main/java/com/devonfw/tools/ide/repo/AbstractToolRepository.java @@ -151,7 +151,7 @@ protected String createDownloadFilename(String tool, String edition, VersionIden * @param downloadFilename the filename of the download file. * @param resolvedVersion the resolved {@link VersionIdentifier} to download. * @return the actual {@link Path} where the file was downloaded to. Typically the given {@link Path} {@code target} but may also be a different file in - * edge-cases. + * edge-cases. */ protected Path download(String url, Path target, String downloadFilename, VersionIdentifier resolvedVersion) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/repo/ToolRepository.java b/cli/src/main/java/com/devonfw/tools/ide/repo/ToolRepository.java index df24e3f3e..153ab3628 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/repo/ToolRepository.java +++ b/cli/src/main/java/com/devonfw/tools/ide/repo/ToolRepository.java @@ -29,7 +29,7 @@ public interface ToolRepository { * @param edition the edition of the tool to download. * @param version the {@link VersionIdentifier} to download. * @return the resolved {@link VersionIdentifier}. If the given {@link VersionIdentifier} is NOT a {@link VersionIdentifier#isPattern() pattern} this method - * will always just return the given {@link VersionIdentifier}. + * will always just return the given {@link VersionIdentifier}. */ VersionIdentifier resolveVersion(String tool, String edition, VersionIdentifier version); diff --git a/cli/src/main/java/com/devonfw/tools/ide/step/Step.java b/cli/src/main/java/com/devonfw/tools/ide/step/Step.java index eb0eb1719..0afffe092 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/step/Step.java +++ b/cli/src/main/java/com/devonfw/tools/ide/step/Step.java @@ -50,7 +50,7 @@ public interface Step extends AutoCloseable { /** * @return {@code Boolean#TRUE} if this {@link Step} has {@link #success() succeeded}, {@code Boolean#FALSE} if the {@link Step} has {@link #close() ended} - * without {@link #success() success} and {@code null} if the {@link Step} is still running. + * without {@link #success() success} and {@code null} if the {@link Step} is still running. */ Boolean getSuccess(); @@ -64,7 +64,7 @@ default boolean isSuccess() { /** * @return {@code true} if this step {@link #close() ended} without {@link #success() success} e.g. with an {@link #error(String) error}, {@code false} - * otherwise. + * otherwise. */ default boolean isFailure() { @@ -197,7 +197,7 @@ default void error(Throwable error, String message, Object... args) { /** * @param i the index of the requested parameter. Should be in the range from {@code 0} to - * {@link #getParameterCount()}-1. + * {@link #getParameterCount()}-1. * @return the parameter at the given index {@code i} or {@code null} if no such parameter exists. */ Object getParameter(int i); 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 bb0af77cc..68d073843 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 @@ -4,7 +4,9 @@ import com.devonfw.tools.ide.repo.CustomTool; import com.devonfw.tools.ide.version.VersionIdentifier; - +/** + * {@link LocalToolCommandlet} for a {@link CustomTool}. + */ public class CustomToolCommandlet extends LocalToolCommandlet { private CustomTool customTool; 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 b52a9508a..ab62797ab 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 @@ -22,7 +22,7 @@ import com.devonfw.tools.ide.version.VersionIdentifier; /** - * {@link ToolCommandlet} that is installed locally into the IDE. + * {@link ToolCommandlet} that is installed locally into the IDEasy. */ public abstract class LocalToolCommandlet extends ToolCommandlet { @@ -113,8 +113,8 @@ protected boolean doInstall(boolean silent) { /** * Determines whether this tool should be installed directly in the software folder or in the software repository. * - * @return {@code true} if the tool should be installed directly in the software folder, ignoring the central software repository; {@code false} if - * the tool should be installed in the central software repository (default behavior). + * @return {@code true} if the tool should be installed directly in the software folder, ignoring the central software repository; {@code false} if the tool + * should be installed in the central software repository (default behavior). */ protected boolean isIgnoreSoftwareRepo() { @@ -256,8 +256,8 @@ public String getInstalledEdition() { } /** - * @param toolPath the installation {@link Path} where to find currently installed tool. The name of the parent directory of the real path corresponding to - * the passed {@link Path path} must be the name of the edition. + * @param toolPath the installation {@link Path} where to find currently installed tool. The name of the parent directory of the real path corresponding + * to the passed {@link Path path} must be the name of the edition. * @return the installed edition of this tool or {@code null} if not installed. */ public String getInstalledEdition(Path toolPath) { 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 0bec6a665..fdd0810d8 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 @@ -92,8 +92,8 @@ public void run() { * Ensures the tool is installed and then runs this tool with the given arguments. * * @param processMode see {@link ProcessMode} - * @param toolVersion the explicit version (pattern) to run. Typically {@code null} to ensure the configured version is installed and use that one. Otherwise, - * the specified version will be installed in the software repository without touching and IDE installation and used to run. + * @param toolVersion the explicit version (pattern) to run. Typically {@code null} to ensure the configured version is installed and use that one. + * Otherwise, the specified version will be installed in the software repository without touching and IDE installation and used to run. * @param args the command-line arguments to run the tool. */ public void runTool(ProcessMode processMode, VersionIdentifier toolVersion, String... args) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/ToolInstallation.java b/cli/src/main/java/com/devonfw/tools/ide/tool/ToolInstallation.java index 682d732b1..e8790e3a6 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/ToolInstallation.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/ToolInstallation.java @@ -9,13 +9,12 @@ * * @param rootDir the top-level installation directory where the tool software package has been extracted to. * @param linkDir the installation directory to link to the software folder inside IDE_HOME. Typically the same as {@code rootDir} but may differ (e.g. for - * MacOS applications). + * MacOS applications). * @param binDir the {@link Path} relative to {@code linkDir} pointing to the directory containing the binaries that should be put on the path (typically - * "bin"). + * "bin"). * @param resolvedVersion the {@link VersionIdentifier} of the resolved tool version installed in {@code rootDir}. * @param newInstallation {@code true} - if the tool should be installed newly, {@code true} - else */ -public record ToolInstallation(Path rootDir, Path linkDir, Path binDir, VersionIdentifier resolvedVersion, - boolean newInstallation) { +public record ToolInstallation(Path rootDir, Path linkDir, Path binDir, VersionIdentifier resolvedVersion, boolean newInstallation) { } diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/aws/AwsUrlUpdater.java b/cli/src/main/java/com/devonfw/tools/ide/tool/aws/AwsUrlUpdater.java index 05faf8fd5..7f9df6110 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/aws/AwsUrlUpdater.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/aws/AwsUrlUpdater.java @@ -60,5 +60,5 @@ protected void addVersion(UrlVersion urlVersion) { doAddVersion(urlVersion, baseUrl + "awscli-exe-linux-x86_64-${version}.zip", OperatingSystem.LINUX); doAddVersion(urlVersion, baseUrl + "AWSCLIV2-${version}.pkg", OperatingSystem.MAC); } - + } diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/mvn/Mvn.java b/cli/src/main/java/com/devonfw/tools/ide/tool/mvn/Mvn.java index b52186076..c4c5ef207 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/mvn/Mvn.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/mvn/Mvn.java @@ -76,42 +76,13 @@ public boolean install(boolean silent) { public void postInstall() { // locate templates... - boolean legacy = false; - boolean hasMvnTemplates = true; - Path settingsFolder = this.context.getSettingsPath(); - Path templatesFolder = settingsFolder.resolve(IdeContext.FOLDER_TEMPLATES); - if (!Files.isDirectory(templatesFolder)) { - Path templatesFolderLegacy = settingsFolder.resolve(IdeContext.FOLDER_LEGACY_TEMPLATES); - if (Files.isDirectory(templatesFolderLegacy)) { - templatesFolder = templatesFolderLegacy; - } else { - this.context.warning("No maven templates found. Neither in {} nor in {} - configuration broken", - templatesFolder, templatesFolderLegacy); - hasMvnTemplates = false; - } - } - Path templatesConfFolder = templatesFolder.resolve(IdeContext.FOLDER_CONF); - Path templatesConfMvnFolder = templatesConfFolder.resolve(MVN_CONFIG_FOLDER); - if (hasMvnTemplates) { - if (!Files.isDirectory(templatesConfMvnFolder)) { - Path templatesConfMvnLegacyFolder = templatesConfFolder.resolve(MVN_CONFIG_LEGACY_FOLDER); - if (Files.isDirectory(templatesConfMvnLegacyFolder)) { - templatesConfMvnFolder = templatesConfMvnLegacyFolder; - legacy = true; - } else { - this.context.warning("No maven templates found. Neither in {} nor in {} - configuration broken", templatesConfMvnFolder, - templatesConfMvnLegacyFolder); - hasMvnTemplates = false; - } - } + Path templatesConfMvnFolder = getMavenTemplatesFolder(); + if (templatesConfMvnFolder == null) { + return; } // locate real config... - Path confPath = this.context.getConfPath(); - Path mvnConfigPath = confPath.resolve(MVN_CONFIG_FOLDER); - if (!Files.isDirectory(mvnConfigPath) && legacy) { - mvnConfigPath = confPath.resolve(MVN_CONFIG_LEGACY_FOLDER); - } - this.context.getFileAccess().mkdirs(mvnConfigPath); + boolean legacy = templatesConfMvnFolder.getFileName().toString().equals(MVN_CONFIG_LEGACY_FOLDER); + Path mvnConfigPath = getMavenConfFolder(legacy); Path settingsSecurityFile = mvnConfigPath.resolve(SETTINGS_SECURITY_FILE); createSettingsSecurityFile(settingsSecurityFile); @@ -229,4 +200,42 @@ public String getToolHelpArguments() { return "-h"; } + + public Path getMavenTemplatesFolder() { + + Path templatesFolder = this.context.getSettingsTemplatePath(); + if (templatesFolder == null) { + return null; + } + Path templatesConfFolder = templatesFolder.resolve(IdeContext.FOLDER_CONF); + Path templatesConfMvnFolder = templatesConfFolder.resolve(MVN_CONFIG_FOLDER); + if (!Files.isDirectory(templatesConfMvnFolder)) { + Path templatesConfMvnLegacyFolder = templatesConfFolder.resolve(MVN_CONFIG_LEGACY_FOLDER); + if (!Files.isDirectory(templatesConfMvnLegacyFolder)) { + this.context.warning("No maven templates found neither in {} nor in {} - configuration broken", templatesConfMvnFolder, + templatesConfMvnLegacyFolder); + return null; + } + templatesConfMvnFolder = templatesConfMvnLegacyFolder; + } + return templatesConfMvnFolder; + } + + public Path getMavenConfFolder(boolean legacy) { + + Path confPath = this.context.getConfPath(); + Path mvnConfigFolder = confPath.resolve(MVN_CONFIG_FOLDER); + if (!Files.isDirectory(mvnConfigFolder)) { + Path mvnConfigLegacyFolder = confPath.resolve(Mvn.MVN_CONFIG_LEGACY_FOLDER); + if (Files.isDirectory(mvnConfigLegacyFolder)) { + mvnConfigFolder = mvnConfigLegacyFolder; + } else { + if (legacy) { + mvnConfigFolder = mvnConfigLegacyFolder; + } + this.context.getFileAccess().mkdirs(mvnConfigFolder); + } + } + return mvnConfigFolder; + } } diff --git a/cli/src/main/java/com/devonfw/tools/ide/url/model/UrlMetadata.java b/cli/src/main/java/com/devonfw/tools/ide/url/model/UrlMetadata.java index 2fcad237a..8daee3a4a 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/url/model/UrlMetadata.java +++ b/cli/src/main/java/com/devonfw/tools/ide/url/model/UrlMetadata.java @@ -97,8 +97,8 @@ private List computeSortedVersions(String tool, String editio /** * @param tool the name of the {@link UrlTool}. * @param edition the name of the {@link UrlEdition}. - * @param version the {@link VersionIdentifier} to match. May be a {@link VersionIdentifier#isPattern() pattern}, a specific version or {@code null} for the - * latest version. + * @param version the {@link VersionIdentifier} to match. May be a {@link VersionIdentifier#isPattern() pattern}, a specific version or {@code null} for + * the latest version. * @return the latest matching {@link VersionIdentifier} for the given {@code tool} and {@code edition}. */ public VersionIdentifier getVersion(String tool, String edition, VersionIdentifier version) { @@ -124,8 +124,8 @@ public VersionIdentifier getVersion(String tool, String edition, VersionIdentifi /** * @param tool the name of the {@link UrlTool}. * @param edition the name of the {@link UrlEdition}. - * @param version the {@link VersionIdentifier} to match. May be a {@link VersionIdentifier#isPattern() pattern}, a specific version or {@code null} for the - * latest version. + * @param version the {@link VersionIdentifier} to match. May be a {@link VersionIdentifier#isPattern() pattern}, a specific version or {@code null} for + * the latest version. * @return the latest matching {@link UrlVersion} for the given {@code tool} and {@code edition}. */ public UrlVersion getVersionFolder(String tool, String edition, VersionIdentifier version) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/variable/IdeVariables.java b/cli/src/main/java/com/devonfw/tools/ide/variable/IdeVariables.java index bc0a4b47b..62ba32ab0 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/variable/IdeVariables.java +++ b/cli/src/main/java/com/devonfw/tools/ide/variable/IdeVariables.java @@ -45,7 +45,7 @@ public interface IdeVariables { VariableDefinitionString MAVEN_ARGS = new VariableDefinitionString("MAVEN_ARGS", null, c -> c.getMavenArgs(), false, true); /** {@link VariableDefinition} arguments for maven to set the m2 repo location. */ - VariableDefinitionPath M2_REPO = new VariableDefinitionPath("M2_REPO", null, c -> c.getMavenRepoEnvVariable(), false, true); + VariableDefinitionPath M2_REPO = new VariableDefinitionPath("M2_REPO", null, c -> c.getMavenRepository(), false, true); /** {@link VariableDefinition} for {@link com.devonfw.tools.ide.context.IdeContext#getWorkspaceName() WORKSPACE}. */ VariableDefinitionString DOCKER_EDITION = new VariableDefinitionString("DOCKER_EDITION", null, c -> "rancher"); diff --git a/cli/src/main/package/bin/ide b/cli/src/main/package/bin/ide index e5a05802a..18307b830 100644 --- a/cli/src/main/package/bin/ide +++ b/cli/src/main/package/bin/ide @@ -11,12 +11,7 @@ if [ $# != 0 ]; then fi fi -ide_env= -if [ "${OSTYPE}" = "cygwin" ] || [ "${OSTYPE}" = "msys" ]; then - ide_env="$("${_IDEASY}" env --bash)" -else - ide_env="$("${_IDEASY}" env)" -fi +ide_env="$("${_IDEASY}" env --bash)" if [ $? = 0 ]; then eval "${ide_env}" if [ $# = 0 ]; then diff --git a/cli/src/main/package/bin/ide.bat b/cli/src/main/package/bin/ide.bat new file mode 100644 index 000000000..30ba76e7b --- /dev/null +++ b/cli/src/main/package/bin/ide.bat @@ -0,0 +1,22 @@ +@echo off + +Set _fBYellow= +Set _fBGreen= +Set _fBRed= +Set _RESET= + +if not "%1%" == "" ( + ideasy %* + if not %ERRORLEVEL% == 0 ( + echo %_fBRed%Error: IDEasy failed with exit code %ERRORLEVEL% %_RESET% + exit /b %ERRORLEVEL% + ) +) + +REM https://stackoverflow.com/questions/61888625/what-is-f-in-the-for-loop-command +for /f "tokens=*" %%i in ('ideasy env') do ( + call set %%i +) +if not %ERRORLEVEL% == 0 ( + echo IDE environment variables have been set for %IDE_HOME% in workspace %WORKSPACE% +) diff --git a/cli/src/main/package/completion b/cli/src/main/package/completion old mode 100644 new mode 100755 diff --git a/cli/src/main/package/setup b/cli/src/main/package/setup old mode 100644 new mode 100755 index b4c7ca2e4..1c75d998a --- a/cli/src/main/package/setup +++ b/cli/src/main/package/setup @@ -3,7 +3,7 @@ cd "$(dirname "${BASH_SOURCE:-$0}")" || exit 255 echo "Setting up your IDEasy in ${PWD}" -AUTOCOMPLETION="source ${PWD}/autocomplete" +AUTOCOMPLETION="source ${PWD}/completion" if ! grep -q "${AUTOCOMPLETION}" ~/.bashrc; then echo -e "${AUTOCOMPLETION}" >> ~/.bashrc diff --git a/cli/src/main/package/setup.bat b/cli/src/main/package/setup.bat new file mode 100644 index 000000000..f6b4eea02 --- /dev/null +++ b/cli/src/main/package/setup.bat @@ -0,0 +1,80 @@ +@echo off + +Set _fBYellow= +Set _fBGreen= +Set _fBRed= +Set _RESET= + +pushd %~dp0 +echo Setting up IDEasy in %CD% + +REM https://stackoverflow.com/questions/61888625/what-is-f-in-the-for-loop-command +for %%f in (ideasy.exe) do set "p=%%~$PATH:f" +if not defined p ( + for /F "tokens=2* delims= " %%f IN ('reg query HKCU\Environment /v PATH ^| findstr /i path') do set USER_PATH=%%g + goto :set_userpath +) else ( + echo IDEasy is already added to your PATH. + goto :find_bash +) +:set_userpath +if "%USER_PATH:~-1,1%" == ";" ( + set "USER_PATH=%USER_PATH:~0,-1%" +) +echo Adding %CD%\bin to your users system PATH +if "%USER_PATH%" == "" ( + echo %_fBYellow%ATTENTION: + echo Your user specific PATH variable seems to be empty. + echo You can double check this by pressing [Windows][r] and launch the programm SystemPropertiesAdvanced. + echo Then click on 'Environment variables' and check if 'PATH' is set in in the 'user variables' from the upper list. + echo In case 'PATH' is defined there non-empty and you get this message, please abort and give us feedback: + echo https://github.com/devonfw/IDEasy/issues + echo Otherwise all is correct and you can continue by pressing enter. + echo %_RESET% + pause + setx PATH "%PATH%;%CD%\bin" +) else ( + setx PATH "%USER_PATH%;%CD%\bin" +) +setx IDE_ROOT "%CD%" + +:find_bash +REM find bash on your Windows system... +for %%H in ( HKEY_LOCAL_MACHINE HKEY_CURRENT_USER ) do for /F "usebackq tokens=2*" %%O in (`call "%SystemRoot%"\system32\reg.exe query "%%H\Software\GitForWindows" /v "InstallPath" 2^>nul ^| "%SystemRoot%\system32\findstr.exe" REG_SZ`) do set GIT_HOME=%%P + +if exist "%GIT_HOME%\bin\bash.exe" ( + set "BASH=%GIT_HOME%\bin\bash.exe" + set "HOME=%USERPROFILE%" + goto :bash_detected +) + +echo %_fBYellow%WARNING: Git-bash is required but was not found at GIT_HOME=%GIT_HOME%.%_RESET% + +REM If bash can not be autodetected allow the user to configure bash via BASH_HOME environment variable as fallback + +if exist "%BASH_HOME%\bin\bash.exe" ( + set "BASH=%BASH_HOME%\bin\bash.exe" + set "HOME=%USERPROFILE%" + goto :bash_detected +) +echo: +echo %_fBYellow%*** ATTENTION ***%_RESET% +echo %_fBRed%ERROR: Could not find bash. It seems git for windows is not installed on your machine%_RESET% +echo %_fBRed%Please download and install git for windows from the following URL and after that rerun devonfw-ide setup:%_RESET% +echo %_fBRed%https://git-scm.com/download/win%_RESET% +exit /b 5 + +:bash_detected +echo Found bash at %BASH% +echo "%BASH%" -l -c "cd \"%CD%\";./setup" +"%BASH%" -l -c "cd \"%CD%\";./setup" +if %ERRORLEVEL% neq 0 ( + echo %_fBRed%Error occurred while running setup of IDEasy in bash.%_RESET% + exit /b %ERRORLEVEL% +) +echo %_fBGreen%Setup of IDEasy completed%_RESET% +if not "%1%" == "-b" ( + pause +) +popd +goto :EOF diff --git a/cli/src/test/java/com/devonfw/tools/ide/commandlet/EnvironmentCommandletTest.java b/cli/src/test/java/com/devonfw/tools/ide/commandlet/EnvironmentCommandletTest.java index 59706d139..06f3a2499 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/commandlet/EnvironmentCommandletTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/commandlet/EnvironmentCommandletTest.java @@ -8,6 +8,8 @@ import com.devonfw.tools.ide.context.IdeTestContext; import com.devonfw.tools.ide.context.IdeTestContextMock; import com.devonfw.tools.ide.log.IdeLogEntry; +import com.devonfw.tools.ide.log.IdeLogLevel; +import com.devonfw.tools.ide.os.SystemInfoImpl; /** * Test of {@link EnvironmentCommandlet}. @@ -15,29 +17,121 @@ public class EnvironmentCommandletTest extends AbstractIdeContextTest { /** - * Test of {@link EnvironmentCommandlet} run. + * Test of {@link EnvironmentCommandlet} run with DEBUG logging (partitioning per type/source). */ @Test - public void testRun() { + public void testRunDebugLogging() { // arrange String path = "project/workspaces/foo-test/my-git-repo"; IdeTestContext context = newContext(PROJECT_BASIC, path, false); EnvironmentCommandlet env = context.getCommandletManager().getCommandlet(EnvironmentCommandlet.class); + Path userProperties = context.getUserHomeIde().resolve("ide.properties"); Path settingsIdeProperties = context.getSettingsPath().resolve("ide.properties"); Path confIdeProperties = context.getConfPath().resolve("ide.properties"); + Path workspaceIdeProperties = context.getWorkspacePath().resolve("ide.properties"); // act env.run(); // assert assertThat(context).log().hasEntriesWithNothingElseInBetween( // - IdeLogEntry.ofDebug("from defaults:"), IdeLogEntry.ofInfo("DOCKER_EDITION=\"docker\""), IdeLogEntry.ofInfo("INTELLIJ_EDITION=\"ultimate\""), // - IdeLogEntry.ofDebug("from " + settingsIdeProperties + ":"), IdeLogEntry.ofInfo("JAVA_VERSION=\"17*\""), - IdeLogEntry.ofInfo("SOME=\"some-${UNDEFINED}\""), IdeLogEntry.ofInfo("BAR=\"bar-some-${UNDEFINED}\""), - IdeLogEntry.ofInfo("IDE_TOOLS=\"mvn,eclipse\""), - IdeLogEntry.ofDebug("from " + confIdeProperties + ":"), IdeLogEntry.ofInfo("MVN_VERSION=\"3.9.1\"") //overwritten by conf + IdeLogEntry.ofDebug("from USER@" + userProperties.toString() + ":"), // + IdeLogEntry.ofInfo("DOCKER_EDITION=\"docker\""), // + IdeLogEntry.ofInfo("FOO=\"foo-bar-some-${UNDEFINED}\""), // + + IdeLogEntry.ofDebug("from SETTINGS@" + settingsIdeProperties + ":"), + IdeLogEntry.ofInfo("BAR=\"bar-some-${UNDEFINED}\""), // + IdeLogEntry.ofInfo("ECLIPSE_VERSION=\"2023-03\""), // + IdeLogEntry.ofInfo("IDE_TOOLS=\"mvn,eclipse\""), // + IdeLogEntry.ofInfo("INTELLIJ_EDITION=\"ultimate\""), // + IdeLogEntry.ofInfo("JAVA_VERSION=\"17*\""), // + IdeLogEntry.ofInfo("TEST_ARGS4=\" settings4\""), // + IdeLogEntry.ofInfo("TEST_ARGSb=\"user10 workspace10 settingsb user1 settings1 workspace1 conf1 user3 workspace3 confa userb\""), // + + IdeLogEntry.ofDebug("from WORKSPACE@" + workspaceIdeProperties + ":"), // + IdeLogEntry.ofInfo("TEST_ARGS10=\"user10 workspace10\""), // + IdeLogEntry.ofInfo("TEST_ARGS3=\" user3 workspace3\""), // + IdeLogEntry.ofInfo("TEST_ARGS9=\"settings9 workspace9\""), // + IdeLogEntry.ofInfo("TEST_ARGSd=\" user1 settings1 workspace1 conf1 userd workspaced\""), // + + IdeLogEntry.ofDebug("from CONF@" + confIdeProperties + ":"), // + IdeLogEntry.ofInfo("MVN_VERSION=\"3.9.1\""), // + IdeLogEntry.ofInfo("SOME=\"some-${UNDEFINED}\""), // + IdeLogEntry.ofInfo("TEST_ARGS1=\" user1 settings1 workspace1 conf1\""), // + IdeLogEntry.ofInfo("TEST_ARGS2=\" user2 conf2\""), // + IdeLogEntry.ofInfo("TEST_ARGS5=\" settings5 conf5\""), // + IdeLogEntry.ofInfo("TEST_ARGS6=\" settings6 workspace6 conf6\""), // + IdeLogEntry.ofInfo("TEST_ARGS7=\"user7 settings7 workspace7 conf7\""), // + IdeLogEntry.ofInfo("TEST_ARGS8=\"settings8 workspace8 conf8\""), // + IdeLogEntry.ofInfo("TEST_ARGSa=\" user1 settings1 workspace1 conf1 user3 workspace3 confa\""), // + IdeLogEntry.ofInfo("TEST_ARGSc=\" user1 settings1 workspace1 conf1 userc settingsc confc\""), // + + IdeLogEntry.ofDebug("from RESOLVED:"), // + IdeLogEntry.ofInfo("HOME=\"" + context.getUserHome() + "\""), // + IdeLogEntry.ofInfo("IDE_HOME=\"" + context.getIdeHome() + "\""), // + IdeLogEntry.ofInfo("export M2_REPO=\"" + context.getUserHome() + "/.m2/repository\""), // + new IdeLogEntry(IdeLogLevel.INFO, "export PATH=", true), // + IdeLogEntry.ofInfo("WORKSPACE=\"foo-test\""), // + IdeLogEntry.ofInfo("WORKSPACE_PATH=\"" + context.getWorkspacePath() + "\"") // ); } + /** + * Test of {@link EnvironmentCommandlet} run with INFO logging (plain variables without source). + */ + @Test + public void testRunInfoLogging() { + + // arrange + String path = "project/workspaces/foo-test/my-git-repo"; + IdeTestContext context = newContext(PROJECT_BASIC, path, false, IdeLogLevel.INFO); + EnvironmentCommandlet env = context.getCommandletManager().getCommandlet(EnvironmentCommandlet.class); + Path userProperties = context.getUserHomeIde().resolve("ide.properties"); + Path settingsIdeProperties = context.getSettingsPath().resolve("ide.properties"); + Path confIdeProperties = context.getConfPath().resolve("ide.properties"); + Path workspaceIdeProperties = context.getWorkspacePath().resolve("ide.properties"); + // act + env.run(); + // assert + if (SystemInfoImpl.INSTANCE.isWindows()) { + assertThat(context).log().hasEntriesWithNothingElseInBetween( // + IdeLogEntry.ofInfo("M2_REPO=" + context.getUserHome() + "/.m2/repository"), // + new IdeLogEntry(IdeLogLevel.INFO, "PATH=", true) // + ); + } else { + assertThat(context).log().hasEntriesWithNothingElseInBetween( // + IdeLogEntry.ofInfo("BAR=\"bar-some-${UNDEFINED}\""), // + IdeLogEntry.ofInfo("DOCKER_EDITION=\"docker\""), // + IdeLogEntry.ofInfo("ECLIPSE_VERSION=\"2023-03\""), // + IdeLogEntry.ofInfo("FOO=\"foo-bar-some-${UNDEFINED}\""), // + IdeLogEntry.ofInfo("HOME=\"" + context.getUserHome() + "\""), // + IdeLogEntry.ofInfo("IDE_HOME=\"" + context.getIdeHome() + "\""), // + IdeLogEntry.ofInfo("IDE_TOOLS=\"mvn,eclipse\""), // + IdeLogEntry.ofInfo("INTELLIJ_EDITION=\"ultimate\""), // + IdeLogEntry.ofInfo("JAVA_VERSION=\"17*\""), // + IdeLogEntry.ofInfo("export M2_REPO=\"" + context.getUserHome() + "/.m2/repository\""), // + IdeLogEntry.ofInfo("MVN_VERSION=\"3.9.1\""), // + new IdeLogEntry(IdeLogLevel.INFO, "export PATH=", true), // + IdeLogEntry.ofInfo("SOME=\"some-${UNDEFINED}\""), // + IdeLogEntry.ofInfo("TEST_ARGS1=\" user1 settings1 workspace1 conf1\""), // + IdeLogEntry.ofInfo("TEST_ARGS10=\"user10 workspace10\""), // + IdeLogEntry.ofInfo("TEST_ARGS2=\" user2 conf2\""), // + IdeLogEntry.ofInfo("TEST_ARGS3=\" user3 workspace3\""), // + IdeLogEntry.ofInfo("TEST_ARGS4=\" settings4\""), // + IdeLogEntry.ofInfo("TEST_ARGS5=\" settings5 conf5\""), // + IdeLogEntry.ofInfo("TEST_ARGS6=\" settings6 workspace6 conf6\""), // + IdeLogEntry.ofInfo("TEST_ARGS7=\"user7 settings7 workspace7 conf7\""), // + IdeLogEntry.ofInfo("TEST_ARGS8=\"settings8 workspace8 conf8\""), // + IdeLogEntry.ofInfo("TEST_ARGS9=\"settings9 workspace9\""), // + IdeLogEntry.ofInfo("TEST_ARGSa=\" user1 settings1 workspace1 conf1 user3 workspace3 confa\""), // + IdeLogEntry.ofInfo("TEST_ARGSb=\"user10 workspace10 settingsb user1 settings1 workspace1 conf1 user3 workspace3 confa userb\""), // + IdeLogEntry.ofInfo("TEST_ARGSc=\" user1 settings1 workspace1 conf1 userc settingsc confc\""), // + IdeLogEntry.ofInfo("TEST_ARGSd=\" user1 settings1 workspace1 conf1 userd workspaced\""), // + IdeLogEntry.ofInfo("WORKSPACE=\"foo-test\""), // + IdeLogEntry.ofInfo("WORKSPACE_PATH=\"" + context.getWorkspacePath() + "\"") // + ); + } + } + /** * Test that {@link EnvironmentCommandlet} requires home. */ diff --git a/cli/src/test/java/com/devonfw/tools/ide/commandlet/VersionGetCommandletTest.java b/cli/src/test/java/com/devonfw/tools/ide/commandlet/VersionGetCommandletTest.java index 863fd0e79..e01f2540e 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/commandlet/VersionGetCommandletTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/commandlet/VersionGetCommandletTest.java @@ -14,7 +14,7 @@ public class VersionGetCommandletTest extends AbstractIdeContextTest { * Test of {@link VersionGetCommandlet} run, when Installed Version is null. */ @Test - public void testVersionGetCommandletRunThrowsCliException() { + public void testVersionGetCommandletNotInstalledRun() { // arrange IdeTestContext context = newContext(PROJECT_BASIC, null, false); @@ -27,6 +27,23 @@ public void testVersionGetCommandletRunThrowsCliException() { "To install that version call the following command:", "ide install java"); } + @Test + public void testVersionGetCommandletNotInstalledRunInstalledFlag() { + + // arrange + IdeTestContext context = newContext(PROJECT_BASIC, null, false); + VersionGetCommandlet versionGet = context.getCommandletManager().getCommandlet(VersionGetCommandlet.class); + versionGet.tool.setValueAsString("java", context); + versionGet.installed.setValue(true); + // act + versionGet.run(); + // assert + assertThat(context).logAtInfo().hasMessage("No installation of tool java was found."); + assertThat(context).logAtInfo().hasMessage("The configured version for tool java is 17*"); + assertThat(context).logAtInfo().hasMessage("To install that version call the following command:"); + assertThat(context).logAtInfo().hasMessage("ide install java"); + } + /** * Test of {@link VersionGetCommandlet} run. */ diff --git a/cli/src/test/java/com/devonfw/tools/ide/commandlet/VersionListCommandletTest.java b/cli/src/test/java/com/devonfw/tools/ide/commandlet/VersionListCommandletTest.java index 3305582bd..b5736a001 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/commandlet/VersionListCommandletTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/commandlet/VersionListCommandletTest.java @@ -23,6 +23,6 @@ public void testVersionListCommandletRun() { // act versionList.run(); // assert - assertThat(context).logAtInfo().hasEntries("3.0.5", "3.1.0", "3.2.1"); + assertThat(context).logAtInfo().hasEntries("3.2.1", "3.1.0", "3.0.5"); } } diff --git a/cli/src/test/java/com/devonfw/tools/ide/context/AbstractIdeContextTest.java b/cli/src/test/java/com/devonfw/tools/ide/context/AbstractIdeContextTest.java index b8ae3da37..84ee34356 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/context/AbstractIdeContextTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/context/AbstractIdeContextTest.java @@ -10,7 +10,7 @@ import com.devonfw.tools.ide.io.FileAccessImpl; import com.devonfw.tools.ide.io.FileCopyMode; import com.devonfw.tools.ide.io.IdeProgressBarTestImpl; -import com.devonfw.tools.ide.log.IdeTestContextAssertion; +import com.devonfw.tools.ide.log.IdeLogLevel; import com.devonfw.tools.ide.repo.ToolRepositoryMock; /** @@ -64,6 +64,20 @@ protected static IdeTestContext newContext(String testProject, String projectPat */ protected static IdeTestContext newContext(String testProject, String projectPath, boolean copyForMutation) { + return newContext(testProject, projectPath, copyForMutation, IdeLogLevel.TRACE); + } + + /** + * @param testProject the (folder)name of the project test case, in this folder a 'project' folder represents the test project in {@link #TEST_PROJECTS}. E.g. + * "basic". + * @param projectPath the relative path inside the test project where to create the context. + * @param copyForMutation - {@code true} to create a copy of the project that can be modified by the test, {@code false} otherwise (only to save resources if + * you are 100% sure that your test never modifies anything in that project.) + * @param logLevel the {@link IdeLogLevel} used as threshold for logging. + * @return the {@link IdeTestContext} pointing to that project. + */ + protected static IdeTestContext newContext(String testProject, String projectPath, boolean copyForMutation, IdeLogLevel logLevel) { + Path ideRoot = TEST_PROJECTS.resolve(testProject); if (copyForMutation) { Path ideRootCopy = TEST_PROJECTS_COPY.resolve(testProject); @@ -82,7 +96,7 @@ protected static IdeTestContext newContext(String testProject, String projectPat if (Files.isDirectory(repositoryFolder)) { toolRepository = new ToolRepositoryMock(repositoryFolder); } - IdeTestContext context = new IdeTestContext(userDir, toolRepository); + IdeTestContext context = new IdeTestContext(userDir, toolRepository, logLevel); if (toolRepository != null) { toolRepository.setContext(context); } 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 05c5a1766..8d55b32ab 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 @@ -1,10 +1,16 @@ package com.devonfw.tools.ide.context; +import java.nio.file.Files; import java.nio.file.Path; import java.util.HashMap; import java.util.Map; import java.util.function.Function; +import com.devonfw.tools.ide.common.SystemPath; +import com.devonfw.tools.ide.environment.AbstractEnvironmentVariables; +import com.devonfw.tools.ide.environment.EnvironmentVariables; +import com.devonfw.tools.ide.environment.EnvironmentVariablesPropertiesFile; +import com.devonfw.tools.ide.environment.EnvironmentVariablesType; import com.devonfw.tools.ide.io.FileAccess; import com.devonfw.tools.ide.io.IdeProgressBar; import com.devonfw.tools.ide.io.IdeProgressBarTestImpl; @@ -13,6 +19,7 @@ import com.devonfw.tools.ide.os.SystemInfo; import com.devonfw.tools.ide.repo.DefaultToolRepository; import com.devonfw.tools.ide.repo.ToolRepository; +import com.devonfw.tools.ide.variable.IdeVariables; /** * Implementation of {@link IdeContext} for testing. @@ -84,6 +91,28 @@ public IdeProgressBar prepareProgressBar(String taskName, long size) { return progressBar; } + @Override + protected AbstractEnvironmentVariables createSystemVariables() { + + Path home = getUserHome(); + if (home != null) { + Path systemPropertiesFile = home.resolve("environment.properties"); + if (Files.exists(systemPropertiesFile)) { + return new EnvironmentVariablesPropertiesFile(null, EnvironmentVariablesType.SYSTEM, systemPropertiesFile, this); + } + } + return super.createSystemVariables(); + } + + @Override + protected SystemPath computeSystemPath() { + + EnvironmentVariables systemVars = getVariables().getByType(EnvironmentVariablesType.SYSTEM); + String envPath = systemVars.get(IdeVariables.PATH.getName()); + envPath = getVariables().resolve(envPath, systemVars.getSource()); + return new SystemPath(this, envPath); + } + @Override public SystemInfo getSystemInfo() { diff --git a/cli/src/test/java/com/devonfw/tools/ide/context/IdeContextTest.java b/cli/src/test/java/com/devonfw/tools/ide/context/IdeContextTest.java index 01d4820c8..adc90756a 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/context/IdeContextTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/context/IdeContextTest.java @@ -40,8 +40,7 @@ public void testBasicProjectEnvironment() { assertThat(context.getWorkspacePath().resolve("readme")).hasContent("this is the foo-test workspace of basic"); SystemPath systemPath = IdeVariables.PATH.get(context); assertThat(systemPath).isSameAs(context.getPath()); - String envPath = System.getenv(IdeVariables.PATH.getName()); - envPath = envPath.replaceAll("[\\\\][;]", ";").replaceAll("[\\/][:]", ":"); + String envPath = context.getIdeRoot().resolve("_ide").resolve("bin").toString(); assertThat(systemPath.toString()).isNotEqualTo(envPath).endsWith(envPath); Path softwarePath = context.getSoftwarePath(); Path javaBin = softwarePath.resolve("java/bin"); diff --git a/cli/src/test/java/com/devonfw/tools/ide/context/IdeTestContext.java b/cli/src/test/java/com/devonfw/tools/ide/context/IdeTestContext.java index c53e93723..1bc9b8742 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/context/IdeTestContext.java +++ b/cli/src/test/java/com/devonfw/tools/ide/context/IdeTestContext.java @@ -5,7 +5,6 @@ import java.util.List; import com.devonfw.tools.ide.log.IdeLogLevel; -import com.devonfw.tools.ide.log.IdeTestLogger; import com.devonfw.tools.ide.log.IdeTestLoggerFactory; import com.devonfw.tools.ide.process.ProcessContext; import com.devonfw.tools.ide.repo.ToolRepository; @@ -15,6 +14,8 @@ */ public class IdeTestContext extends AbstractIdeTestContext { + private final IdeTestLoggerFactory loggerFactory; + private LinkedList inputValues; /** @@ -38,13 +39,27 @@ public IdeTestContext(Path userDir, String... answers) { */ public IdeTestContext(Path userDir, ToolRepository toolRepository, String... answers) { - super(new IdeTestLoggerFactory(), userDir, toolRepository, answers); + this(userDir, toolRepository, IdeLogLevel.TRACE, answers); } - @Override - public IdeTestLogger level(IdeLogLevel level) { + /** + * The constructor. + * + * @param userDir the optional {@link Path} to current working directory. + * @param toolRepository the {@link ToolRepository} of the context. If it is set to {@code null} * {@link com.devonfw.tools.ide.repo.DefaultToolRepository} + * will be used. + * @param logLevel the {@link IdeLogLevel} used as threshold for logging. + * @param answers the automatic answers simulating a user in test. + */ + public IdeTestContext(Path userDir, ToolRepository toolRepository, IdeLogLevel logLevel, String... answers) { + + this(new IdeTestLoggerFactory(logLevel), userDir, toolRepository, answers); + } + + private IdeTestContext(IdeTestLoggerFactory loggerFactory, Path userDir, ToolRepository toolRepository, String... answers) { - return (IdeTestLogger) super.level(level); + super(loggerFactory, userDir, toolRepository, answers); + this.loggerFactory = loggerFactory; } @Override @@ -83,4 +98,11 @@ public String askForInput(String message) { return this.inputValues.isEmpty() ? null : this.inputValues.poll(); } + /** + * @return the {@link IdeTestLoggerFactory}. + */ + public IdeTestLoggerFactory getLoggerFactory() { + + return loggerFactory; + } } diff --git a/cli/src/test/java/com/devonfw/tools/ide/log/IdeTestContextAssertion.java b/cli/src/test/java/com/devonfw/tools/ide/context/IdeTestContextAssertion.java similarity index 86% rename from cli/src/test/java/com/devonfw/tools/ide/log/IdeTestContextAssertion.java rename to cli/src/test/java/com/devonfw/tools/ide/context/IdeTestContextAssertion.java index 9e40135de..635e8ee0b 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/log/IdeTestContextAssertion.java +++ b/cli/src/test/java/com/devonfw/tools/ide/context/IdeTestContextAssertion.java @@ -1,6 +1,7 @@ -package com.devonfw.tools.ide.log; +package com.devonfw.tools.ide.context; -import com.devonfw.tools.ide.context.IdeTestContext; +import com.devonfw.tools.ide.log.IdeLogLevel; +import com.devonfw.tools.ide.log.IdeTestLoggerAssertion; /** * Assertion for {@link IdeTestContext}. @@ -25,7 +26,7 @@ public IdeTestContextAssertion(IdeTestContext context) { */ public IdeTestLoggerAssertion log(IdeLogLevel level) { - return new IdeTestLoggerAssertion(context.level(IdeLogLevel.INFO).getEntries(), level); // random level - all loggers share the same list of log entries + return new IdeTestLoggerAssertion(context.getLoggerFactory().getEntries(), level); } /** diff --git a/cli/src/test/java/com/devonfw/tools/ide/environment/VariableLineTest.java b/cli/src/test/java/com/devonfw/tools/ide/environment/VariableLineTest.java index 78354486f..0c587a06a 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/environment/VariableLineTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/environment/VariableLineTest.java @@ -18,7 +18,7 @@ public class VariableLineTest extends Assertions { private VariableLine line(String line) { - return VariableLine.of(line, LOGGER, "junit-source"); + return VariableLine.of(line, LOGGER, new VariableSource(EnvironmentVariablesType.RESOLVED, null)); } /** diff --git a/cli/src/test/java/com/devonfw/tools/ide/log/IdeLogEntry.java b/cli/src/test/java/com/devonfw/tools/ide/log/IdeLogEntry.java index 4f1d11ed7..fb495ca88 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/log/IdeLogEntry.java +++ b/cli/src/test/java/com/devonfw/tools/ide/log/IdeLogEntry.java @@ -6,7 +6,25 @@ * @param level the {@link IdeLogLevel}. * @param message the message that has been logged. */ -public record IdeLogEntry(IdeLogLevel level, String message) { +public record IdeLogEntry(IdeLogLevel level, String message, boolean contains) { + + public IdeLogEntry(IdeLogLevel level, String message) { + this(level, message, false); + } + + public boolean matches(IdeLogEntry entry) { + + if (this.level != entry.level) { + return false; + } else if (this.contains) { + if (!entry.message.contains(this.message)) { + return false; + } + } else if (!entry.message.equals(this.message)) { + return false; + } + return true; + } public static IdeLogEntry ofError(String message) { @@ -42,4 +60,5 @@ public static IdeLogEntry ofTrace(String message) { return new IdeLogEntry(IdeLogLevel.TRACE, message); } + } diff --git a/cli/src/test/java/com/devonfw/tools/ide/log/IdeTestLoggerAssertion.java b/cli/src/test/java/com/devonfw/tools/ide/log/IdeTestLoggerAssertion.java index 8632c18f4..a83e4bab7 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/log/IdeTestLoggerAssertion.java +++ b/cli/src/test/java/com/devonfw/tools/ide/log/IdeTestLoggerAssertion.java @@ -69,58 +69,73 @@ public void hasEntries(String... messages) { } /** - * @param exprectedEntries the expected {@link com.devonfw.tools.ide.log.IdeLogEntry log entries} in order. + * @param expectedEntries the expected {@link com.devonfw.tools.ide.log.IdeLogEntry log entries} in order. */ - public void hasEntries(IdeLogEntry... exprectedEntries) { + public void hasEntries(IdeLogEntry... expectedEntries) { - hasEntries(false, exprectedEntries); + hasEntries(false, expectedEntries); } /** - * @param exprectedEntries the expected {@link com.devonfw.tools.ide.log.IdeLogEntry log entries} to be logged in order without any other log statement in + * @param expectedEntries the expected {@link com.devonfw.tools.ide.log.IdeLogEntry log entries} to be logged in order without any other log statement in * between them. */ - public void hasEntriesWithNothingElseInBetween(IdeLogEntry... exprectedEntries) { + public void hasEntriesWithNothingElseInBetween(IdeLogEntry... expectedEntries) { - hasEntries(true, exprectedEntries); + hasEntries(true, expectedEntries); } - private void hasEntries(boolean nothingElseInBetween, IdeLogEntry... exprectedEntries) { + private void hasEntries(boolean nothingElseInBetween, IdeLogEntry... expectedEntries) { - assert (exprectedEntries.length > 0); + assert (expectedEntries.length > 0); int i = 0; - for (IdeLogEntry entry : exprectedEntries) { - if (entry.equals(exprectedEntries[i])) { + int max = 0; + for (IdeLogEntry entry : this.entries) { + if (expectedEntries[i].matches(entry)) { i++; } else { if (nothingElseInBetween) { i = 0; - } else if (entry.equals(exprectedEntries[i])) { + } else if (expectedEntries[0].matches(entry)) { i = 1; } } - if (i == exprectedEntries.length) { + if (i == expectedEntries.length) { return; } + if (i > max) { + max = i; + } } StringBuilder error = new StringBuilder(4096); - error.append("Could not find expected log entries:\n"); - for (IdeLogEntry entry : exprectedEntries) { - error.append(entry.level()); - error.append(":"); - error.append(entry.message()); - error.append('\n'); + if (max > 0) { + error.append("Found expected log entries:\n"); + for (i = 0; i < max; i++) { + appendEntry(error, expectedEntries[i]); + } + } + error.append("\nThe first entry that was not matching from a block of "); + error.append(expectedEntries.length); + error.append(" expected log-entries "); + if (nothingElseInBetween) { + error.append("with nothing else logged in between "); } + error.append("was:\n"); + appendEntry(error, expectedEntries[max]); error.append("\nIn the logs of this test:\n"); for (IdeLogEntry entry : this.entries) { - error.append(entry.level()); - error.append(":"); - error.append(entry.message()); - error.append('\n'); + appendEntry(error, entry); } Assertions.fail(error.toString()); } + private static void appendEntry(StringBuilder sb, IdeLogEntry entry) { + sb.append(entry.level()); + sb.append(":"); + sb.append(entry.message()); + sb.append('\n'); + } + private void fulfillsPredicate(Predicate predicate, PredicateMode mode, String errorMessage) { if (this.level != null) { @@ -138,7 +153,9 @@ private void fulfillsPredicate(Predicate predicate, PredicateMode m } } } - Assertions.fail(errorMessage); // no log entry matched by predicate + if (mode == PredicateMode.MATCH_ONE) { + Assertions.fail(errorMessage); // no log entry matched by predicate + } } } diff --git a/cli/src/test/java/com/devonfw/tools/ide/log/IdeTestLoggerFactory.java b/cli/src/test/java/com/devonfw/tools/ide/log/IdeTestLoggerFactory.java index f8cba715a..02589c4a5 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/log/IdeTestLoggerFactory.java +++ b/cli/src/test/java/com/devonfw/tools/ide/log/IdeTestLoggerFactory.java @@ -9,19 +9,34 @@ */ public class IdeTestLoggerFactory implements Function { + private final IdeLogLevel logLevel; + private final List entries; /** * The constructor. */ public IdeTestLoggerFactory() { + this(IdeLogLevel.TRACE); + } + + /** + * The constructor. + * + * @param logLevel the {@link IdeLogLevel} used as threshold for logging. + */ + public IdeTestLoggerFactory(IdeLogLevel logLevel) { super(); + this.logLevel = logLevel; this.entries = new ArrayList<>(512); } @Override public IdeSubLogger apply(IdeLogLevel ideLogLevel) { + if (ideLogLevel.ordinal() < this.logLevel.ordinal()) { + return new IdeSubLoggerNone(ideLogLevel); + } return new IdeTestLogger(ideLogLevel, this.entries); } diff --git a/cli/src/test/java/com/devonfw/tools/ide/step/StepTest.java b/cli/src/test/java/com/devonfw/tools/ide/step/StepTest.java index a2601c50e..56b9bd0dd 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/step/StepTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/step/StepTest.java @@ -52,9 +52,9 @@ public void testValidUsageSuccessSilent() { assertThat(step.getParameter(1)).isEqualTo("arg2"); assertThat(step.getParameter(2)).isNull(); assertThat(context).log().hasEntries(IdeLogEntry.ofTrace("Starting step Test-Step with params [arg1, arg2]..."), - IdeLogEntry.ofStep("Start: Test-Step"), - IdeLogEntry.ofSuccess("Test-Step"), IdeLogEntry.ofDebug("Step 'Test-Step' ended successfully.")); + assertThat(context).log().hasNoMessage("Start: Test-Step"); + assertThat(context).log().hasNoMessage("Test-Step"); } @Test diff --git a/cli/src/test/java/com/devonfw/tools/ide/tool/mvn/MvnTest.java b/cli/src/test/java/com/devonfw/tools/ide/tool/mvn/MvnTest.java index 0837cec15..3591f11c1 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/tool/mvn/MvnTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/tool/mvn/MvnTest.java @@ -13,6 +13,7 @@ import com.devonfw.tools.ide.commandlet.InstallCommandlet; import com.devonfw.tools.ide.context.AbstractIdeContextTest; import com.devonfw.tools.ide.context.IdeTestContext; +import com.devonfw.tools.ide.variable.IdeVariables; /** * Integration test of {@link Mvn}. @@ -85,6 +86,22 @@ private void checkInstallation(IdeTestContext context) throws IOException { assertFileContent(settingsSecurityFile, List.of("masterPassword")); } + /** + * Tests if the user is starting IDEasy without a Maven repository, IDEasy should fall back to USER_HOME/.m2/repository. + *

+ * See: #463 + */ + @Test + public void testMavenRepositoryPathFallsBackToUserHome() { + // arrange + String path = "project/workspaces"; + // act + IdeTestContext context = newContext(PROJECT_MVN, path, false); + Path mavenRepository = context.getUserHome().resolve(".m2").resolve("repository"); + // assert + assertThat(IdeVariables.M2_REPO.get(context)).isEqualTo(mavenRepository); + } + private void assertFileContent(Path filePath, List expectedValues) throws IOException { String content = new String(Files.readAllBytes(filePath)); diff --git a/cli/src/test/resources/ide-projects/basic/_ide/bin/ide b/cli/src/test/resources/ide-projects/basic/_ide/bin/ide new file mode 100644 index 000000000..01f20642f --- /dev/null +++ b/cli/src/test/resources/ide-projects/basic/_ide/bin/ide @@ -0,0 +1,2 @@ +#!/bin/bash +echo "ide $*" diff --git a/cli/src/test/resources/ide-projects/basic/project/home/environment.properties b/cli/src/test/resources/ide-projects/basic/project/home/environment.properties new file mode 100644 index 000000000..c0a59da37 --- /dev/null +++ b/cli/src/test/resources/ide-projects/basic/project/home/environment.properties @@ -0,0 +1,2 @@ +# if this file is present, it will replace and mock System.getenv in the text context +PATH=${IDE_ROOT}/_ide/bin diff --git a/cli/src/test/resources/ide-projects/basic/project/software/mvn/bin/mvn b/cli/src/test/resources/ide-projects/basic/project/software/mvn/bin/mvn new file mode 100644 index 000000000..4a97b4f4a --- /dev/null +++ b/cli/src/test/resources/ide-projects/basic/project/software/mvn/bin/mvn @@ -0,0 +1,5 @@ +#!/bin/bash +if [ "$1" == "-h" ]; then + echo "usage: mvn [options] [] []" +fi +echo "mvn $*" diff --git a/cli/src/test/resources/ide-projects/mvn/project/home/environment.properties b/cli/src/test/resources/ide-projects/mvn/project/home/environment.properties new file mode 100644 index 000000000..c0a59da37 --- /dev/null +++ b/cli/src/test/resources/ide-projects/mvn/project/home/environment.properties @@ -0,0 +1,2 @@ +# if this file is present, it will replace and mock System.getenv in the text context +PATH=${IDE_ROOT}/_ide/bin