Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#131: Support for tool dependencies #145

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
a9f80db
#131: Support for tool dependencies
aBega2000 Nov 20, 2023
41d23ef
Merge branch 'main' of https://github.com/aBega2000/IDEasy into featu…
aBega2000 Nov 20, 2023
96f807f
Changes after review
aBega2000 Nov 22, 2023
ecd1df3
Update ToolCommandlet.java
aBega2000 Nov 22, 2023
b222aa5
#131: Changes after reviews
aBega2000 Nov 27, 2023
6080c80
Merge branch 'feature/131-support-for-tool-dependecies' of https://gi…
aBega2000 Nov 27, 2023
f7ff2c2
#131: Changes after review
aBega2000 Dec 1, 2023
e44d547
#131: Changes after review
aBega2000 Dec 13, 2023
4a6c8e9
Merge branch 'devonfw:main' into feature/131-support-for-tool-depende…
aBega2000 Dec 13, 2023
2805f0c
Merge branch 'feature/131-support-for-tool-dependecies' of https://gi…
aBega2000 Dec 13, 2023
67c5c9c
Small Change
aBega2000 Dec 13, 2023
cc8c0cb
#131: Changes after review
aBega2000 Dec 14, 2023
ed0b0f3
Merge branch 'feature/131-support-for-tool-dependecies' of https://gi…
aBega2000 Dec 14, 2023
18884c2
Merge branch 'main' into feature/131-support-for-tool-dependecies
jan-vcapgemini Jan 8, 2024
f2ed771
Merge branch 'devonfw:main' into feature/131-support-for-tool-depende…
aBega2000 Jan 8, 2024
32e4bd1
#131: Changes after review and according to VersionRange with boundar…
aBega2000 Jan 10, 2024
48e3437
Merge branch 'main' into feature/131-support-for-tool-dependecies
aBega2000 Jan 17, 2024
a3d82b9
Merge branch 'main' into feature/131-support-for-tool-dependecies
hohwille Jan 19, 2024
21781f2
#131: Resolved Conflicts
aBega2000 Feb 1, 2024
e4ad57b
Merge commit '43272714a125e23e8b47d4501a16b722cde17ab2' into feature/…
aBega2000 Feb 1, 2024
b9846f8
Merge branch 'feature/131-support-for-tool-dependecies' of https://gi…
aBega2000 Feb 1, 2024
52840dc
Merge branch 'main' of https://github.com/aBega2000/IDEasy into featu…
aBega2000 Feb 1, 2024
fbcbf31
#131: Changes after review
aBega2000 Feb 7, 2024
0a9b2b5
Merge branch 'main' into feature/131-support-for-tool-dependecies
aBega2000 Mar 7, 2024
8f04879
Merge branch 'devonfw:main' into feature/131-support-for-tool-depende…
aBega2000 Mar 12, 2024
584c5f4
Update DependencyJson.java
hohwille Mar 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 150 additions & 17 deletions cli/src/main/java/com/devonfw/tools/ide/tool/LocalToolCommandlet.java
Original file line number Diff line number Diff line change
@@ -1,31 +1,45 @@
package com.devonfw.tools.ide.tool;

