Skip to content

Improve dependent installations #1965

@hohwille

Description

@hohwille

Task summary

We already support tool dependencies via dependencies.json files from ide-urls.
This is properly handled here:

public ToolInstallation installAsDependency(VersionRange versionRange, ToolInstallRequest parentRequest) {
ToolInstallRequest request = new ToolInstallRequest(parentRequest);
ToolEditionAndVersion requested = new ToolEditionAndVersion(getToolWithConfiguredEdition());
request.setRequested(requested);
VersionIdentifier configuredVersion = getConfiguredVersion();
if (versionRange.contains(configuredVersion)) {
// prefer configured version if contained in version range
requested.setVersion(configuredVersion);
// return install(true, configuredVersion, processContext, null);
return install(request);
} else {
if (isIgnoreSoftwareRepo()) {
throw new IllegalStateException(
"Cannot satisfy dependency to " + this.tool + " in version " + versionRange + " for " + parentRequest.getRequested()
+ " since it is conflicting with configured version "
+ configuredVersion
+ " and this tool does not support the software repository.");
}
LOG.info(
"The tool {} requires {} in the version range {}, but your project uses version {}, which does not match."
+ " Therefore, we install a compatible version in that range.",
parentRequest.getRequested().getEdition(), this.tool, versionRange, configuredVersion);
requested.setVersion(versionRange);
return installTool(request);
}
}

As we can see we are creating a ToolInstallationRequest for the dependent installation as a child that is connected to the parent ToolInstallationRequest.
This way we can properly detect cycles in dependent installations that could lead to infinity loops:

public ToolInstallation installTool(ToolInstallRequest request) {
completeRequest(request); // most likely already done, but if installTool was called directly and not from install
if (request.isInstallLoop()) {
return toolAlreadyInstalled(request);
}

However, we also have PackageManagerBasedLocalToolCommandlet that implicitly causes a dependency to the package-manager (e.g. npm or pip).
Here we had infinity loop bugs causing stack-overflow errors in tests.
As a hackish quickfix we have this:

public ProcessResult runPackageManager(PackageManagerRequest request, boolean skipInstallation) {
completeRequest(request);
ProcessContext pc = request.getProcessContext();
ToolCommandlet pm = request.getPackageManager();
if (!skipInstallation) { // See Node.postInstallOnNewInstallation
ToolInstallRequest installRequest = new ToolInstallRequest(true);
installRequest.setProcessContext(pc.createChild());
pm.install(installRequest);
}
return pm.runTool(pc, request.getProcessMode(), request.getArgs());
}

and:

ProcessResult result = npm.runPackageManager(packageManagerRequest, true);

The idea of this story is to improve this and create a clean solution replacing the hack.
The problem is actually already visible to end-users via our logs:

$ ide install nest
No CVEs found for version 11.15.0 of tool npm.
No CVEs found for version v24.15.0 of tool node.
...
No CVEs found for version 11.0.21 of tool nest.
No CVEs found for version 11.15.0 of tool npm.
No CVEs found for version v24.15.0 of tool node.
...
Successfully installed nest in version 11.0.21 replacing previous version v24.15.0 of nest at D:\projects\test\software\node

So still we see that installation of npm and node was called twice because loop was not detected.
After this issue was fixed we should only see:

$ ide install nest
No CVEs found for version 11.15.0 of tool npm.
No CVEs found for version v24.15.0 of tool node.
...
No CVEs found for version 11.0.21 of tool nest.
...
Successfully installed nest in version v24.15.0 of nest at D:\projects\test\software\node

Additional context

In order to implement a clean fix we would somehow refactor PackageManagerRequest to somehow be compatbile with ToolInstallRequest or to have the ability to contain a ToolInstallRequest as a parent.
Then we could use the same chaining and loop detection fixing this issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    dependenciesdependencies.json (if tool A requires tool B)enhancementNew feature or requestnpmnode package managerpipPython package manager (Pip Installs Packages)ready-to-implement

    Type

    No fields configured for Task.

    Projects

    Status

    🆕 New

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions