From 661ea6ad052eb80b768f5ca28338faf3feaa533f Mon Sep 17 00:00:00 2001 From: ndemirca <157051033+ndemirca@users.noreply.github.com> Date: Mon, 15 Apr 2024 11:00:43 +0200 Subject: [PATCH 1/3] #206: Implemented UninstallCommandlet (#258) --- .../ide/commandlet/CommandletManagerImpl.java | 15 ++-- .../ide/commandlet/UninstallCommandlet.java | 53 +++++++++++++ .../commandlet/UninstallCommandletTest.java | 77 +++++++++++++++++++ .../ide/context/AbstractIdeTestContext.java | 21 +++-- .../basic/project/software/npm/bin/readme | 1 + 5 files changed, 155 insertions(+), 12 deletions(-) create mode 100644 cli/src/main/java/com/devonfw/tools/ide/commandlet/UninstallCommandlet.java create mode 100644 cli/src/test/java/com/devonfw/tools/ide/commandlet/UninstallCommandletTest.java create mode 100644 cli/src/test/resources/ide-projects/basic/project/software/npm/bin/readme diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java index 17b02a0a1..ef80dbaea 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java @@ -1,11 +1,5 @@ package com.devonfw.tools.ide.commandlet; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.property.KeywordProperty; import com.devonfw.tools.ide.property.Property; @@ -18,6 +12,7 @@ import com.devonfw.tools.ide.tool.gh.Gh; import com.devonfw.tools.ide.tool.gradle.Gradle; import com.devonfw.tools.ide.tool.helm.Helm; +import com.devonfw.tools.ide.tool.jasypt.Jasypt; import com.devonfw.tools.ide.tool.java.Java; import com.devonfw.tools.ide.tool.jmc.Jmc; import com.devonfw.tools.ide.tool.kotlinc.Kotlinc; @@ -30,7 +25,12 @@ import com.devonfw.tools.ide.tool.sonar.Sonar; import com.devonfw.tools.ide.tool.terraform.Terraform; import com.devonfw.tools.ide.tool.vscode.Vscode; -import com.devonfw.tools.ide.tool.jasypt.Jasypt; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; /** * Implementation of {@link CommandletManager}. @@ -70,6 +70,7 @@ public CommandletManagerImpl(IdeContext context) { add(new EditionListCommandlet(context)); add(new VersionCommandlet(context)); add(new RepositoryCommandlet(context)); + add(new UninstallCommandlet(context)); add(new UpdateCommandlet(context)); add(new CreateCommandlet(context)); add(new Gh(context)); diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UninstallCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UninstallCommandlet.java new file mode 100644 index 000000000..10eecea34 --- /dev/null +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UninstallCommandlet.java @@ -0,0 +1,53 @@ +package com.devonfw.tools.ide.commandlet; + +import java.nio.file.Files; +import java.nio.file.Path; + +import com.devonfw.tools.ide.context.IdeContext; +import com.devonfw.tools.ide.io.FileAccess; +import com.devonfw.tools.ide.property.ToolProperty; + +/** + * An internal {@link Commandlet} to uninstall a tool. + */ +public class UninstallCommandlet extends Commandlet { + + /** The tool to uninstall. */ + public final ToolProperty tool; + + /** + * The constructor. + * + * @param context the {@link IdeContext}. + */ + public UninstallCommandlet(IdeContext context) { + + super(context); + addKeyword(getName()); + this.tool = add(new ToolProperty("", true, "tool")); + } + + @Override + public String getName() { + + return "uninstall"; + } + + @Override + public void run() { + + String commandletName = this.tool.getValue().getName(); + Path softwarePath = context.getSoftwarePath().resolve(commandletName); + if (Files.exists(softwarePath)) { + FileAccess fileAccess = context.getFileAccess(); + try { + fileAccess.delete(softwarePath); + this.context.success("Successfully uninstalled " + commandletName); + } catch (Exception e) { + throw new IllegalStateException("Couldn't uninstall " + commandletName, e); + } + } else { + this.context.info("An installed version of " + commandletName + " does not exist"); + } + } +} diff --git a/cli/src/test/java/com/devonfw/tools/ide/commandlet/UninstallCommandletTest.java b/cli/src/test/java/com/devonfw/tools/ide/commandlet/UninstallCommandletTest.java new file mode 100644 index 000000000..82808a81b --- /dev/null +++ b/cli/src/test/java/com/devonfw/tools/ide/commandlet/UninstallCommandletTest.java @@ -0,0 +1,77 @@ +package com.devonfw.tools.ide.commandlet; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; + +import java.nio.file.Files; +import java.nio.file.Path; + +import org.junit.jupiter.api.Test; + +import com.devonfw.tools.ide.context.AbstractIdeContextTest; +import com.devonfw.tools.ide.context.IdeTestContext; +import com.devonfw.tools.ide.io.FileAccess; +import com.devonfw.tools.ide.log.IdeLogLevel; + +/** + * Integration test of {@link UninstallCommandlet}. + */ +public class UninstallCommandletTest extends AbstractIdeContextTest { + + /** + * Test of {@link UninstallCommandlet} run. + * + */ + @Test + public void testUninstallCommandletRun_WithExistingCommandlet() { + + // arrange + String toolName = "npm"; + IdeTestContext context = newContext(PROJECT_BASIC); + UninstallCommandlet uninstallCommandlet = context.getCommandletManager().getCommandlet(UninstallCommandlet.class); + uninstallCommandlet.tool.setValueAsString(toolName, context); + // act + uninstallCommandlet.run(); + // assert + assertLogMessage(context, IdeLogLevel.SUCCESS, "Successfully uninstalled " + toolName); + assertThat(Files.notExists(context.getSoftwarePath().resolve(toolName))); + } + + @Test + public void testUninstallCommandletRun_WithNonExistingCommandlet() { + + // arrange + String toolName = "eclipse"; + IdeTestContext context = newContext(PROJECT_BASIC); + UninstallCommandlet uninstallCommandlet = context.getCommandletManager().getCommandlet(UninstallCommandlet.class); + uninstallCommandlet.tool.setValueAsString(toolName, context); + // act + uninstallCommandlet.run(); + // assert + assertLogMessage(context, IdeLogLevel.INFO, "An installed version of " + toolName + " does not exist"); + assertThat(Files.notExists(context.getSoftwarePath().resolve(toolName))); + } + + @Test + public void testUninstallCommandletRun_ThrowsException() { + + // arrange + String toolName = "npm"; + IdeTestContext context = newContext(PROJECT_BASIC); + + FileAccess mockFileAccess = mock(FileAccess.class); + doThrow(new IllegalStateException()).when(mockFileAccess).delete(any(Path.class)); + context.setMockFileAccess(mockFileAccess); + + UninstallCommandlet uninstallCommandlet = context.getCommandletManager().getCommandlet(UninstallCommandlet.class); + uninstallCommandlet.tool.setValueAsString(toolName, context); + // act + try { + uninstallCommandlet.run(); + } catch (IllegalStateException e) { + // assert + assertThat(e).hasMessageContaining("Couldn't uninstall " + toolName); + } + } +} 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 0cfa42719..8e0f6624c 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,5 +1,11 @@ package com.devonfw.tools.ide.context; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +import com.devonfw.tools.ide.io.FileAccess; import com.devonfw.tools.ide.io.IdeProgressBar; import com.devonfw.tools.ide.io.IdeProgressBarTestImpl; import com.devonfw.tools.ide.log.IdeLogLevel; @@ -8,11 +14,6 @@ import com.devonfw.tools.ide.repo.DefaultToolRepository; import com.devonfw.tools.ide.repo.ToolRepository; -import java.nio.file.Path; -import java.util.HashMap; -import java.util.Map; -import java.util.function.Function; - /** * Implementation of {@link IdeContext} for testing. */ @@ -26,6 +27,8 @@ public class AbstractIdeTestContext extends AbstractIdeContext { private SystemInfo systemInfo; + private FileAccess mockFileAccess; + /** * The constructor. * @@ -93,4 +96,12 @@ public void setSystemInfo(SystemInfo systemInfo) { this.systemInfo = systemInfo; } + + + /** + * @param fileAccess the {@link FileAccess} to use for testing. + */ + public void setMockFileAccess(FileAccess fileAccess){ + this.mockFileAccess = fileAccess; + } } diff --git a/cli/src/test/resources/ide-projects/basic/project/software/npm/bin/readme b/cli/src/test/resources/ide-projects/basic/project/software/npm/bin/readme new file mode 100644 index 000000000..ed366f6d3 --- /dev/null +++ b/cli/src/test/resources/ide-projects/basic/project/software/npm/bin/readme @@ -0,0 +1 @@ +this is npm software of basic \ No newline at end of file From 622884143193f96fa6eed36b2ea7360b5743a7d1 Mon Sep 17 00:00:00 2001 From: Marco Vomiero <102261609+mvomiero@users.noreply.github.com> Date: Tue, 16 Apr 2024 13:31:11 +0200 Subject: [PATCH 2/3] #204: Non existing version installation (#271) --- .../tools/ide/tool/ToolCommandlet.java | 68 ++++++++----------- .../tools/ide/url/model/UrlMetadata.java | 32 ++++----- 2 files changed, 44 insertions(+), 56 deletions(-) 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 be5a5ee10..9260188d0 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 @@ -1,11 +1,5 @@ package com.devonfw.tools.ide.tool; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.List; -import java.util.Set; - import com.devonfw.tools.ide.commandlet.Commandlet; import com.devonfw.tools.ide.common.Tag; import com.devonfw.tools.ide.common.Tags; @@ -19,6 +13,12 @@ import com.devonfw.tools.ide.property.StringListProperty; import com.devonfw.tools.ide.version.VersionIdentifier; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Set; + /** * {@link Commandlet} for a tool integrated into the IDE. */ @@ -39,8 +39,7 @@ public abstract class ToolCommandlet extends Commandlet implements Tags { * * @param context the {@link IdeContext}. * @param tool the {@link #getName() tool name}. - * @param tags the {@link #getTags() tags} classifying the tool. Should be created via {@link Set#of(Object) Set.of} - * method. + * @param tags the {@link #getTags() tags} classifying the tool. Should be created via {@link Set#of(Object) Set.of} method. */ public ToolCommandlet(IdeContext context, String tool, Set tags) { @@ -93,9 +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) { @@ -108,8 +106,7 @@ public void runTool(ProcessMode processMode, VersionIdentifier toolVersion, Stri } else { throw new UnsupportedOperationException("Not yet implemented!"); } - ProcessContext pc = this.context.newProcess().errorHandling(ProcessErrorHandling.WARNING).executable(binaryPath) - .addArgs(args); + ProcessContext pc = this.context.newProcess().errorHandling(ProcessErrorHandling.WARNING).executable(binaryPath).addArgs(args); pc.run(processMode); } @@ -133,8 +130,7 @@ public String getEdition() { } /** - * @return the {@link #getName() tool} with its {@link #getEdition() edition}. The edition will be omitted if same as - * tool. + * @return the {@link #getName() tool} with its {@link #getEdition() edition}. The edition will be omitted if same as tool. * @see #getToolWithEdition(String, String) */ protected final String getToolWithEdition() { @@ -145,8 +141,7 @@ protected final String getToolWithEdition() { /** * @param tool the tool name. * @param edition the edition. - * @return the {@link #getName() tool} with its {@link #getEdition() edition}. The edition will be omitted if same as - * tool. + * @return the {@link #getName() tool} with its {@link #getEdition() edition}. The edition will be omitted if same as tool. */ protected final static String getToolWithEdition(String tool, String edition) { @@ -165,11 +160,9 @@ public VersionIdentifier getConfiguredVersion() { } /** - * Method to be called for {@link #install(boolean)} from dependent - * {@link com.devonfw.tools.ide.commandlet.Commandlet}s. + * Method to be called for {@link #install(boolean)} from dependent {@link com.devonfw.tools.ide.commandlet.Commandlet}s. * - * @return {@code true} if the tool was newly installed, {@code false} if the tool was already installed before and - * nothing has changed. + * @return {@code true} if the tool was newly installed, {@code false} if the tool was already installed before and nothing has changed. */ public boolean install() { @@ -177,12 +170,10 @@ public boolean install() { } /** - * Performs the installation of the {@link #getName() tool} managed by this - * {@link com.devonfw.tools.ide.commandlet.Commandlet}. + * Performs the installation of the {@link #getName() tool} managed by this {@link com.devonfw.tools.ide.commandlet.Commandlet}. * * @param silent - {@code true} if called recursively to suppress verbose logging, {@code false} otherwise. - * @return {@code true} if the tool was newly installed, {@code false} if the tool was already installed before and - * nothing has changed. + * @return {@code true} if the tool was newly installed, {@code false} if the tool was already installed before and nothing has changed. */ public boolean install(boolean silent) { @@ -193,8 +184,7 @@ public boolean install(boolean silent) { * Installs or updates the managed {@link #getName() tool}. * * @param silent - {@code true} if called recursively to suppress verbose logging, {@code false} otherwise. - * @return {@code true} if the tool was newly installed, {@code false} if the tool was already installed before and - * nothing has changed. + * @return {@code true} if the tool was newly installed, {@code false} if the tool was already installed before and nothing has changed. */ protected abstract boolean doInstall(boolean silent); @@ -270,8 +260,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) { @@ -287,10 +277,8 @@ public String getInstalledEdition(Path toolPath) { } return edition; } catch (IOException e) { - throw new IllegalStateException("Couldn't determine the edition of " + getName() - + " from the directory structure of its software path " + toolPath - + ", assuming the name of the parent directory of the real path of the software path to be the edition " - + "of the tool.", e); + throw new IllegalStateException("Couldn't determine the edition of " + getName() + " from the directory structure of its software path " + toolPath + + ", assuming the name of the parent directory of the real path of the software path to be the edition " + "of the tool.", e); } } @@ -342,9 +330,11 @@ public void setVersion(String version) { */ public void setVersion(VersionIdentifier version, boolean hint) { + String edition = getEdition(); + this.context.getUrls().getVersionFolder(tool, edition, version); // CliException is thrown if the version is not existing + EnvironmentVariables variables = this.context.getVariables(); EnvironmentVariables settingsVariables = variables.getByType(EnvironmentVariablesType.SETTINGS); - String edition = getEdition(); String name = EnvironmentVariables.getToolVersionVariable(this.tool); VersionIdentifier resolvedVersion = this.context.getUrls().getVersion(this.tool, edition, version); if (version.isPattern()) { @@ -355,9 +345,8 @@ public void setVersion(VersionIdentifier version, boolean hint) { this.context.info("{}={} has been set in {}", name, version, settingsVariables.getSource()); EnvironmentVariables declaringVariables = variables.findVariable(name); if ((declaringVariables != null) && (declaringVariables != settingsVariables)) { - this.context.warning( - "The variable {} is overridden in {}. Please remove the overridden declaration in order to make the change affect.", - name, declaringVariables.getSource()); + this.context.warning("The variable {} is overridden in {}. Please remove the overridden declaration in order to make the change affect.", name, + declaringVariables.getSource()); } if (hint) { this.context.info("To install that version call the following command:"); @@ -400,9 +389,8 @@ public void setEdition(String edition, boolean hint) { this.context.info("{}={} has been set in {}", name, edition, settingsVariables.getSource()); EnvironmentVariables declaringVariables = variables.findVariable(name); if ((declaringVariables != null) && (declaringVariables != settingsVariables)) { - this.context.warning( - "The variable {} is overridden in {}. Please remove the overridden declaration in order to make the change affect.", - name, declaringVariables.getSource()); + this.context.warning("The variable {} is overridden in {}. Please remove the overridden declaration in order to make the change affect.", name, + declaringVariables.getSource()); } if (hint) { this.context.info("To install that edition call the following command:"); 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 ab2f90369..14d64cad2 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 @@ -1,12 +1,5 @@ package com.devonfw.tools.ide.url.model; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - import com.devonfw.tools.ide.cli.CliException; import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.url.model.folder.UrlEdition; @@ -15,6 +8,13 @@ import com.devonfw.tools.ide.url.model.folder.UrlVersion; import com.devonfw.tools.ide.version.VersionIdentifier; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + /** * Service to {@link #getEdition(String, String) load} an {@link UrlEdition} to get access to its versions. */ @@ -73,8 +73,7 @@ public List getSortedEditions(String tool) { /** * @param tool the name of the {@link UrlTool}. * @param edition the name of the {@link UrlEdition}. - * @return the {@link List} of {@link VersionIdentifier}s sorted descending so the latest version comes first and the - * oldest comes last. + * @return the {@link List} of {@link VersionIdentifier}s sorted descending so the latest version comes first and the oldest comes last. */ public List getSortedVersions(String tool, String edition) { @@ -98,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) { @@ -117,15 +116,16 @@ public VersionIdentifier getVersion(String tool, String edition, VersionIdentifi return vi; } } - throw new CliException("Could not find any version matching '" + version + "' for tool '" + tool + "' - potentially there are " + versions.size() - + " version(s) available in " + getEdition(tool, edition).getPath() + " but none matched!"); + throw new CliException( + "Could not find any version matching '" + version + "' for tool '" + tool + "' - potentially there are " + versions.size() + " version(s) available in " + + getEdition(tool, edition).getPath() + " but none matched!"); } /** * @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) { @@ -133,7 +133,7 @@ public UrlVersion getVersionFolder(String tool, String edition, VersionIdentifie VersionIdentifier resolvedVersion = getVersion(tool, edition, version); UrlVersion urlVersion = getEdition(tool, edition).getChild(resolvedVersion.toString()); if (urlVersion == null) { - throw new IllegalArgumentException("Version " + version + " for tool " + tool + " does not exist in edition " + edition + "."); + throw new CliException("Version " + version + " for tool " + tool + " does not exist in edition " + edition + "."); } return urlVersion; } From bd09ce4b82e387cc323a2cb996eada7c0dd86e6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Hohwiller?= Date: Tue, 16 Apr 2024 15:11:17 +0200 Subject: [PATCH 3/3] Update configuration.adoc --- documentation/configuration.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/configuration.adoc b/documentation/configuration.adoc index b2aa40471..406cd5e76 100644 --- a/documentation/configuration.adoc +++ b/documentation/configuration.adoc @@ -8,7 +8,7 @@ The configuration of the link:cli.adoc[ide] command and environment variables ta The following list shows these configuration files in the order they are loaded so files can override variables from files above in the list: 1. build in defaults (for `JAVA_HOME`, `MAVEN_ARGS`, etc.) -2. `~/ide.properties` - user specific global defaults (on windows in `%USERPROFILE%/ide.properties`) +2. `~/.ide/ide.properties` - user specific global defaults (on Windows in `%USERPROFILE%/.ide/ide.properties`) 3. `https://github.com/devonfw/ide-settings/blob/main/ide.properties[settings/ide.properties]` (`settings/ide.properties`) - project specific configurations from link:settings.adoc[settings]. 4. `workspaces/${WORKSPACE}/ide.properties` - optional workspace specific configurations (especially helpful in projects using docker). 5. `https://github.com/devonfw/ide-settings/blob/main/template/conf/ide.properties[conf/ide.properties]` - user specific configurations (e.g. `M2_REPO=~/.m2/repository`).