import java.io.BufferedReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;

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.io.FileCopyMode;
import com.devonfw.tools.ide.json.mapping.JsonMapping;
import com.devonfw.tools.ide.log.IdeLogLevel;
import com.devonfw.tools.ide.repo.ToolRepository;
import com.devonfw.tools.ide.url.model.file.dependencyJson.DependencyInfo;
import com.devonfw.tools.ide.url.model.file.dependencyJson.DependencyJson;
import com.devonfw.tools.ide.version.VersionIdentifier;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Set;
import com.devonfw.tools.ide.version.VersionRange;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
* {@link ToolCommandlet} that is installed locally into the IDE.
*/
public abstract class LocalToolCommandlet extends ToolCommandlet {

private static final ObjectMapper MAPPER = JsonMapping.create();

private static final String DEPENDENCY_FILENAME = "dependencies.json";

/**
* The constructor.
*
* @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.
* method.
*/
public LocalToolCommandlet(IdeContext context, String tool, Set<Tag> tags) {

Expand All @@ -42,13 +56,13 @@ public Path getToolPath() {

/**
* @return the {@link Path} where the executables of the tool can be found. Typically a "bin" folder inside
* {@link #getToolPath() tool path}.
* {@link #getToolPath() tool path}.
*/
public Path getToolBinPath() {

Path toolPath = getToolPath();
Path binPath = this.context.getFileAccess()
.findFirst(toolPath, path -> path.getFileName().toString().equals("bin"), false);
Path binPath = this.context.getFileAccess().findFirst(toolPath, path -> path.getFileName().toString().equals("bin"),
false);
if ((binPath != null) && Files.isDirectory(binPath)) {
return binPath;
}
Expand All @@ -58,6 +72,12 @@ public Path getToolBinPath() {
@Override
protected boolean doInstall(boolean silent) {

if (Files.exists(getDependencyJsonPath())) {
installDependencies();
} else {
this.context.trace("No Dependencies file found");
}

VersionIdentifier configuredVersion = getConfiguredVersion();
// get installed version before installInRepo actually may install the software
VersionIdentifier installedVersion = getInstalledVersion();
Expand All @@ -68,8 +88,8 @@ protected boolean doInstall(boolean silent) {
VersionIdentifier resolvedVersion = installation.resolvedVersion();
if (resolvedVersion.equals(installedVersion) && !installation.newInstallation()) {
IdeLogLevel level = silent ? IdeLogLevel.DEBUG : IdeLogLevel.INFO;
this.context.level(level)
.log("Version {} of tool {} is already installed", installedVersion, getToolWithEdition());
this.context.level(level).log("Version {} of tool {} is already installed", installedVersion,
getToolWithEdition());
return false;
}
// we need to link the version or update the link.
Expand Down Expand Up @@ -97,7 +117,7 @@ protected boolean doInstall(boolean silent) {
* IDE installation.
*
* @param version the {@link VersionIdentifier} requested to be installed. May also be a
* {@link VersionIdentifier#isPattern() version pattern}.
* {@link VersionIdentifier#isPattern() version pattern}.
* @return the {@link ToolInstallation} in the central software repository matching the given {@code version}.
*/
public ToolInstallation installInRepo(VersionIdentifier version) {
Expand All @@ -111,7 +131,7 @@ public ToolInstallation installInRepo(VersionIdentifier version) {
* IDE installation.
*
* @param version the {@link VersionIdentifier} requested to be installed. May also be a
* {@link VersionIdentifier#isPattern() version pattern}.
* {@link VersionIdentifier#isPattern() version pattern}.
* @param edition the specific edition to install.
* @return the {@link ToolInstallation} in the central software repository matching the given {@code version}.
*/
Expand All @@ -126,7 +146,7 @@ public ToolInstallation installInRepo(VersionIdentifier version, String edition)
* IDE installation.
*
* @param version the {@link VersionIdentifier} requested to be installed. May also be a
* {@link VersionIdentifier#isPattern() version pattern}.
* {@link VersionIdentifier#isPattern() version pattern}.
* @param edition the specific edition to install.
* @param toolRepository the {@link ToolRepository} to use.
* @return the {@link ToolInstallation} in the central software repository matching the given {@code version}.
Expand Down Expand Up @@ -191,8 +211,8 @@ private ToolInstallation createToolInstallation(Path rootDir, VersionIdentifier
}
if (linkDir != rootDir) {
assert (!linkDir.equals(rootDir));
this.context.getFileAccess()
.copy(toolVersionFile, linkDir.resolve(IdeContext.FILE_SOFTWARE_VERSION), FileCopyMode.COPY_FILE_OVERRIDE);
this.context.getFileAccess().copy(toolVersionFile, linkDir.resolve(IdeContext.FILE_SOFTWARE_VERSION),
FileCopyMode.COPY_FILE_OVERRIDE);
}
return new ToolInstallation(rootDir, linkDir, binDir, resolvedVersion, newInstallation);
}
Expand All @@ -203,4 +223,117 @@ private ToolInstallation createToolInstallation(Path rootDir, VersionIdentifier
return createToolInstallation(rootDir, resolvedVersion, toolVersionFile, false);
}

/**
* Method to get the Path of the dependencies Json file
*
* @return the {@link Path} of the dependencies file for the tool
*/
public Path getDependencyJsonPath() {

Path urlsPath = this.context.getUrlsPath();
Path toolPath = urlsPath.resolve(getName()).resolve(getEdition());
return toolPath.resolve(DEPENDENCY_FILENAME);
}

private void installDependencies() {

List<DependencyInfo> dependencies = readJson();

for (DependencyInfo dependencyInfo : dependencies) {
VersionRange dependencyVersionRangeFound = dependencyInfo.getVersionRange();
String dependencyName = dependencyInfo.getTool();
ToolCommandlet dependencyTool = this.context.getCommandletManager().getToolCommandlet(dependencyName);
VersionIdentifier dependencyVersionToInstall = findDependencyVersionToInstall(dependencyInfo);
if (dependencyVersionToInstall == null) {
continue;
}
String defaultToolRepositoryId = this.context.getDefaultToolRepository().getId();
Path dependencyRepository = this.context.getSoftwareRepositoryPath().resolve(defaultToolRepositoryId)
.resolve(dependencyName).resolve(dependencyTool.getEdition());

if (versionExistsInRepository(dependencyRepository, dependencyVersionRangeFound)) {
this.context.info("Necessary version of the dependency {} is already installed in repository", dependencyName);
} else {
this.context.info("The version {} of the dependency {} is being installed", dependencyVersionToInstall,
dependencyName);
LocalToolCommandlet dependencyLocal = (LocalToolCommandlet) dependencyTool;
dependencyLocal.installInRepo(dependencyVersionToInstall);
this.context.info("The version {} of the dependency {} was successfully installed", dependencyVersionToInstall,
dependencyName);
}
}
}

private List<DependencyInfo> readJson() {

Path dependencyJsonPath = getDependencyJsonPath();

try (BufferedReader reader = Files.newBufferedReader(dependencyJsonPath)) {
DependencyJson dependencyJson = MAPPER.readValue(reader, DependencyJson.class);
return findDependenciesFromJson(dependencyJson.getDependencies(), getConfiguredVersion());
} catch (IOException e) {
throw new RuntimeException(e);
}
}

/**
* Method to search the List of versions available in the ide and find the right version to install
*
* @param dependencyFound the {@link DependencyInfo} of the dependency that was found that needs to be installed
*
* @return {@link VersionIdentifier} of the dependency that is to be installed
*/
private VersionIdentifier findDependencyVersionToInstall(DependencyInfo dependencyFound) {

String dependencyEdition = this.context.getVariables().getToolEdition(dependencyFound.getTool());

List<VersionIdentifier> versions = this.context.getUrls().getSortedVersions(dependencyFound.getTool(),
dependencyEdition);

for (VersionIdentifier vi : versions) {
if (dependencyFound.getVersionRange().contains(vi)) {
return vi;
}
}
return null;
}

/**
* Method to check if in the repository of the dependency there is a Version greater or equal to the version range to
* be installed
*
* @param dependencyRepositoryPath the {@link Path} of the dependency repository
* @param dependencyVersionRangeFound the {@link VersionRange} of the dependency version to be installed
*
* @return the {@code true} if such version exists in repository already, or {@code false} otherwise
*/
private boolean versionExistsInRepository(Path dependencyRepositoryPath, VersionRange dependencyVersionRangeFound) {

try (Stream<Path> versions = Files.list(dependencyRepositoryPath)) {
Iterator<Path> versionsIterator = versions.iterator();
while (versionsIterator.hasNext()) {
VersionIdentifier versionFound = VersionIdentifier.of(versionsIterator.next().getFileName().toString());
if (dependencyVersionRangeFound.contains(versionFound)) {
return true;
}
}
} catch (IOException e) {
throw new IllegalStateException("Failed to iterate through " + dependencyRepositoryPath, e);
}
return false;
}

private List<DependencyInfo> findDependenciesFromJson(Map<VersionRange, List<DependencyInfo>> dependencies,
VersionIdentifier toolVersionToCheck) {

for (Map.Entry<VersionRange, List<DependencyInfo>> map : dependencies.entrySet()) {

VersionRange foundToolVersionRange = map.getKey();

if (foundToolVersionRange.contains(toolVersionToCheck)) {
return map.getValue();
}
}
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.devonfw.tools.ide.url.model.file.dependencyJson;

import com.devonfw.tools.ide.version.VersionRange;

/**
* Model to represent the Object of the dependencies inside the Json file.
*/
public final class DependencyInfo {
hohwille marked this conversation as resolved.
Show resolved Hide resolved
private String tool;

private String versionRange;

/**
* @return the dependency name
*/
public String getTool() {

return tool;
}

/**
* @return the VersionRange of the dependency
*/
public VersionRange getVersionRange() {

return VersionRange.of(versionRange);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.devonfw.tools.ide.url.model.file.dependencyJson;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import com.devonfw.tools.ide.version.VersionRange;

/**
* Model to represent the Json file for dependencies to be installed.
*/
public class DependencyJson {
hohwille marked this conversation as resolved.
Show resolved Hide resolved
private final Map<VersionRange, List<DependencyInfo>> dependencies;

/**
* The constructor.
*/
public DependencyJson() {

this.dependencies = new LinkedHashMap<>();
}

/**
* @return the {@link Map} that maps the {@link VersionRange} of the tool to the list of {@link DependencyInfo}
*/
public Map<VersionRange, List<DependencyInfo>> getDependencies() {

return this.dependencies;
}
}
Loading