From b4abadd1a7ca781069feedc6caeb20b907457292 Mon Sep 17 00:00:00 2001 From: hj-lorenz Date: Wed, 26 Feb 2025 15:52:09 +0100 Subject: [PATCH 1/4] #1033: Introduce dependencies.json for jmc (#1057) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: jan-vcapgemini <59438728+jan-vcapgemini@users.noreply.github.com> Co-authored-by: Jörg Hohwiller --- cli/src/main/java/com/devonfw/tools/ide/tool/jmc/Jmc.java | 8 -------- .../ide-projects/jmc/_ide/urls/jmc/dependencies.json | 8 ++++++++ 2 files changed, 8 insertions(+), 8 deletions(-) create mode 100644 cli/src/test/resources/ide-projects/jmc/_ide/urls/jmc/dependencies.json diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/jmc/Jmc.java b/cli/src/main/java/com/devonfw/tools/ide/tool/jmc/Jmc.java index 6cc7b7135..5f37fa229 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/jmc/Jmc.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/jmc/Jmc.java @@ -13,7 +13,6 @@ import com.devonfw.tools.ide.process.ProcessMode; import com.devonfw.tools.ide.tool.LocalToolCommandlet; import com.devonfw.tools.ide.tool.ToolCommandlet; -import com.devonfw.tools.ide.tool.java.Java; /** * {@link ToolCommandlet} for JDK Mission Control, An advanced set of tools for @@ -31,13 +30,6 @@ public Jmc(IdeContext context) { super(context, "jmc", Set.of(Tag.JAVA, Tag.ANALYSE)); } - @Override - protected void installDependencies() { - - // TODO create jmc/jmc/dependencies.json file in ide-urls and delete this method - getCommandlet(Java.class).install(); - } - @Override public void run() { diff --git a/cli/src/test/resources/ide-projects/jmc/_ide/urls/jmc/dependencies.json b/cli/src/test/resources/ide-projects/jmc/_ide/urls/jmc/dependencies.json new file mode 100644 index 000000000..f02b9baa9 --- /dev/null +++ b/cli/src/test/resources/ide-projects/jmc/_ide/urls/jmc/dependencies.json @@ -0,0 +1,8 @@ +{ + "[8.1.*,9.0.0)": [ + { + "tool": "java", + "versionRange": "[11,)" + } + ] +} From 211057fa29ecad9e729ecc110ed70aa64cdbab7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Hohwiller?= Date: Wed, 26 Feb 2025 16:41:52 +0100 Subject: [PATCH 2/4] #789: implement uninstall of IDEasy (#1071) Co-authored-by: jan-vcapgemini <59438728+jan-vcapgemini@users.noreply.github.com> --- .../ide/commandlet/UninstallCommandlet.java | 22 ++- .../tools/ide/common/SimpleSystemPath.java | 85 ++++++++++ .../devonfw/tools/ide/os/WindowsHelper.java | 5 + .../tools/ide/os/WindowsHelperImpl.java | 11 ++ .../tools/ide/tool/IdeasyCommandlet.java | 159 ++++++++++++++---- cli/src/main/resources/nls/Help.properties | 4 +- cli/src/main/resources/nls/Help_de.properties | 4 +- .../commandlet/UninstallCommandletTest.java | 54 +++++- .../tools/ide/os/WindowsHelperMock.java | 6 + .../uninstall/_ide/installation/.gitkeep | 0 .../uninstall/_ide/software/.gitkeep | 0 .../ide-projects/uninstall/_ide/urls/.gitkeep | 0 .../uninstall/project/home/.bashrc | 8 + .../uninstall/project/home/.ide/.gitkeep | 0 .../uninstall/project/home/.zshrc | 9 + .../uninstall/project/settings/ide.properties | 1 + .../project/workspaces/main/.gitkeep | 0 17 files changed, 320 insertions(+), 48 deletions(-) create mode 100644 cli/src/main/java/com/devonfw/tools/ide/common/SimpleSystemPath.java create mode 100644 cli/src/test/resources/ide-projects/uninstall/_ide/installation/.gitkeep create mode 100644 cli/src/test/resources/ide-projects/uninstall/_ide/software/.gitkeep create mode 100644 cli/src/test/resources/ide-projects/uninstall/_ide/urls/.gitkeep create mode 100644 cli/src/test/resources/ide-projects/uninstall/project/home/.bashrc create mode 100644 cli/src/test/resources/ide-projects/uninstall/project/home/.ide/.gitkeep create mode 100644 cli/src/test/resources/ide-projects/uninstall/project/home/.zshrc create mode 100644 cli/src/test/resources/ide-projects/uninstall/project/settings/ide.properties create mode 100644 cli/src/test/resources/ide-projects/uninstall/project/workspaces/main/.gitkeep 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 index c5fc77c0d..0514833e0 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UninstallCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UninstallCommandlet.java @@ -2,6 +2,7 @@ import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.property.ToolProperty; +import com.devonfw.tools.ide.tool.IdeasyCommandlet; import com.devonfw.tools.ide.tool.ToolCommandlet; /** @@ -21,7 +22,7 @@ public UninstallCommandlet(IdeContext context) { super(context); addKeyword(getName()); - this.tools = add(new ToolProperty("", true, true, "tool")); + this.tools = add(new ToolProperty("", false, true, "tool")); } @Override @@ -30,10 +31,27 @@ public String getName() { return "uninstall"; } + @Override + public boolean isIdeRootRequired() { + + return this.tools.getValueCount() > 0; + } + @Override public void run() { - for (int i = 0; i < this.tools.getValueCount(); i++) { + int valueCount = this.tools.getValueCount(); + if (valueCount == 0) { + if (!this.context.isForceMode()) { + this.context.askToContinue("Sub-command uninstall without any further arguments will perform the entire uninstallation of IDEasy.\n" + + "Since this is typically not to be called manually, you may have forgotten to specify the tool to install as extra argument.\n" + + "The current command will uninstall IDEasy from your computer. Are you sure?"); + } + IdeasyCommandlet ideasy = new IdeasyCommandlet(this.context); + ideasy.uninstallIdeasy(); + return; + } + for (int i = 0; i < valueCount; i++) { ToolCommandlet toolCommandlet = this.tools.getValue(i); if (toolCommandlet.getInstalledVersion() != null) { toolCommandlet.uninstall(); diff --git a/cli/src/main/java/com/devonfw/tools/ide/common/SimpleSystemPath.java b/cli/src/main/java/com/devonfw/tools/ide/common/SimpleSystemPath.java new file mode 100644 index 000000000..ee66f436b --- /dev/null +++ b/cli/src/main/java/com/devonfw/tools/ide/common/SimpleSystemPath.java @@ -0,0 +1,85 @@ +package com.devonfw.tools.ide.common; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.function.Predicate; + +/** + * Represents the PATH variable in a structured way. Similar to {@link SystemPath} but much simper: It just tokenizes the PATH into a {@link java.util.List} of + * {@link String}s. + */ +public class SimpleSystemPath { + + private final char separator; + + private final List entries; + + private SimpleSystemPath(char separator, List entries) { + + super(); + this.separator = separator; + this.entries = entries; + } + + /** + * @return the entries of this PATH as a mutable {@link List}. + */ + public List getEntries() { + + return this.entries; + } + + /** + * Remove all entries from this PATH that match the given {@link Predicate}. + * + * @param filter the {@link Predicate} {@link Predicate#test(Object) deciding} what to filter and remove. + */ + public void removeEntries(Predicate filter) { + + Iterator iterator = this.entries.iterator(); + while (iterator.hasNext()) { + String entry = iterator.next(); + if (filter.test(entry)) { + iterator.remove(); + } + } + } + + @Override + public String toString() { + + StringBuilder sb = new StringBuilder(); + boolean first = true; + for (String entry : this.entries) { + if (first) { + first = false; + } else { + sb.append(this.separator); + } + sb.append(entry); + } + return sb.toString(); + } + + /** + * @param path the entire PATH as {@link String}, + * @param separator the separator character. + * @return the {@link SimpleSystemPath}. + */ + public static SimpleSystemPath of(String path, char separator) { + + List entries = new ArrayList<>(); + int start = 0; + int len = path.length(); + while (start < len) { + int end = path.indexOf(separator, start); + if (end < 0) { + end = len; + } + entries.add(path.substring(start, end)); + start = end + 1; + } + return new SimpleSystemPath(separator, entries); + } +} diff --git a/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelper.java b/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelper.java index 8500b3bce..a0a9a6eb7 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelper.java +++ b/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelper.java @@ -14,6 +14,11 @@ public interface WindowsHelper { */ void setUserEnvironmentValue(String key, String value); + /** + * @param key the name of the environment variable to remove. + */ + void removeUserEnvironmentValue(String key); + /** * @param key the name of the environment variable. * @return the value of the environment variable in the users context. diff --git a/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java b/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java index b57a975ce..d22a162ce 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/os/WindowsHelperImpl.java @@ -3,6 +3,7 @@ import java.util.List; import com.devonfw.tools.ide.context.IdeContext; +import com.devonfw.tools.ide.log.IdeLogLevel; import com.devonfw.tools.ide.process.ProcessMode; import com.devonfw.tools.ide.process.ProcessResult; @@ -33,6 +34,16 @@ public void setUserEnvironmentValue(String key, String value) { assert (result.isSuccessful()); } + @Override + public void removeUserEnvironmentValue(String key) { + ProcessResult result = this.context.newProcess().executable("reg").addArgs("delete", HKCU_ENVIRONMENT, "/v", key, "/f").run(ProcessMode.DEFAULT_CAPTURE); + if (result.isSuccessful()) { + this.context.debug("Removed environment variable {}", key); + } else { + result.log(IdeLogLevel.WARNING, this.context); + } + } + @Override public String getUserEnvironmentValue(String key) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/IdeasyCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/tool/IdeasyCommandlet.java index 76e91673e..c7d742c4d 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/IdeasyCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/IdeasyCommandlet.java @@ -9,11 +9,13 @@ import com.devonfw.tools.ide.cli.CliException; import com.devonfw.tools.ide.commandlet.UpgradeMode; +import com.devonfw.tools.ide.common.SimpleSystemPath; import com.devonfw.tools.ide.common.Tag; import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.io.FileAccess; import com.devonfw.tools.ide.os.WindowsHelper; import com.devonfw.tools.ide.os.WindowsPathSyntax; +import com.devonfw.tools.ide.process.ProcessMode; import com.devonfw.tools.ide.tool.mvn.MvnArtifact; import com.devonfw.tools.ide.tool.mvn.MvnBasedLocalToolCommandlet; import com.devonfw.tools.ide.tool.repository.MavenRepository; @@ -33,6 +35,10 @@ public class IdeasyCommandlet extends MvnBasedLocalToolCommandlet { /** The {@link #getName() tool name}. */ public static final String TOOL_NAME = "ideasy"; + public static final String BASHRC = ".bashrc"; + public static final String ZSHRC = ".zshrc"; + public static final String IDE_BIN = "\\_ide\\bin"; + public static final String IDE_INSTALLATION_BIN = "\\_ide\\installation\\bin"; private final UpgradeMode mode; @@ -168,8 +174,8 @@ public void installIdeasy(Path cwd) { fileAccess.copy(installationArtifact, ideasyVersionPath); } fileAccess.symlink(ideasyVersionPath, installationPath); - addToShellRc(".bashrc", ideRoot, null); - addToShellRc(".zshrc", ideRoot, "autoload -U +X bashcompinit && bashcompinit"); + addToShellRc(BASHRC, ideRoot, null); + addToShellRc(ZSHRC, ideRoot, "autoload -U +X bashcompinit && bashcompinit"); installIdeasyWindowsEnv(ideRoot, installationPath); this.context.success("IDEasy has been installed successfully on your system."); this.context.warning("IDEasy has been setup for new shells but it cannot work in your current shell(s).\n" @@ -177,35 +183,40 @@ public void installIdeasy(Path cwd) { } private void installIdeasyWindowsEnv(Path ideRoot, Path installationPath) { - if (this.context.getSystemInfo().isWindows()) { - WindowsHelper helper = WindowsHelper.get(this.context); - helper.setUserEnvironmentValue(IdeVariables.IDE_ROOT.getName(), ideRoot.toString()); - String userPath = helper.getUserEnvironmentValue(IdeVariables.PATH.getName()); - if (userPath == null) { - this.context.error("Could not read user PATH from registry!"); + if (!this.context.getSystemInfo().isWindows()) { + return; + } + WindowsHelper helper = WindowsHelper.get(this.context); + helper.setUserEnvironmentValue(IdeVariables.IDE_ROOT.getName(), ideRoot.toString()); + String userPath = helper.getUserEnvironmentValue(IdeVariables.PATH.getName()); + if (userPath == null) { + this.context.error("Could not read user PATH from registry!"); + } else { + this.context.info("Found user PATH={}", userPath); + Path ideasyBinPath = installationPath.resolve("bin"); + SimpleSystemPath path = SimpleSystemPath.of(userPath, ';'); + if (path.getEntries().isEmpty()) { + this.context.warning("ATTENTION:\n" + + "Your user specific PATH variable seems to be empty.\n" + + "You can double check this by pressing [Windows][r] and launch the program SystemPropertiesAdvanced.\n" + + "Then click on 'Environment variables' and check if 'PATH' is set in the 'user variables' from the upper list.\n" + + "In case 'PATH' is defined there non-empty and you get this message, please abort and give us feedback:\n" + + "https://github.com/devonfw/IDEasy/issues\n" + + "Otherwise all is correct and you can continue."); + this.context.askToContinue("Are you sure you want to override your PATH?"); } else { - this.context.info("Found user PATH={}", userPath); - Path ideasyBinPath = installationPath.resolve("bin"); - userPath = removeObsoleteEntryFromWindowsPath(userPath); - if (userPath.isEmpty()) { - this.context.warning("ATTENTION:\n" - + "Your user specific PATH variable seems to be empty.\n" - + "You can double check this by pressing [Windows][r] and launch the program SystemPropertiesAdvanced.\n" - + "Then click on 'Environment variables' and check if 'PATH' is set in the 'user variables' from the upper list.\n" - + "In case 'PATH' is defined there non-empty and you get this message, please abort and give us feedback:\n" - + "https://github.com/devonfw/IDEasy/issues\n" - + "Otherwise all is correct and you can continue."); - this.context.askToContinue("Are you sure you want to override your PATH?"); - userPath = ideasyBinPath.toString(); - } else { - userPath = userPath + ";" + ideasyBinPath; - } - helper.setUserEnvironmentValue(IdeVariables.PATH.getName(), userPath); + path.removeEntries(s -> s.endsWith(IDE_BIN)); } + path.getEntries().add(ideasyBinPath.toString()); + helper.setUserEnvironmentValue(IdeVariables.PATH.getName(), path.toString()); } } static String removeObsoleteEntryFromWindowsPath(String userPath) { + return removeEntryFromWindowsPath(userPath, IDE_BIN); + } + + static String removeEntryFromWindowsPath(String userPath, String suffix) { int len = userPath.length(); int start = 0; while ((start >= 0) && (start < len)) { @@ -214,7 +225,7 @@ static String removeObsoleteEntryFromWindowsPath(String userPath) { end = len; } String entry = userPath.substring(start, end); - if (entry.endsWith("\\_ide\\bin")) { + if (entry.endsWith(suffix)) { String prefix = ""; int offset = 1; if (start > 0) { @@ -224,7 +235,7 @@ static String removeObsoleteEntryFromWindowsPath(String userPath) { if (end == len) { return prefix; } else { - return prefix + userPath.substring(end + offset); + return removeEntryFromWindowsPath(prefix + userPath.substring(end + offset), suffix); } } start = end + 1; @@ -240,11 +251,34 @@ static String removeObsoleteEntryFromWindowsPath(String userPath) { */ private void addToShellRc(String filename, Path ideRoot, String extraLine) { - this.context.info("Configuring IDEasy in {}", filename); + modifyShellRc(filename, ideRoot, true, extraLine); + } + + private void removeFromShellRc(String filename, Path ideRoot) { + + modifyShellRc(filename, ideRoot, false, null); + } + + /** + * Adds ourselves to the shell RC (run-commands) configuration file. + * + * @param filename the name of the RC file. + * @param ideRoot the IDE_ROOT {@link Path}. + */ + private void modifyShellRc(String filename, Path ideRoot, boolean add, String extraLine) { + + if (add) { + this.context.info("Configuring IDEasy in {}", filename); + } else { + this.context.info("Removing IDEasy from {}", filename); + } Path rcFile = this.context.getUserHome().resolve(filename); FileAccess fileAccess = this.context.getFileAccess(); List lines = fileAccess.readFileLines(rcFile); if (lines == null) { + if (!add) { + return; + } lines = new ArrayList<>(); } else { // since it is unspecified if the returned List may be immutable we want to get sure @@ -263,14 +297,17 @@ private void addToShellRc(String filename, Path ideRoot, String extraLine) { extraLine = null; } } - if (extraLine != null) { - lines.add(extraLine); - } - if (!this.context.getSystemInfo().isWindows()) { - lines.add("export IDE_ROOT=\"" + WindowsPathSyntax.MSYS.format(ideRoot) + "\""); + if (add) { + if (extraLine != null) { + lines.add(extraLine); + } + if (!this.context.getSystemInfo().isWindows()) { + lines.add("export IDE_ROOT=\"" + WindowsPathSyntax.MSYS.format(ideRoot) + "\""); + } + lines.add(BASH_CODE_SOURCE_FUNCTIONS); } - lines.add(BASH_CODE_SOURCE_FUNCTIONS); fileAccess.writeFileLines(lines, rcFile); + this.context.debug("Successfully updated {}", filename); } private static boolean isObsoleteRcLine(String line) { @@ -317,4 +354,58 @@ private Path determineIdeRoot(Path cwd) { return ideRoot; } + /** + * Uninstalls IDEasy entirely from the system. + */ + public void uninstallIdeasy() { + + Path ideRoot = this.context.getIdeRoot(); + removeFromShellRc(BASHRC, ideRoot); + removeFromShellRc(ZSHRC, ideRoot); + Path idePath = this.context.getIdePath(); + uninstallIdeasyWindowsEnv(ideRoot); + uninstallIdeasyIdePath(idePath); + this.context.success("IDEasy has been uninstalled from your system."); + this.context.interaction("ATTENTION:\n" + + "In order to prevent data-loss, we do not delete your projects and git repositories!\n" + + "To entirely get rid of IDEasy, also check your IDE_ROOT folder at:\n" + + "{}", ideRoot); + } + + private void uninstallIdeasyIdePath(Path idePath) { + if (this.context.getSystemInfo().isWindows()) { + this.context.newProcess().executable("bash").addArgs("-c", + "sleep 10 && rm -rf \"" + WindowsPathSyntax.MSYS.format(idePath) + "\"").run(ProcessMode.BACKGROUND); + this.context.interaction("To prevent windows file locking errors, we perform an asynchronous deletion of {} in background now.\n" + + "Please close all terminals and wait a minute for the deletion to complete before running other commands.", idePath); + } else { + this.context.info("Finally deleting {}", idePath); + this.context.getFileAccess().delete(idePath); + } + } + + private void uninstallIdeasyWindowsEnv(Path ideRoot) { + if (!this.context.getSystemInfo().isWindows()) { + return; + } + WindowsHelper helper = WindowsHelper.get(this.context); + helper.removeUserEnvironmentValue(IdeVariables.IDE_ROOT.getName()); + String userPath = helper.getUserEnvironmentValue(IdeVariables.PATH.getName()); + if (userPath == null) { + this.context.error("Could not read user PATH from registry!"); + } else { + this.context.info("Found user PATH={}", userPath); + String newUserPath = userPath; + if (!userPath.isEmpty()) { + SimpleSystemPath path = SimpleSystemPath.of(userPath, ';'); + path.removeEntries(s -> s.endsWith(IDE_BIN) || s.endsWith(IDE_INSTALLATION_BIN)); + newUserPath = path.toString(); + } + if (newUserPath.equals(userPath)) { + this.context.error("Could not find IDEasy in PATH:\n{}", userPath); + } else { + helper.setUserEnvironmentValue(IdeVariables.PATH.getName(), newUserPath); + } + } + } } diff --git a/cli/src/main/resources/nls/Help.properties b/cli/src/main/resources/nls/Help.properties index 3c7ac3fa8..d8654be8c 100644 --- a/cli/src/main/resources/nls/Help.properties +++ b/cli/src/main/resources/nls/Help.properties @@ -102,10 +102,10 @@ cmd.terraform.detail=Terraform is an infrastructure as code tool for managing cl cmd.tomcat=Tool commandlet for Tomcat cmd.tomcat.detail=Tomcat is an open-source web server and Servlet container for Java code. cmd.tomcat.val.command=Action to perform ( START | STOP ) -cmd.uninstall=Uninstall the selected tool. +cmd.uninstall=Uninstall selected tool(s). cmd.uninstall-plugin=Uninstall the selected plugin for the selected tool. cmd.uninstall-plugin.detail=Plugins can be only installed or uninstalled for tools that support such. Using the command "ide install-plugin", an uninstalled plugin can be restored. -cmd.uninstall.detail=Can be used to uninstall any tool e.g. to uninstall java simply type: 'uninstall java'. +cmd.uninstall.detail=Can be used to uninstall selected tool(s). E.g. to uninstall java simply call 'ide uninstall java'. To uninstall IDEasy itself, run 'ide uninstall' without further arguments. cmd.update=Pull your settings and apply updates (software, configuration and repositories). cmd.update.detail=To update your IDE (if instructed by your ide-admin), you only need to run the following command: 'ide update'. cmd.upgrade=Upgrade the version of IDEasy to the latest version available. diff --git a/cli/src/main/resources/nls/Help_de.properties b/cli/src/main/resources/nls/Help_de.properties index 1522aa9c1..c0ab49d81 100644 --- a/cli/src/main/resources/nls/Help_de.properties +++ b/cli/src/main/resources/nls/Help_de.properties @@ -102,10 +102,10 @@ cmd.terraform.detail=Terraform ist ein Tool für Infrastructure as Code zur Verw cmd.tomcat=Werkzeug Kommando für Tomcat cmd.tomcat.detail=Tomcat ist ein leistungsstarker Open-Source-Webserver und Servlet-Container für Java-Anwendungen. cmd.tomcat.val.command=Auszuführende Aktion ( START | STOP ) -cmd.uninstall=Deinstalliert das ausgewählte Werkzeug. +cmd.uninstall=Deinstalliert ausgewählte Werkzeug(e). cmd.uninstall-plugin=Deinstalliert die selektierte Erweiterung für das selektierte Werkzeug. cmd.uninstall-plugin.detail=Erweiterung können nur für Werkzeuge installiert und deinstalliert werden die diese unterstützen. Mit dem Befehl "ide install-plugin" kann die Erweiterung wieder hergestellt werden. -cmd.uninstall.detail=Wird dazu verwendet um jedwedes Werkzeug zu deinstallieren. Um z.B. Java zu deinstallieren geben Sie einfach 'uninstall java' in die Konsole ein. +cmd.uninstall.detail=Wird dazu verwendet um ausgewählte Werkzeuge zu deinstallieren. Um z.B. Java zu deinstallieren, dient der Befehl 'ide uninstall java'. Um IDEasy selbst zu installieren, dient der Befehl 'ide uninstall' ohne weitere Parameter. cmd.update=Updatet die Settings, Software und Repositories. cmd.update.detail=Um die IDE auf den neuesten Stand zu bringen (falls von Ihrem Admin angewiesen) geben Sie einfach 'ide update' in die Konsole ein. cmd.upgrade=Aktualisiere IDEasy auf die neueste Version. 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 index 7e147b302..98ac5b284 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/commandlet/UninstallCommandletTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/commandlet/UninstallCommandletTest.java @@ -3,14 +3,20 @@ import java.nio.file.Files; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import com.devonfw.tools.ide.context.AbstractIdeContextTest; import com.devonfw.tools.ide.context.IdeTestContext; import com.devonfw.tools.ide.log.IdeLogEntry; +import com.devonfw.tools.ide.os.SystemInfo; +import com.devonfw.tools.ide.os.SystemInfoMock; +import com.devonfw.tools.ide.os.WindowsHelper; import com.devonfw.tools.ide.property.ToolProperty; import com.devonfw.tools.ide.tool.dotnet.DotNet; import com.devonfw.tools.ide.tool.eclipse.Eclipse; import com.devonfw.tools.ide.tool.npm.Npm; +import com.devonfw.tools.ide.variable.IdeVariables; /** * Integration test of {@link UninstallCommandlet}. @@ -21,13 +27,13 @@ public class UninstallCommandletTest extends AbstractIdeContextTest { * Test of {@link UninstallCommandlet} run. */ @Test - public void testUninstallCommandletRun_WithExistingCommandlet() { + public void testUninstallNpmAndDontButDotNetNotInstalled() { // arrange String npm = "npm"; String dotnet = "dotnet"; IdeTestContext context = newContext(PROJECT_BASIC); - CommandletManager commandletManager = getCommandletManager(context); + CommandletManager commandletManager = context.getCommandletManager(); UninstallCommandlet uninstallCommandlet = commandletManager.getCommandlet(UninstallCommandlet.class); Npm npmCommandlet = commandletManager.getCommandlet(Npm.class); DotNet dotnetCommandlet = commandletManager.getCommandlet(DotNet.class); @@ -45,12 +51,12 @@ public void testUninstallCommandletRun_WithExistingCommandlet() { } @Test - public void testUninstallCommandletRun_WithNonExistingCommandlet() { + public void testUninstallEclipseFailsWhenNotInstalled() { // arrange String eclipse = "eclipse"; IdeTestContext context = newContext(PROJECT_BASIC); - CommandletManager commandletManager = getCommandletManager(context); + CommandletManager commandletManager = context.getCommandletManager(); UninstallCommandlet uninstallCommandlet = commandletManager.getCommandlet(UninstallCommandlet.class); Eclipse eclipseCommandlet = commandletManager.getCommandlet(Eclipse.class); uninstallCommandlet.tools.addValue(eclipseCommandlet); @@ -62,12 +68,12 @@ public void testUninstallCommandletRun_WithNonExistingCommandlet() { } @Test - public void testUninstallCommandletRun() { + public void testUninstallNpm() { // arrange IdeTestContext context = newContext(PROJECT_BASIC); - CommandletManager commandletManager = getCommandletManager(context); + CommandletManager commandletManager = context.getCommandletManager(); UninstallCommandlet uninstallCommandlet = commandletManager.getCommandlet(UninstallCommandlet.class); Npm npmCommandlet = commandletManager.getCommandlet(Npm.class); uninstallCommandlet.tools.addValue(npmCommandlet); @@ -78,8 +84,40 @@ public void testUninstallCommandletRun() { assertThat(context).log().hasEntries(IdeLogEntry.ofSuccess("Successfully uninstalled npm")); } - private CommandletManager getCommandletManager(IdeTestContext context) { + /** Test {@link UninstallCommandlet} without arguments uninstalls IDEasy. */ + @ParameterizedTest + @ValueSource(strings = { "windows", "mac", "linux" }) + public void testUninstallIdeasy(String os) { - return context.getCommandletManager(); + // arrange + SystemInfo systemInfo = SystemInfoMock.of(os); + IdeTestContext context = newContext("uninstall"); + context.setSystemInfo(systemInfo); + CommandletManager commandletManager = context.getCommandletManager(); + UninstallCommandlet uninstallCommandlet = commandletManager.getCommandlet(UninstallCommandlet.class); + context.getStartContext().setForceMode(true); + WindowsHelper helper = context.getWindowsHelper(); + String originalPath = helper.getUserEnvironmentValue(IdeVariables.PATH.getName()); + if (systemInfo.isWindows()) { + helper.setUserEnvironmentValue(IdeVariables.PATH.getName(), "C:\\projects\\_ide\\installation\\bin;" + originalPath); + } + // act + uninstallCommandlet.run(); + // assert + assertThat(context.getIdePath()).doesNotExist(); + assertThat(context.getUserHome().resolve(".bashrc")).hasContent("#already exists\n" + + "alias devon=\"source ~/.devon/devon\"\n" + + "devon\n" + + "source ~/.devon/autocomplete\n"); + assertThat(context.getUserHome().resolve(".zshrc")).hasContent("#already exists\n" + + "autoload -U +X bashcompinit && bashcompinit\n" + + "alias devon=\"source ~/.devon/devon\"\n" + + "devon\n" + + "source ~/.devon/autocomplete\n"); + if (systemInfo.isWindows()) { + assertThat(helper.getUserEnvironmentValue("IDE_ROOT")).isNull(); + assertThat(helper.getUserEnvironmentValue("PATH")).isEqualTo( + "C:\\Users\\testuser\\AppData\\Local\\Microsoft\\WindowsApps;C:\\Users\\testuser\\scoop\\apps\\python\\current\\Scripts;C:\\Users\\testuser\\scoop\\apps\\python\\current;C:\\Users\\testuser\\scoop\\shims"); + } } } diff --git a/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperMock.java b/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperMock.java index 1e90a8a78..cacd679b7 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperMock.java +++ b/cli/src/test/java/com/devonfw/tools/ide/os/WindowsHelperMock.java @@ -27,6 +27,12 @@ public void setUserEnvironmentValue(String key, String value) { this.env.setProperty(key, value); } + @Override + public void removeUserEnvironmentValue(String key) { + + this.env.remove(key); + } + @Override public String getUserEnvironmentValue(String key) { diff --git a/cli/src/test/resources/ide-projects/uninstall/_ide/installation/.gitkeep b/cli/src/test/resources/ide-projects/uninstall/_ide/installation/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/cli/src/test/resources/ide-projects/uninstall/_ide/software/.gitkeep b/cli/src/test/resources/ide-projects/uninstall/_ide/software/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/cli/src/test/resources/ide-projects/uninstall/_ide/urls/.gitkeep b/cli/src/test/resources/ide-projects/uninstall/_ide/urls/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/cli/src/test/resources/ide-projects/uninstall/project/home/.bashrc b/cli/src/test/resources/ide-projects/uninstall/project/home/.bashrc new file mode 100644 index 000000000..dc2f5d69f --- /dev/null +++ b/cli/src/test/resources/ide-projects/uninstall/project/home/.bashrc @@ -0,0 +1,8 @@ +#already exists +alias devon="source ~/.devon/devon" +devon +source ~/.devon/autocomplete +source "$IDE_ROOT/_ide/functions" +ide +ide init +source "$IDE_ROOT/_ide/installation/functions" diff --git a/cli/src/test/resources/ide-projects/uninstall/project/home/.ide/.gitkeep b/cli/src/test/resources/ide-projects/uninstall/project/home/.ide/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/cli/src/test/resources/ide-projects/uninstall/project/home/.zshrc b/cli/src/test/resources/ide-projects/uninstall/project/home/.zshrc new file mode 100644 index 000000000..54e329359 --- /dev/null +++ b/cli/src/test/resources/ide-projects/uninstall/project/home/.zshrc @@ -0,0 +1,9 @@ +#already exists +autoload -U +X bashcompinit && bashcompinit +source "$IDE_ROOT/_ide/functions" +ide +ide init +alias devon="source ~/.devon/devon" +devon +source ~/.devon/autocomplete +source "$IDE_ROOT/_ide/installation/functions" diff --git a/cli/src/test/resources/ide-projects/uninstall/project/settings/ide.properties b/cli/src/test/resources/ide-projects/uninstall/project/settings/ide.properties new file mode 100644 index 000000000..ea30561d8 --- /dev/null +++ b/cli/src/test/resources/ide-projects/uninstall/project/settings/ide.properties @@ -0,0 +1 @@ +#empty diff --git a/cli/src/test/resources/ide-projects/uninstall/project/workspaces/main/.gitkeep b/cli/src/test/resources/ide-projects/uninstall/project/workspaces/main/.gitkeep new file mode 100644 index 000000000..e69de29bb From f47ce1d48a7523a55082efb9c3fa9cea579ea41e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Hohwiller?= Date: Wed, 26 Feb 2025 18:50:09 +0100 Subject: [PATCH 3/4] #1065: fix NPE on upgrade (maven repo access) (#1072) Co-authored-by: jan-vcapgemini <59438728+jan-vcapgemini@users.noreply.github.com> --- .../devonfw/tools/ide/context/IdeContext.java | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) 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 bacf693ec..bf89288ff 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 @@ -609,21 +609,24 @@ default String getMavenArgs() { */ default Path getMavenConfigurationFolder() { - if (getIdeHome() != null) { - Path confPath = getConfPath(); - Path m2Folder = confPath.resolve(Mvn.MVN_CONFIG_FOLDER); - if (!Files.isDirectory(m2Folder)) { + Path confPath = getConfPath(); + Path mvnConfFolder = null; + if (confPath != null) { + mvnConfFolder = confPath.resolve(Mvn.MVN_CONFIG_FOLDER); + if (!Files.isDirectory(mvnConfFolder)) { Path m2LegacyFolder = confPath.resolve(Mvn.MVN_CONFIG_LEGACY_FOLDER); if (Files.isDirectory(m2LegacyFolder)) { - m2Folder = m2LegacyFolder; + mvnConfFolder = m2LegacyFolder; } else { - // fallback to USER_HOME/.m2 folder - m2Folder = getUserHome().resolve(Mvn.MVN_CONFIG_LEGACY_FOLDER); + mvnConfFolder = null; // see fallback below } } - return m2Folder; } - return null; + if (mvnConfFolder == null) { + // fallback to USER_HOME/.m2 folder + mvnConfFolder = getUserHome().resolve(Mvn.MVN_CONFIG_LEGACY_FOLDER); + } + return mvnConfFolder; } /** From 20f52ca6f1d259be6a97d8700a790a7aee25fdf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Hohwiller?= Date: Wed, 26 Feb 2025 18:54:02 +0100 Subject: [PATCH 4/4] #1082: fixed start scripts for linux/mac (#1083) Co-authored-by: jan-vcapgemini <59438728+jan-vcapgemini@users.noreply.github.com> --- .../devonfw/tools/ide/commandlet/AbstractUpdateCommandlet.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/AbstractUpdateCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/AbstractUpdateCommandlet.java index 595fac3c6..2a53adf05 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/AbstractUpdateCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/AbstractUpdateCommandlet.java @@ -268,7 +268,7 @@ private void createStartScript(String ide, String workspace) { scriptContent = "#!/usr/bin/env bash\n" + "cd \"$(dirname \"$0\")\"\n" + "cd workspaces/" + workspace + "\n" - + "ide " + ide + "\n"; + + "ideasy " + ide + "\n"; } FileAccess fileAccess = this.context.getFileAccess(); fileAccess.writeFileContent(scriptContent, scriptPath);