From b6a41bd0839f8924ee6df67be2085c31fe313119 Mon Sep 17 00:00:00 2001 From: salimbouch <145128725+salimbouch@users.noreply.github.com> Date: Mon, 8 Apr 2024 11:52:45 +0200 Subject: [PATCH] #101: #102: implement update & create commandlet (#150) --- .../com/devonfw/tools/ide/cli/Ideasy.java | 3 + .../commandlet/AbstractUpdateCommandlet.java | 148 ++++++++++++++++++ .../ide/commandlet/CommandletManager.java | 15 +- .../ide/commandlet/CommandletManagerImpl.java | 2 + .../ide/commandlet/CreateCommandlet.java | 75 +++++++++ .../ide/commandlet/UpdateCommandlet.java | 33 ++++ .../tools/ide/common/StepContainer.java | 89 +++++++++++ .../tools/ide/context/AbstractIdeContext.java | 137 +++++++++------- .../devonfw/tools/ide/context/IdeContext.java | 37 +++++ .../com/devonfw/tools/ide/io/FileAccess.java | 6 + .../devonfw/tools/ide/io/FileAccessImpl.java | 6 + .../ide/repo/CustomToolRepositoryImpl.java | 6 +- .../tools/ide/tool/CustomToolCommandlet.java | 35 +++++ cli/src/main/resources/nls/Ide.properties | 3 + cli/src/main/resources/nls/Ide_de.properties | 5 +- .../ide/commandlet/CreateCommandletTest.java | 31 ++++ .../ide/commandlet/UpdateCommandletTest.java | 57 +++++++ .../java/java/17.0.6/.ide.software.version | 1 + .../mvn/mvn/3.2.1/.ide.software.version | 1 + .../ide-projects/update/_ide/software/readme | 1 + .../update/_ide/urls/java/java/17.0.6/readme | 1 + .../update/_ide/urls/mvn/mvn/3.2.1/readme | 1 + .../ide-projects/update/_ide/urls/readme | 1 + .../ide-projects/update/project/readme | 1 + .../update/project/settings/ide.properties | 7 + .../project/settings/templates/conf/readme | 1 + .../software/mvn/.ide.software.version | 1 + .../update/project/workspaces/main/readme | 1 + .../test/resources/ide-projects/update/readme | 1 + 29 files changed, 646 insertions(+), 60 deletions(-) create mode 100644 cli/src/main/java/com/devonfw/tools/ide/commandlet/AbstractUpdateCommandlet.java create mode 100644 cli/src/main/java/com/devonfw/tools/ide/commandlet/CreateCommandlet.java create mode 100644 cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java create mode 100644 cli/src/main/java/com/devonfw/tools/ide/common/StepContainer.java create mode 100644 cli/src/main/java/com/devonfw/tools/ide/tool/CustomToolCommandlet.java create mode 100644 cli/src/test/java/com/devonfw/tools/ide/commandlet/CreateCommandletTest.java create mode 100644 cli/src/test/java/com/devonfw/tools/ide/commandlet/UpdateCommandletTest.java create mode 100644 cli/src/test/resources/ide-projects/update/_ide/software/default/java/java/17.0.6/.ide.software.version create mode 100644 cli/src/test/resources/ide-projects/update/_ide/software/default/mvn/mvn/3.2.1/.ide.software.version create mode 100644 cli/src/test/resources/ide-projects/update/_ide/software/readme create mode 100644 cli/src/test/resources/ide-projects/update/_ide/urls/java/java/17.0.6/readme create mode 100644 cli/src/test/resources/ide-projects/update/_ide/urls/mvn/mvn/3.2.1/readme create mode 100644 cli/src/test/resources/ide-projects/update/_ide/urls/readme create mode 100644 cli/src/test/resources/ide-projects/update/project/readme create mode 100644 cli/src/test/resources/ide-projects/update/project/settings/ide.properties create mode 100644 cli/src/test/resources/ide-projects/update/project/settings/templates/conf/readme create mode 100644 cli/src/test/resources/ide-projects/update/project/software/mvn/.ide.software.version create mode 100644 cli/src/test/resources/ide-projects/update/project/workspaces/main/readme create mode 100644 cli/src/test/resources/ide-projects/update/readme diff --git a/cli/src/main/java/com/devonfw/tools/ide/cli/Ideasy.java b/cli/src/main/java/com/devonfw/tools/ide/cli/Ideasy.java index d567df48a..4ba4b87f7 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/cli/Ideasy.java +++ b/cli/src/main/java/com/devonfw/tools/ide/cli/Ideasy.java @@ -86,6 +86,9 @@ public int runOrThrow(String... args) { CliArguments arguments = new CliArguments(args); this.context = initContext(arguments); + if (this.context.getIdeRoot() == null) { + return 1; + } return this.context.run(arguments); } 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 new file mode 100644 index 000000000..4843d621f --- /dev/null +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/AbstractUpdateCommandlet.java @@ -0,0 +1,148 @@ +package com.devonfw.tools.ide.commandlet; + +import com.devonfw.tools.ide.common.StepContainer; +import com.devonfw.tools.ide.context.IdeContext; +import com.devonfw.tools.ide.property.StringProperty; +import com.devonfw.tools.ide.repo.CustomTool; +import com.devonfw.tools.ide.tool.CustomToolCommandlet; +import com.devonfw.tools.ide.tool.ToolCommandlet; +import com.devonfw.tools.ide.variable.IdeVariables; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public abstract class AbstractUpdateCommandlet extends Commandlet { + + protected final StringProperty settingsRepo; + + /** + * The constructor. + * + * @param context the {@link IdeContext}. + */ + public AbstractUpdateCommandlet(IdeContext context) { + + super(context); + settingsRepo = new StringProperty("", false, "settingsRepository"); + } + + @Override + public void run() { + + updateSettings(); + Path templatesFolder = this.context.getSettingsPath().resolve(IdeContext.FOLDER_TEMPLATES); + + if (!Files.exists(templatesFolder)) { + Path legacyTemplatesFolder = this.context.getSettingsPath().resolve(IdeContext.FOLDER_LEGACY_TEMPLATES); + if (Files.exists(legacyTemplatesFolder)) { + templatesFolder = legacyTemplatesFolder; + } else { + this.context.warning("Templates folder is missing in settings repository."); + return; + } + } + + setupConf(templatesFolder, this.context.getIdeHome()); + updateSoftware(); + } + + private void setupConf(Path template, Path conf) { + + List children = this.context.getFileAccess().listChildren(template, f -> true); + for (Path child : children) { + + String basename = child.getFileName().toString(); + Path confPath = conf.resolve(basename); + + if (Files.isDirectory(child)) { + if (!Files.isDirectory(confPath)) { + this.context.getFileAccess().mkdirs(confPath); + } + setupConf(child, confPath); + } else if (Files.isRegularFile(child)) { + if (Files.isRegularFile(confPath)) { + this.context.debug("Configuration {} already exists - skipping to copy from {}", confPath, child); + } else { + if (!basename.equals("settings.xml")) { + this.context.info("Copying template {} to {}.", child, confPath); + this.context.getFileAccess().copy(child, confPath); + } + } + } + } + } + + private void updateSettings() { + + this.context.info("Updating settings repository ..."); + Path settingsPath = this.context.getSettingsPath(); + if (Files.isDirectory(settingsPath) && !this.context.getFileAccess().isEmptyDir(settingsPath)) { + // perform git pull on the settings repo + this.context.getGitContext().pull(settingsPath); + this.context.success("Successfully updated settings repository."); + } else { + // check if a settings repository is given then clone, otherwise prompt user for a repository. + String repository = settingsRepo.getValue(); + if (repository == null) { + String message = "Missing your settings at " + settingsPath + " and no SETTINGS_URL is defined.\n" + + "Further details can be found here: https://github.com/devonfw/IDEasy/blob/main/documentation/settings.asciidoc\n" + + "Please contact the technical lead of your project to get the SETTINGS_URL for your project.\n" + + "In case you just want to test IDEasy you may simply hit return to install the default settings.\n" + + "Settings URL [" + IdeContext.DEFAULT_SETTINGS_REPO_URL + "]:"; + repository = this.context.askForInput(message, IdeContext.DEFAULT_SETTINGS_REPO_URL); + } + this.context.getGitContext().pullOrClone(repository, settingsPath); + this.context.success("Successfully cloned settings repository."); + } + } + + private void updateSoftware() { + + Set toolCommandlets = new HashSet<>(); + + // installed tools in IDE_HOME/software + List softwares = this.context.getFileAccess().listChildren(this.context.getSoftwarePath(), Files::isDirectory); + for (Path software : softwares) { + String toolName = software.getFileName().toString(); + ToolCommandlet toolCommandlet = this.context.getCommandletManager().getToolCommandletOrNull(toolName); + if (toolCommandlet != null) { + toolCommandlets.add(toolCommandlet); + } + } + + // regular tools in $IDE_TOOLS + List regularTools = IdeVariables.IDE_TOOLS.get(this.context); + if (regularTools != null) { + for (String regularTool : regularTools) { + toolCommandlets.add(this.context.getCommandletManager().getToolCommandlet(regularTool)); + } + } + + // custom tools in ide-custom-tools.json + for (CustomTool customTool : this.context.getCustomToolRepository().getTools()) { + CustomToolCommandlet customToolCommandlet = new CustomToolCommandlet(this.context, customTool); + toolCommandlets.add(customToolCommandlet); + } + + // update/install the toolCommandlets + StepContainer container = new StepContainer(this.context); + for (ToolCommandlet toolCommandlet : toolCommandlets) { + try { + container.startStep(toolCommandlet.getName()); + toolCommandlet.install(false); + container.endStep(toolCommandlet.getName(), true, null); + } catch (Exception e) { + container.endStep(toolCommandlet.getName(), false, e); + } + } + // summary + if (!toolCommandlets.isEmpty()) { + container.complete(); + } + } + +} + 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 8c171e327..bb96fe882 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 @@ -39,7 +39,8 @@ public interface CommandletManager { /** * @param name the {@link Commandlet#getName() name} of the requested {@link ToolCommandlet}. - * @return the requested {@link ToolCommandlet} or {@code null} if not found. + * @return the requested {@link ToolCommandlet} if found. + * @throws IllegalArgumentException if the commandlet with the given name is not a {@link ToolCommandlet} */ default ToolCommandlet getToolCommandlet(String name) { @@ -50,4 +51,16 @@ default ToolCommandlet getToolCommandlet(String name) { throw new IllegalArgumentException("The commandlet " + name + " is not a ToolCommandlet!"); } + /** + * @param name the {@link Commandlet#getName() name} of the requested {@link ToolCommandlet}. + * @return the requested {@link ToolCommandlet} or {@code null} if not found. + */ + default ToolCommandlet getToolCommandletOrNull(String name) { + + Commandlet commandlet = getCommandlet(name); + if (commandlet instanceof ToolCommandlet) { + return (ToolCommandlet) commandlet; + } + return null; + } } 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 122ea7842..17b02a0a1 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 @@ -70,6 +70,8 @@ public CommandletManagerImpl(IdeContext context) { add(new EditionListCommandlet(context)); add(new VersionCommandlet(context)); add(new RepositoryCommandlet(context)); + add(new UpdateCommandlet(context)); + add(new CreateCommandlet(context)); add(new Gh(context)); add(new Helm(context)); add(new Java(context)); diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/CreateCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CreateCommandlet.java new file mode 100644 index 000000000..2a56579d7 --- /dev/null +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CreateCommandlet.java @@ -0,0 +1,75 @@ +package com.devonfw.tools.ide.commandlet; + +import com.devonfw.tools.ide.context.AbstractIdeContext; +import com.devonfw.tools.ide.context.IdeContext; +import com.devonfw.tools.ide.io.FileAccess; +import com.devonfw.tools.ide.property.StringProperty; + +import java.nio.file.Path; + +/** + * {@link Commandlet} to create a new IDEasy instance + */ +public class CreateCommandlet extends AbstractUpdateCommandlet { + + public final StringProperty newProject; + + + /** + * The constructor. + * + * @param context the {@link IdeContext}. + */ + public CreateCommandlet(IdeContext context) { + + super(context); + addKeyword(getName()); + newProject = add(new StringProperty("", true, "project")); + add(this.settingsRepo); + } + + @Override + public String getName() { + + return "create"; + } + + @Override + public boolean isIdeHomeRequired() { + + return false; + } + + @Override + public void run() { + + String newProjectName = newProject.getValue(); + Path newProjectPath = this.context.getIdeRoot().resolve(newProjectName); + + this.context.info("Creating new IDEasy project in {}", newProjectPath); + if (!this.context.getFileAccess().isEmptyDir(newProjectPath)) { + this.context.askToContinue("Directory " + newProjectPath + " already exists. Do you want to continue?"); + } else { + this.context.getFileAccess().mkdirs(newProjectPath); + } + + initializeProject(newProjectPath); + this.context.setIdeHome(newProjectPath); + super.run(); + updateRepositories(); + this.context.success("Successfully created new project '{}'.", newProjectName); + } + + private void initializeProject(Path newInstancePath) { + + FileAccess fileAccess = this.context.getFileAccess(); + fileAccess.mkdirs(newInstancePath.resolve(IdeContext.FOLDER_SOFTWARE)); + fileAccess.mkdirs(newInstancePath.resolve(IdeContext.FOLDER_PLUGINS)); + fileAccess.mkdirs(newInstancePath.resolve(IdeContext.FOLDER_WORKSPACES).resolve(IdeContext.WORKSPACE_MAIN)); + } + + private void updateRepositories() { + + this.context.getCommandletManager().getCommandlet(RepositoryCommandlet.class).run(); + } +} diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java new file mode 100644 index 000000000..6082ac530 --- /dev/null +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UpdateCommandlet.java @@ -0,0 +1,33 @@ +package com.devonfw.tools.ide.commandlet; + +import com.devonfw.tools.ide.context.IdeContext; +import com.devonfw.tools.ide.property.StringProperty; + +/** + * {@link Commandlet} to update settings, software and repositories + */ +public class UpdateCommandlet extends AbstractUpdateCommandlet { + + /** + * The constructor. + * + * @param context the {@link IdeContext}. + */ + public UpdateCommandlet(IdeContext context) { + + super(context); + addKeyword(getName()); + } + + @Override + public String getName() { + + return "update"; + } + + @Override + public void run() { + + super.run(); + } +} diff --git a/cli/src/main/java/com/devonfw/tools/ide/common/StepContainer.java b/cli/src/main/java/com/devonfw/tools/ide/common/StepContainer.java new file mode 100644 index 000000000..bd11e1e91 --- /dev/null +++ b/cli/src/main/java/com/devonfw/tools/ide/common/StepContainer.java @@ -0,0 +1,89 @@ +package com.devonfw.tools.ide.common; + +import com.devonfw.tools.ide.cli.CliException; +import com.devonfw.tools.ide.context.IdeContext; + +import java.util.ArrayList; +import java.util.List; + +/** + * A utility class to manage and log the progress of steps in a process. + * Each step can be started, ended with success or failure, and the overall completion + * status can be checked. + * @throws CliException if one or more steps fail. + */ +public class StepContainer { + + private final IdeContext context; + + /** List of steps that ended successfully. */ + private List successfulSteps; + + /** List of steps that failed. */ + private List failedSteps; + + /** + * The constructor. + * + * @param context the {@link IdeContext}. + */ + public StepContainer(IdeContext context) { + + this.context = context; + successfulSteps = new ArrayList<>(); + failedSteps = new ArrayList<>(); + } + + /** + * Logs the start of a step. + * + * @param stepName the name of the step. + */ + public void startStep(String stepName) { + + this.context.step("Starting step: {}", stepName); + } + + /** + * Logs the end of a step, indicating success or failure. + * + * @param stepName the name of the step. + * @param success {@code true} if the step succeeded, {@code false} otherwise. + * @param e the exception associated with the failure, or {@code null} if the step succeeded. + */ + public void endStep(String stepName, boolean success, Throwable e) { + + if (success) { + successfulSteps.add(stepName); + this.context.success("Step '{}' succeeded.", stepName); + } else { + failedSteps.add(stepName); + this.context.warning("Step '{}' failed.", stepName); + if (e != null) { + this.context.error(e); + } + } + } + + /** + * Checks the overall completion status of all steps. + * + * @throws CliException if one or more steps fail, providing a detailed summary. + */ + public void complete() { + + if (failedSteps.isEmpty()) { + this.context.success("All {} steps ended successfully!", successfulSteps.size()); + } else { + throw new CliException(String.format("%d step(s) failed (%d%%) and %d step(s) succeeded (%d%%) out of %d step(s)!", + failedSteps.size(), calculatePercentage(failedSteps.size()), successfulSteps.size(), + 100 - calculatePercentage(failedSteps.size()), successfulSteps.size() + failedSteps.size())); + } + } + + private int calculatePercentage(int count) { + + return (count * 100) / (successfulSteps.size() + failedSteps.size()); + } + +} 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 30c995813..b3d1f4dc8 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java +++ b/cli/src/main/java/com/devonfw/tools/ide/context/AbstractIdeContext.java @@ -1,5 +1,6 @@ package com.devonfw.tools.ide.context; +import com.devonfw.tools.ide.cli.CliAbortException; import com.devonfw.tools.ide.cli.CliArgument; import com.devonfw.tools.ide.cli.CliArguments; import com.devonfw.tools.ide.cli.CliException; @@ -54,45 +55,45 @@ public abstract class AbstractIdeContext implements IdeContext { private final Map loggers; - private final Path ideHome; + private Path ideHome; private final Path ideRoot; - private final Path confPath; + private Path confPath; - private final Path settingsPath; + private Path settingsPath; - private final Path softwarePath; + private Path softwarePath; - private final Path softwareRepositoryPath; + private Path softwareRepositoryPath; - private final Path pluginsPath; + private Path pluginsPath; - private final Path workspacePath; + private Path workspacePath; - private final String workspaceName; + private String workspaceName; - private final Path urlsPath; + private Path urlsPath; - private final Path tempPath; + private Path tempPath; - private final Path tempDownloadPath; + private Path tempDownloadPath; - private final Path cwd; + private Path cwd; - private final Path downloadPath; + private Path downloadPath; - private final Path toolRepository; + private Path toolRepository; - private final Path userHome; + private Path userHome; - private final Path userHomeIde; + private Path userHomeIde; - private final SystemPath path; + private SystemPath path; private final SystemInfo systemInfo; - private final EnvironmentVariables variables; + private EnvironmentVariables variables; private final FileAccess fileAccess; @@ -100,9 +101,9 @@ public abstract class AbstractIdeContext implements IdeContext { private final ToolRepository defaultToolRepository; - private final CustomToolRepository customToolRepository; + private CustomToolRepository customToolRepository; - private final DirectoryMerger workspaceMerger; + private DirectoryMerger workspaceMerger; private final Function loggerFactory; @@ -141,12 +142,12 @@ public AbstractIdeContext(IdeLogLevel minLogLevel, Function O question(String question, O... options) { 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 4090da8bb..d6ee85ffd 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 @@ -25,6 +25,8 @@ */ public interface IdeContext extends IdeLogger { + String DEFAULT_SETTINGS_REPO_URL = "https://github.com/devonfw/ide-settings"; + /** The name of the workspaces folder. */ String FOLDER_WORKSPACES = "workspaces"; @@ -118,6 +120,10 @@ public interface IdeContext extends IdeLogger { /** The default for {@link #getWorkspaceName()}. */ String WORKSPACE_MAIN = "main"; + String FOLDER_TEMPLATES = "templates"; + + String FOLDER_LEGACY_TEMPLATES = "devon"; + /** * @return {@code true} in case of quiet mode (reduced output), {@code false} otherwise. */ @@ -153,6 +159,15 @@ default boolean isOffline() { */ boolean isOnline(); + /** + * Asks the user for a single string input. + * + * @param message The information message to display. + * @param defaultValue The default value to return when no input is provided. + * @return The string input from the user, or the default value if no input is provided. + */ + String askForInput(String message, String defaultValue); + /** * @param question the question to ask. * @return {@code true} if the user answered with "yes", {@code false} otherwise ("no"). @@ -394,4 +409,26 @@ default void requireOnline(String purpose) { */ GitContext getGitContext(); + /** + * Updates the current working directory (CWD) and configures the environment paths according to the specified parameters. + * This method is central to changing the IDE's notion of where it operates, affecting where configurations, workspaces, + * settings, and other resources are located or loaded from. + * + * @param ideHome The path to the IDE home directory. + */ + default void setIdeHome(Path ideHome) { + + setCwd(ideHome, WORKSPACE_MAIN, ideHome); + } + + /** + * Updates the current working directory (CWD) and configures the environment paths according to the specified parameters. + * This method is central to changing the IDE's notion of where it operates, affecting where configurations, workspaces, + * settings, and other resources are located or loaded from. + * + * @param userDir The path to set as the current working directory. + * @param workspace The name of the workspace within the IDE's environment. + * @param ideHome The path to the IDE home directory. + */ + void setCwd(Path userDir, String workspace, Path ideHome); } diff --git a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java index 8b1260083..edcbe652d 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java +++ b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java @@ -232,4 +232,10 @@ default void extract(Path archiveFile, Path targetDir, Consumer postExtrac */ Path findExistingFile(String fileName, List searchDirs); + /** + * Checks if the given directory is empty. + * @param dir The {@link Path} object representing the directory to check. + * @return {@code true} if the directory is empty, {@code false} otherwise. + */ + boolean isEmptyDir(Path dir); } diff --git a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java index 7e03e4a76..85f30ad65 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java @@ -794,6 +794,12 @@ public List listChildren(Path dir, Predicate filter) { return children; } + @Override + public boolean isEmptyDir(Path dir) { + + return listChildren(dir, f -> true).isEmpty(); + } + @Override public Path findExistingFile(String fileName, List searchDirs) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/repo/CustomToolRepositoryImpl.java b/cli/src/main/java/com/devonfw/tools/ide/repo/CustomToolRepositoryImpl.java index 584535d7f..261b180be 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/repo/CustomToolRepositoryImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/repo/CustomToolRepositoryImpl.java @@ -189,7 +189,7 @@ private static boolean getBoolean(JsonObject json, String property, Boolean defa } return defaultValue.booleanValue(); } - ValueType valueType = json.getValueType(); + ValueType valueType = value.getValueType(); if (valueType == ValueType.TRUE) { return true; } else if (valueType == ValueType.FALSE) { @@ -213,8 +213,8 @@ private static String getString(JsonObject json, String property, String default } return defaultValue; } - require(json, ValueType.STRING); - return ((JsonString) json).getString(); + require(value, ValueType.STRING); + return ((JsonString) value).getString(); } /** diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/CustomToolCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/tool/CustomToolCommandlet.java new file mode 100644 index 000000000..1e363cc55 --- /dev/null +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/CustomToolCommandlet.java @@ -0,0 +1,35 @@ +package com.devonfw.tools.ide.tool; + +import com.devonfw.tools.ide.context.IdeContext; +import com.devonfw.tools.ide.repo.CustomTool; +import com.devonfw.tools.ide.version.VersionIdentifier; + + +public class CustomToolCommandlet extends LocalToolCommandlet { + + private CustomTool customTool; + + public CustomToolCommandlet(IdeContext context, CustomTool customTool) { + + super(context, customTool.getTool(), null); + this.customTool = customTool; + } + + @Override + public ToolInstallation installInRepo(VersionIdentifier version) { + + return installInRepo(version, this.customTool.getEdition()); + } + + @Override + public ToolInstallation installInRepo(VersionIdentifier version, String edition) { + + return installInRepo(version, edition, this.context.getCustomToolRepository()); + } + + @Override + public VersionIdentifier getConfiguredVersion() { + + return this.customTool.getVersion(); + } +} diff --git a/cli/src/main/resources/nls/Ide.properties b/cli/src/main/resources/nls/Ide.properties index 158414033..035da2360 100644 --- a/cli/src/main/resources/nls/Ide.properties +++ b/cli/src/main/resources/nls/Ide.properties @@ -12,10 +12,12 @@ cmd-get-edition=Get the edition of the selected tool. cmd-get-version=Get the version of the selected tool. cmd-gh=Tool commandlet for Github CLI. cmd-repository=setup the pre-configured git repository. +cmd-create=create a new IDEasy project. cmd-gradle=Tool commandlet for Gradle (Build-Tool) cmd-helm=Tool commandlet for Helm (Kubernetes Package Manager) cmd-help=Prints this help. cmd-install=Install the selected tool. +cmd-update=update settings, software and repositories. cmd-java=Tool commandlet for Java (OpenJDK) cmd-jmc=Tool commandlet for JDK Mission Control cmd-kotlinc=Tool commandlet for Kotlin. @@ -48,6 +50,7 @@ val-tool=The tool commandlet to select. val-version=The tool version val-set-version-version=The tool version to set. val-repository-repository=The name of the properties file of the pre-configured git repository to setup, omit to setup all active repositories. +val-create-project=The name of the new project that will be created. version-banner=Current version of IDE is {} opt--batch=enable batch mode (non-interactive) opt--debug=enable debug logging diff --git a/cli/src/main/resources/nls/Ide_de.properties b/cli/src/main/resources/nls/Ide_de.properties index 374ef169f..8774fe05b 100644 --- a/cli/src/main/resources/nls/Ide_de.properties +++ b/cli/src/main/resources/nls/Ide_de.properties @@ -14,6 +14,8 @@ cmd-repository=Richtet das vorkonfigurierte Git Repository ein. cmd-helm=Werkzeug Kommando für Helm (Kubernetes Package Manager) cmd-help=Zeigt diese Hilfe an. cmd-install=Installiert das selektierte Werkzeug. +cmd-update=Updatet die Settings, Software und Repositories. +cmd-create=Erstellt ein neues IDEasy Projekt. cmd-java=Werkzeug Kommando für Java (OpenJDK) cmd-jmc=Werkzeug Kommando für JDK Mission Control cmd-kotlinc=Werkzeug Kommando für Kotlin. @@ -39,8 +41,9 @@ val-args=Die Kommandozeilen-Argumente zur Übergabe an das Werkzeug. val-edition=Die Werkzeug Edition. val-tool=Das zu selektierende Werkzeug Kommando. val-version=Die Werkzeug Version. -val-set-version-version=Die zu setztende Werkzeug Version. +val-set-version-version=Die zu setzende Werkzeug Version. val-repository-repository=Der Name der Properties-Datei des vorkonfigurierten Git Repositories zum Einrichten. Falls nicht angegeben, werden alle aktiven Projekte eingerichtet. +val-create-project=Der Name des zu erstellende Projekts. version-banner=Die aktuelle Version der IDE ist {} opt--batch=Aktiviert den Batch-Modus (nicht-interaktive Stapelverarbeitung) opt--debug=Aktiviert Debug-Ausgaben (Fehleranalyse) diff --git a/cli/src/test/java/com/devonfw/tools/ide/commandlet/CreateCommandletTest.java b/cli/src/test/java/com/devonfw/tools/ide/commandlet/CreateCommandletTest.java new file mode 100644 index 000000000..ecda22e1c --- /dev/null +++ b/cli/src/test/java/com/devonfw/tools/ide/commandlet/CreateCommandletTest.java @@ -0,0 +1,31 @@ +package com.devonfw.tools.ide.commandlet; + +import com.devonfw.tools.ide.context.AbstractIdeContextTest; +import com.devonfw.tools.ide.context.IdeContext; +import com.devonfw.tools.ide.context.IdeTestContext; +import org.junit.jupiter.api.Test; + +import java.nio.file.Path; + +class CreateCommandletTest extends AbstractIdeContextTest { + + private final String NEW_PROJECT_NAME = "newProject"; + + @Test + public void testCreateCommandletRun() { + + // arrange + IdeTestContext context = newContext(PROJECT_BASIC); + CreateCommandlet cc = context.getCommandletManager().getCommandlet(CreateCommandlet.class); + cc.newProject.setValueAsString(NEW_PROJECT_NAME, context); + cc.settingsRepo.setValue(IdeContext.DEFAULT_SETTINGS_REPO_URL); + // act + cc.run(); + // assert + Path newProjectPath = context.getIdeRoot().resolve(NEW_PROJECT_NAME); + assertThat(newProjectPath).exists(); + assertThat(newProjectPath.resolve(IdeContext.FOLDER_PLUGINS)).exists(); + assertThat(newProjectPath.resolve(IdeContext.FOLDER_SOFTWARE)).exists(); + assertThat(newProjectPath.resolve(IdeContext.FOLDER_WORKSPACES).resolve(IdeContext.WORKSPACE_MAIN)).exists(); + } +} diff --git a/cli/src/test/java/com/devonfw/tools/ide/commandlet/UpdateCommandletTest.java b/cli/src/test/java/com/devonfw/tools/ide/commandlet/UpdateCommandletTest.java new file mode 100644 index 000000000..01df7760f --- /dev/null +++ b/cli/src/test/java/com/devonfw/tools/ide/commandlet/UpdateCommandletTest.java @@ -0,0 +1,57 @@ +package com.devonfw.tools.ide.commandlet; + +import com.devonfw.tools.ide.context.AbstractIdeContextTest; +import com.devonfw.tools.ide.context.IdeContext; +import com.devonfw.tools.ide.context.IdeTestContext; +import com.devonfw.tools.ide.log.IdeLogLevel; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +class UpdateCommandletTest extends AbstractIdeContextTest { + + private final String PROJECT_UPDATE = "update"; + + @Test + public void testRunPullSettingsAndUpdateSoftware() { + + // arrange + IdeTestContext context = newContext(PROJECT_UPDATE); + UpdateCommandlet uc = context.getCommandletManager().getCommandlet(UpdateCommandlet.class); + // act + uc.run(); + + // assert + assertLogMessage(context, IdeLogLevel.SUCCESS, "Successfully updated settings repository."); + assertThat(context.getConfPath()).exists(); + assertThat(context.getSoftwarePath().resolve("java")).exists(); + assertThat(context.getSoftwarePath().resolve("mvn")).exists(); + assertLogMessage(context, IdeLogLevel.SUCCESS, "All 2 steps ended successfully!"); + } + + @Test + public void testRunTemplatesNotFound() throws IOException { + + // arrange + IdeTestContext context = newContext(PROJECT_UPDATE); + UpdateCommandlet uc = context.getCommandletManager().getCommandlet(UpdateCommandlet.class); + deleteTemplatesFolder(context); + + // act + uc.run(); + + // assert + assertLogMessage(context, IdeLogLevel.WARNING, "Templates folder is missing in settings repository."); + } + + private void deleteTemplatesFolder(IdeContext context) throws IOException { + + Path templates = context.getSettingsPath().resolve(IdeContext.FOLDER_TEMPLATES).resolve(IdeContext.FOLDER_CONF) + .resolve("readme"); + Files.delete(templates); + Files.delete(templates.getParent()); + Files.delete(templates.getParent().getParent()); + } +} diff --git a/cli/src/test/resources/ide-projects/update/_ide/software/default/java/java/17.0.6/.ide.software.version b/cli/src/test/resources/ide-projects/update/_ide/software/default/java/java/17.0.6/.ide.software.version new file mode 100644 index 000000000..ff608a0f4 --- /dev/null +++ b/cli/src/test/resources/ide-projects/update/_ide/software/default/java/java/17.0.6/.ide.software.version @@ -0,0 +1 @@ +17.0.6 diff --git a/cli/src/test/resources/ide-projects/update/_ide/software/default/mvn/mvn/3.2.1/.ide.software.version b/cli/src/test/resources/ide-projects/update/_ide/software/default/mvn/mvn/3.2.1/.ide.software.version new file mode 100644 index 000000000..d39e90c03 --- /dev/null +++ b/cli/src/test/resources/ide-projects/update/_ide/software/default/mvn/mvn/3.2.1/.ide.software.version @@ -0,0 +1 @@ +3.2.1 diff --git a/cli/src/test/resources/ide-projects/update/_ide/software/readme b/cli/src/test/resources/ide-projects/update/_ide/software/readme new file mode 100644 index 000000000..d39f2d81c --- /dev/null +++ b/cli/src/test/resources/ide-projects/update/_ide/software/readme @@ -0,0 +1 @@ +this is the tool repository diff --git a/cli/src/test/resources/ide-projects/update/_ide/urls/java/java/17.0.6/readme b/cli/src/test/resources/ide-projects/update/_ide/urls/java/java/17.0.6/readme new file mode 100644 index 000000000..c2aeb9e0c --- /dev/null +++ b/cli/src/test/resources/ide-projects/update/_ide/urls/java/java/17.0.6/readme @@ -0,0 +1 @@ +needed for resolving version diff --git a/cli/src/test/resources/ide-projects/update/_ide/urls/mvn/mvn/3.2.1/readme b/cli/src/test/resources/ide-projects/update/_ide/urls/mvn/mvn/3.2.1/readme new file mode 100644 index 000000000..c2aeb9e0c --- /dev/null +++ b/cli/src/test/resources/ide-projects/update/_ide/urls/mvn/mvn/3.2.1/readme @@ -0,0 +1 @@ +needed for resolving version diff --git a/cli/src/test/resources/ide-projects/update/_ide/urls/readme b/cli/src/test/resources/ide-projects/update/_ide/urls/readme new file mode 100644 index 000000000..ea25f95ae --- /dev/null +++ b/cli/src/test/resources/ide-projects/update/_ide/urls/readme @@ -0,0 +1 @@ +this is the download metadata diff --git a/cli/src/test/resources/ide-projects/update/project/readme b/cli/src/test/resources/ide-projects/update/project/readme new file mode 100644 index 000000000..e3725034d --- /dev/null +++ b/cli/src/test/resources/ide-projects/update/project/readme @@ -0,0 +1 @@ +this is the IDE_HOME directory diff --git a/cli/src/test/resources/ide-projects/update/project/settings/ide.properties b/cli/src/test/resources/ide-projects/update/project/settings/ide.properties new file mode 100644 index 000000000..f37ddf32b --- /dev/null +++ b/cli/src/test/resources/ide-projects/update/project/settings/ide.properties @@ -0,0 +1,7 @@ +#******************************************************************************** +# This file contains project specific environment variables +#******************************************************************************** + +JAVA_VERSION=17.0.6 +MVN_VERSION=3.2.1 +IDE_TOOLS=java, mvn diff --git a/cli/src/test/resources/ide-projects/update/project/settings/templates/conf/readme b/cli/src/test/resources/ide-projects/update/project/settings/templates/conf/readme new file mode 100644 index 000000000..548039eaa --- /dev/null +++ b/cli/src/test/resources/ide-projects/update/project/settings/templates/conf/readme @@ -0,0 +1 @@ +this is the conf folder diff --git a/cli/src/test/resources/ide-projects/update/project/software/mvn/.ide.software.version b/cli/src/test/resources/ide-projects/update/project/software/mvn/.ide.software.version new file mode 100644 index 000000000..d39e90c03 --- /dev/null +++ b/cli/src/test/resources/ide-projects/update/project/software/mvn/.ide.software.version @@ -0,0 +1 @@ +3.2.1 diff --git a/cli/src/test/resources/ide-projects/update/project/workspaces/main/readme b/cli/src/test/resources/ide-projects/update/project/workspaces/main/readme new file mode 100644 index 000000000..72244ca5b --- /dev/null +++ b/cli/src/test/resources/ide-projects/update/project/workspaces/main/readme @@ -0,0 +1 @@ +this is the main workspace of basic diff --git a/cli/src/test/resources/ide-projects/update/readme b/cli/src/test/resources/ide-projects/update/readme new file mode 100644 index 000000000..48c8fd9cd --- /dev/null +++ b/cli/src/test/resources/ide-projects/update/readme @@ -0,0 +1 @@ +this is the IDE_ROOT directory