Skip to content

Commit

Permalink
Merge branch 'devonfw:main' into feature/devonfw#13-implement-ToolCom…
Browse files Browse the repository at this point in the history
…mandlet-for-AWS-CLI
  • Loading branch information
MattesMrzik authored Nov 7, 2023
2 parents 01f9484 + ad2f006 commit bd4b999
Show file tree
Hide file tree
Showing 32 changed files with 466 additions and 313 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,29 @@ public Commandlet getCommandletByFirstKeyword(String keyword) {
}

/**
* This method gives global access to the {@link CommandletManager} instance. Typically you should have access to
* {@link IdeContext} and use {@link IdeContext#getCommandletManager()} to access the proper instance of
* {@link CommandletManager}. Only in very specific cases where there is no {@link IdeContext} available, you may use
* this method to access it (e.g. from {@link com.devonfw.tools.ide.property.CommandletProperty})
*
* @return the static instance of this {@link CommandletManager} implementation that has already been initialized.
* @throws IllegalStateException if the instance has not been previously initialized via
* {@link #getOrCreate(IdeContext)}.
*/
public static CommandletManager get() {

return getOrCreate(null);
}

/**
* This method has to be called initially from {@link IdeContext} to create the instance of this
* {@link CommandletManager} implementation. It will store that instance internally in a static variable so it can
* later be retrieved with {@link #get()}.
*
* @param context the {@link IdeContext}.
* @return the {@link CommandletManager}.
*/
public static CommandletManager of(IdeContext context) {
public static CommandletManager getOrCreate(IdeContext context) {

if (context == null) {
if (INSTANCE == null) {
Expand Down
38 changes: 38 additions & 0 deletions cli/src/main/java/com/devonfw/tools/ide/common/SystemPath.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ public class SystemPath {

private final IdeContext context;

private static final List<String> EXTENSION_PRIORITY = List.of(".exe", ".cmd", ".bat", ".msi", ".ps1", "");

/**
* The constructor.
*
Expand Down Expand Up @@ -89,6 +91,7 @@ private void collectToolPath(Path softwarePath) {
if (Files.isDirectory(bin)) {
toolPath = bin;
}
this.paths.add(0, toolPath);
this.tool2pathMap.put(child.getFileName().toString(), toolPath);
}
}
Expand All @@ -112,6 +115,41 @@ private static String getTool(Path path, Path softwarePath) {
return null;
}

private Path findBinaryInOrder(Path path, String tool) {

for (String extension : EXTENSION_PRIORITY) {

Path fileToExecute = path.resolve(tool + extension);

if (Files.exists(fileToExecute)) {
return fileToExecute;
}
}

return null;
}

public Path findBinary(Path toolPath) {
Path parent = toolPath.getParent();
String fileName = toolPath.getFileName().toString();

if (parent == null) {
for (Path path : this.paths) {
Path binaryPath = findBinaryInOrder(path, fileName);
if (binaryPath != null) {
return binaryPath;
}
}
} else {
Path binaryPath = findBinaryInOrder(parent, fileName);
if (binaryPath != null) {
return binaryPath;
}
}

return toolPath;
}

/**
* @param tool the name of the tool.
* @return the {@link Path} to the directory of the tool where the binaries can be found or {@code null} if the tool
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ public AbstractIdeContext(IdeLogLevel minLogLevel, Function<IdeLogLevel, IdeSubL
this.loggers.put(level, logger);
}
this.systemInfo = new SystemInfoImpl();
this.commandletManager = CommandletManagerImpl.of(this);
this.commandletManager = CommandletManagerImpl.getOrCreate(this);
this.fileAccess = new FileAccessImpl(this);
String workspace = WORKSPACE_MAIN;
if (userDir == null) {
Expand Down
14 changes: 14 additions & 0 deletions cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.devonfw.tools.ide.io;

import static com.devonfw.tools.ide.logging.Log.info;

import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
Expand All @@ -11,6 +13,7 @@
import java.net.http.HttpClient.Redirect;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.FileSystemException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
Expand Down Expand Up @@ -289,6 +292,17 @@ public void symlink(Path source, Path targetLink) {
Files.delete(targetLink);
}
Files.createSymbolicLink(targetLink, source);
} catch (FileSystemException e) {
if (this.context.getSystemInfo().isWindows()) {
info(
"Due to lack of permissions, Microsofts mklink with junction had to be used to create a Symlink. See https://github.com/devonfw/IDEasy/blob/main/documentation/symlinks.asciidoc for further details. Error was: "
+ e.getMessage());

context.newProcess().executable("cmd")
.addArgs("/c", "mklink", "/d", "/j", targetLink.toString(), source.toString()).run();
} else {
throw new RuntimeException(e);
}
} catch (IOException e) {
throw new IllegalStateException("Failed to create a symbolic link " + targetLink + " pointing to " + source, e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,29 +77,8 @@ public ProcessContext executable(Path command) {
if (!this.arguments.isEmpty()) {
throw new IllegalStateException("Arguments already present - did you forget to call run for previous call?");
}
Path exe = command;
if (exe.isAbsolute()) {
Path parent = command.getParent();
String filename = command.getFileName().toString();
String extension = FilenameUtil.getExtension(filename);
if (extension == null) {
if (this.context.getSystemInfo().isWindows()) {
Path cmd = parent.resolve(filename + ".cmd");
if (Files.exists(cmd)) {
exe = cmd;
} else {
cmd = parent.resolve(filename + ".bat");
if (Files.exists(cmd)) {
exe = cmd;
}
}
}
}
}
if (!exe.equals(command)) {
this.context.debug("Using " + exe.getFileName() + " for " + command);
}
this.executable = exe;

this.executable = this.context.getPath().findBinary(command);
return this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ protected String format(Commandlet valueToFormat) {
public Commandlet parse(String valueAsString) {

// needs to be initialized before calling this...
Commandlet commandlet = CommandletManagerImpl.of(null).getCommandlet(valueAsString);
Commandlet commandlet = CommandletManagerImpl.get().getCommandlet(valueAsString);
if (commandlet == null) {
throw new IllegalArgumentException(valueAsString);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ protected String format(ToolCommandlet valueToFormat) {
public ToolCommandlet parse(String valueAsString) {

// needs to be initialized before calling this...
return CommandletManagerImpl.of(null).getToolCommandlet(valueAsString);
return CommandletManagerImpl.get().getToolCommandlet(valueAsString);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.devonfw.tools.ide.tool;

import com.devonfw.tools.ide.context.IdeContext;
import com.devonfw.tools.ide.io.FileAccess;
import com.devonfw.tools.ide.process.ProcessContext;
import com.devonfw.tools.ide.process.ProcessErrorHandling;
import com.devonfw.tools.ide.repo.ToolRepository;
import com.devonfw.tools.ide.version.VersionIdentifier;

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Set;

/**
* {@link ToolCommandlet} that is installed globally.
*/
public abstract class GlobalToolCommandlet extends ToolCommandlet {

/**
* 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.
*/
public GlobalToolCommandlet(IdeContext context, String tool, Set<String> tags) {

super(context, tool, tags);
}

@Override
protected boolean isExtract() {
// for global tools we usually download installers and do not want to extract them (e.g. installer.msi file shall not be extracted)
return false;
}

@Override
protected boolean doInstall(boolean silent) {

Path binaryPath = this.context.getPath().findBinary(Path.of(getBinaryName()));
//if force mode is enabled, go through with the installation even if the tool is already installed
if (binaryPath != null && Files.exists(binaryPath) && !this.context.isForceMode()) {
this.context.debug("{} is already installed at {}", this.tool, binaryPath);
return false;
}
String edition = getEdition();
ToolRepository toolRepository = this.context.getDefaultToolRepository();
VersionIdentifier configuredVersion = getConfiguredVersion();
VersionIdentifier resolvedVersion = toolRepository.resolveVersion(this.tool, edition, configuredVersion);
// download and install the global tool
FileAccess fileAccess = this.context.getFileAccess();
Path target = toolRepository.download(this.tool, edition, resolvedVersion);
Path tmpDir = fileAccess.createTempDir(getName());
Path downloadBinaryPath = tmpDir.resolve(target.getFileName());
extract(target, downloadBinaryPath);
if (isExtract()) {
downloadBinaryPath = fileAccess.findFirst(downloadBinaryPath, Files::isExecutable, false);
}
ProcessContext pc = this.context.newProcess().errorHandling(ProcessErrorHandling.WARNING).executable(downloadBinaryPath);
int exitCode = pc.run();
fileAccess.delete(tmpDir);
fileAccess.delete(target);
if (exitCode == 0) {
this.context.success("Successfully installed {} in version {}", this.tool, resolvedVersion);
} else {
this.context.warning("{} in version {} was not successfully installed", this.tool, resolvedVersion);
return false;
}
postInstall();
return true;
}

}
Loading

0 comments on commit bd4b999

Please sign in to comment.