Skip to content

Commit

Permalink
Merge branch 'main' into feature/56-implement-autocompletion
Browse files Browse the repository at this point in the history
  • Loading branch information
jan-vcapgemini committed Nov 7, 2023
2 parents 67b35db + ad2f006 commit 51d3ae0
Show file tree
Hide file tree
Showing 22 changed files with 381 additions and 257 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,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
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
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;
}

}
185 changes: 185 additions & 0 deletions cli/src/main/java/com/devonfw/tools/ide/tool/LocalToolCommandlet.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
package com.devonfw.tools.ide.tool;

import com.devonfw.tools.ide.commandlet.Commandlet;
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.log.IdeLogLevel;
import com.devonfw.tools.ide.repo.ToolRepository;
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;

/**
* {@link ToolCommandlet} that is installed locally into the IDE.
*/
public abstract class LocalToolCommandlet 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 LocalToolCommandlet(IdeContext context, String tool, Set<String> tags) {

super(context, tool, tags);
}


/**
* @return the {@link Path} where the tool is located (installed).
*/
public Path getToolPath() {

return this.context.getSoftwarePath().resolve(getName());
}

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

Path toolPath = getToolPath();
Path binPath = this.context.getFileAccess().findFirst(toolPath, path -> path.getFileName().toString().equals("bin"),
false);
if ((binPath != null) && Files.isDirectory(binPath)) {
return binPath;
}
return toolPath;
}

@Override
protected boolean doInstall(boolean silent) {

VersionIdentifier configuredVersion = getConfiguredVersion();
// install configured version of our tool in the software repository if not already installed
ToolInstallation installation = installInRepo(configuredVersion);

// check if we already have this version installed (linked) locally in IDE_HOME/software
VersionIdentifier installedVersion = getInstalledVersion();
VersionIdentifier resolvedVersion = installation.resolvedVersion();
if (isInstalledVersion(resolvedVersion, installedVersion, silent)) {
return false;
}
// we need to link the version or update the link.
Path toolPath = getToolPath();
FileAccess fileAccess = this.context.getFileAccess();
if (Files.exists(toolPath)) {
fileAccess.backup(toolPath);
}
fileAccess.symlink(installation.linkDir(), toolPath);
this.context.getPath().setPath(this.tool, installation.binDir());
if (installedVersion == null) {
this.context.success("Successfully installed {} in version {}", this.tool, resolvedVersion);
} else {
this.context.success("Successfully installed {} in version {} replacing previous version {]", this.tool,
resolvedVersion, installedVersion);
}
postInstall();
return true;
}

/**
* Performs the installation of the {@link #getName() tool} managed by this {@link Commandlet} only in the central
* software repository without touching the IDE installation.
*
* @param version the {@link VersionIdentifier} requested to be installed. May also be a
* {@link VersionIdentifier#isPattern() version pattern}.
* @return the {@link ToolInstallation} in the central software repository matching the given {@code version}.
*/
public ToolInstallation installInRepo(VersionIdentifier version) {

return installInRepo(version, getEdition());
}

/**
* Performs the installation of the {@link #getName() tool} managed by this {@link Commandlet} only in the central
* software repository without touching the IDE installation.
*
* @param version the {@link VersionIdentifier} requested to be installed. May also be a
* {@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}.
*/
public ToolInstallation installInRepo(VersionIdentifier version, String edition) {

return installInRepo(version, edition, this.context.getDefaultToolRepository());
}

/**
* Performs the installation of the {@link #getName() tool} managed by this {@link Commandlet} only in the central
* software repository without touching the IDE installation.
*
* @param version the {@link VersionIdentifier} requested to be installed. May also be a
* {@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}.
*/
public ToolInstallation installInRepo(VersionIdentifier version, String edition, ToolRepository toolRepository) {

VersionIdentifier resolvedVersion = toolRepository.resolveVersion(this.tool, edition, version);
Path toolPath = this.context.getSoftwareRepositoryPath().resolve(toolRepository.getId()).resolve(this.tool)
.resolve(edition).resolve(resolvedVersion.toString());
Path toolVersionFile = toolPath.resolve(IdeContext.FILE_SOFTWARE_VERSION);
FileAccess fileAccess = this.context.getFileAccess();
if (Files.isDirectory(toolPath)) {
if (Files.exists(toolVersionFile)) {
this.context.debug("Version {} of tool {} is already installed at {}", resolvedVersion,
getToolWithEdition(this.tool, edition), toolPath);
return createToolInstallation(toolPath, resolvedVersion, toolVersionFile);
}
this.context.warning("Deleting corrupted installation at {}", toolPath);
fileAccess.delete(toolPath);
}
Path target = toolRepository.download(this.tool, edition, resolvedVersion);
fileAccess.mkdirs(toolPath.getParent());
extract(target, toolPath);
try {
Files.writeString(toolVersionFile, resolvedVersion.toString(), StandardOpenOption.CREATE_NEW);
} catch (IOException e) {
throw new IllegalStateException("Failed to write version file " + toolVersionFile, e);
}
return createToolInstallation(toolPath, resolvedVersion, toolVersionFile);
}

private ToolInstallation createToolInstallation(Path rootDir, VersionIdentifier resolvedVersion,
Path toolVersionFile) {

Path linkDir = getMacOsHelper().findLinkDir(rootDir);
Path binDir = linkDir;
Path binFolder = binDir.resolve(IdeContext.FOLDER_BIN);
if (Files.isDirectory(binFolder)) {
binDir = binFolder;
}
if (linkDir != rootDir) {
assert (!linkDir.equals(rootDir));
this.context.getFileAccess().copy(toolVersionFile, linkDir, FileCopyMode.COPY_FILE_OVERRIDE);
}
return new ToolInstallation(rootDir, linkDir, binDir, resolvedVersion);
}

private boolean isInstalledVersion(VersionIdentifier expectedVersion, VersionIdentifier installedVersion,
boolean silent) {

if (expectedVersion.equals(installedVersion)) {
IdeLogLevel level = IdeLogLevel.INFO;
if (silent) {
level = IdeLogLevel.DEBUG;
}
this.context.level(level).log("Version {} of tool {} is already installed", installedVersion,
getToolWithEdition());
return true;
}
return false;
}

}
Loading

0 comments on commit 51d3ae0

Please sign in to comment.