Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ This file documents all notable changes to https://github.com/devonfw/IDEasy[IDE

Release with new features and bugfixes:

* https://github.com/devonfw/IDEasy/issues/2345[#2345]: DOCKER_EDITION ignored on Linux
* https://github.com/devonfw/IDEasy/issues/2381[#2381]: Make node and npm pristine versioned tools with per-project npm global prefix

The full list of changes for this release can be found in https://github.com/devonfw/IDEasy/milestone/51?closed=1[milestone 2026.10.001].
Expand Down
27 changes: 26 additions & 1 deletion cli/src/main/java/com/devonfw/tools/ide/tool/NativePackage.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.devonfw.tools.ide.tool;

import java.nio.file.Path;
import java.util.List;
import java.util.Objects;

Expand All @@ -13,6 +14,7 @@ public class NativePackage {
private final List<String> extraInstallOptions;
private final List<String> setupCommands;
private final List<String> cleanupCommands;
private final List<Path> optionalNativePackageArtifactPaths;

/**
* Creates a new {@link NativePackage} with optional fields defaulting to empty lists.
Expand All @@ -25,11 +27,27 @@ public class NativePackage {
*/
public NativePackage(NativePackageManager pm, List<String> packages,
List<String> extraInstallOptions, List<String> setupCommands, List<String> cleanupCommands) {
this(pm, packages, extraInstallOptions, setupCommands, cleanupCommands, null);
}

/**
* Creates a new {@link NativePackage} with optional fields defaulting to empty lists.
*
* @param pm the specific {@link NativePackageManager}
* @param packages the packages that need to be handled.
* @param extraInstallOptions extra install options (optional)
* @param setupCommands commands to run before install (optional)
* @param cleanupCommands commands to run after uninstall (optional)
* @param optionalNativePackageArtifactPaths paths to local package artifacts (optional)
*/
public NativePackage(NativePackageManager pm, List<String> packages,
List<String> extraInstallOptions, List<String> setupCommands, List<String> cleanupCommands, List<Path> optionalNativePackageArtifactPaths) {
this.packageManager = Objects.requireNonNull(pm, "package manager must not be null");
this.packages = List.copyOf(Objects.requireNonNull(packages, "packages must not be null"));
this.extraInstallOptions = extraInstallOptions != null ? List.copyOf(extraInstallOptions) : List.of();
this.setupCommands = setupCommands != null ? List.copyOf(setupCommands) : List.of();
this.cleanupCommands = cleanupCommands != null ? List.copyOf(cleanupCommands) : List.of();
this.optionalNativePackageArtifactPaths = optionalNativePackageArtifactPaths != null ? List.copyOf(optionalNativePackageArtifactPaths) : List.of();
}

/**
Expand All @@ -39,7 +57,7 @@ public NativePackage(NativePackageManager pm, List<String> packages,
* @param packages the packages that need to be handled
*/
public NativePackage(NativePackageManager pm, List<String> packages) {
this(pm, packages, null, null, null);
this(pm, packages, null, null, null, null);
}

/**
Expand Down Expand Up @@ -88,6 +106,13 @@ public List<String> getCleanupCommands() {
return cleanupCommands;
}

/**
* @return set {@link optionalNativePackageArtifactPaths}.
*/
public List<Path> getOptionalNativePackageArtifactPaths() {
return optionalNativePackageArtifactPaths;
}

/**
* @param version the version to pin the {@link #getPackages()} to or {@code null} to install the latest available version.
* @return {@link PackageManagerCommand} for installation.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.devonfw.tools.ide.tool;

import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;

Expand Down Expand Up @@ -183,8 +184,14 @@ public PackageManagerCommand install(NativePackage nativePackage, String version
command.append(' ').append(option);
}
command.append(' ').append(this.installCommand);
for (String pkg : nativePackage.getPackages()) {
command.append(' ').append(getPackageSpec(pkg, version));
if (nativePackage.getOptionalNativePackageArtifactPaths().isEmpty()) {
for (String pkg : nativePackage.getPackages()) {
command.append(' ').append(getPackageSpec(pkg, version));
}
} else {
for (Path nativePackageArtifactPath : nativePackage.getOptionalNativePackageArtifactPaths()) {
command.append(' ').append(nativePackageArtifactPath);
}
}
commands.add(command.toString());
return new PackageManagerCommand(this, commands);
Expand Down
63 changes: 63 additions & 0 deletions cli/src/main/java/com/devonfw/tools/ide/tool/docker/Docker.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.devonfw.tools.ide.tool.docker;

import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import java.util.Set;
Expand All @@ -16,8 +17,12 @@
import com.devonfw.tools.ide.tool.GlobalToolCommandlet;
import com.devonfw.tools.ide.tool.NativePackage;
import com.devonfw.tools.ide.tool.NativePackageManager;
import com.devonfw.tools.ide.tool.PackageManagerCommand;
import com.devonfw.tools.ide.tool.ToolEdition;
import com.devonfw.tools.ide.tool.ToolEditionAndVersion;
import com.devonfw.tools.ide.tool.ToolInstallRequest;
import com.devonfw.tools.ide.tool.ToolInstallation;
import com.devonfw.tools.ide.tool.repository.ToolRepository;
import com.devonfw.tools.ide.version.VersionIdentifier;

/**
Expand All @@ -35,6 +40,10 @@ public class Docker extends GlobalToolCommandlet {

private static final Pattern DOCKER_DESKTOP_VERSION_PATTERN = Pattern.compile("^([0-9]+(?:\\.[0-9]+){1,2})");

private static final String EDITION_DOCKER = "docker";

private Path downloadedDebPackageForDocker;

/**
* The constructor.
*
Expand Down Expand Up @@ -70,6 +79,34 @@ private String detectContainerRuntime() {

@Override
protected List<NativePackage> getNativePackages() {

if (isDockerDesktopEditionConfigured()) {

List<Path> artifactPaths = (this.downloadedDebPackageForDocker == null) ? List.of() : List.of(this.downloadedDebPackageForDocker);

return List.of(
new NativePackage(
NativePackageManager.APT,
List.of("docker-desktop"),
List.of("--allow-downgrades"),
List.of(
"sudo install -m 0755 -d /etc/apt/keyrings",
"sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc",
"sudo chmod a+r /etc/apt/keyrings/docker.asc",
"echo \"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] "
+ "https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo \\\"$VERSION_CODENAME\\\") stable\" | "
+ "sudo tee /etc/apt/sources.list.d/docker.list > /dev/null",
"sudo apt update"
),
List.of(
"sudo rm -f /etc/apt/sources.list.d/docker.list",
"sudo rm -f /etc/apt/keyrings/docker.asc"
),
Comment thread
Hiepiscus marked this conversation as resolved.
artifactPaths
)
);
}
Comment thread
Hiepiscus marked this conversation as resolved.
Comment thread
Hiepiscus marked this conversation as resolved.

return List.of(
new NativePackage(
NativePackageManager.ZYPPER,
Expand Down Expand Up @@ -119,6 +156,28 @@ protected ToolEditionAndVersion adjustRequestedEdition(ToolEditionAndVersion req
return requested;
}

@Override
protected ToolInstallation doInstall(ToolInstallRequest request) {
if (isDockerDesktopEditionConfigured()) {
downloadDebPackageStepAndSetPackagePath(request.getRequested().getResolvedVersion());
}
return super.doInstall(request);
}

private void downloadDebPackageStepAndSetPackagePath(VersionIdentifier resolvedVersion) {
ToolRepository toolRepository = this.context.getDefaultToolRepository();
this.downloadedDebPackageForDocker = toolRepository.download(this.tool, EDITION_DOCKER, resolvedVersion, this);
}

@Override
protected List<PackageManagerCommand> getInstallPackageManagerCommands(VersionIdentifier resolvedVersion) {
if (!isDockerDesktopEditionConfigured()) {
return super.getInstallPackageManagerCommands(resolvedVersion);
}

return getNativePackages().stream().map(nativePackage -> nativePackage.install(null)).toList();
}

@Override
public boolean isExtract() {

Expand Down Expand Up @@ -200,6 +259,10 @@ private VersionIdentifier getRancherDesktopClientVersion() {
}
}

private boolean isDockerDesktopEditionConfigured() {
return EDITION_DOCKER.equals(getConfiguredEdition());
}

@Override
public String getToolHelpArguments() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static org.assertj.core.api.Assertions.assertThat;

import java.nio.file.Path;
import java.util.List;

import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -213,6 +214,53 @@ void testParseVersionQueryOutputForRpmBasedPackageManager() {
assertThat(NativePackageManager.DNF.parseVersionQueryOutput("1.0.0")).isEqualTo("1.0.0");
}

@Test
void installUsesPackageNameAndVersionWhenNoArtifactPathConfigured() {
NativePackage nativePackage = new NativePackage(NativePackageManager.APT, List.of("docker-desktop"));
PackageManagerCommand result = NativePackageManager.APT.install(nativePackage, "1.2.3");

List<String> commands = result.commands();
String installCommand = commands.getLast();
assertThat(installCommand).contains("apt", "install -y", "docker-desktop=1.2.3*");
assertThat(installCommand).doesNotContain(".deb");
}

@Test
void installUsesArtifactPathWhenConfiguredInsteadOfPackageName() {
Path debPath = Path.of("/tmp/downloads/docker-desktop-4.34.0-amd64");

NativePackage nativePackage = new NativePackage(NativePackageManager.APT, List.of("docker-desktop"), null, null, null, List.of(debPath));

PackageManagerCommand result = NativePackageManager.APT.install(nativePackage, null);

List<String> commands = result.commands();
String installCommand = commands.getLast();
assertThat(installCommand).contains(debPath.toString());
assertThat(installCommand).doesNotContain("docker-desktop=");
}

@Test
void installFallsBackToPackagesWhenArtifactPathListIsEmpty() {
NativePackage nativePackage = new NativePackage(NativePackageManager.APT, List.of("docker-desktop"), null, null, null);

PackageManagerCommand result = NativePackageManager.APT.install(nativePackage, null);

List<String> commands = result.commands();
String installCommand = commands.getLast();
assertThat(installCommand).contains("docker-desktop");
}

@Test
void installRejectsMismatchingPackageManager() {
NativePackage nativePackage = new NativePackage(NativePackageManager.APT, List.of("docker-desktop"), null, null, null);

PackageManagerCommand result = NativePackageManager.APT.install(nativePackage, null);

List<String> commands = result.commands();
String installCommand = commands.getLast();
assertThat(installCommand).contains("docker-desktop");
}

@Test
void testBrewInstallAndUninstallCommandsDoNotUseSudo() {
NativePackage np = NativePackage.of(NativePackageManager.BREW, "pkg1");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.devonfw.tools.ide.url.tool.docker;


import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
Expand Down Expand Up @@ -39,17 +40,14 @@ public String getTool() {
protected void addVersion(UrlVersion urlVersion) {

VersionIdentifier vid = VersionIdentifier.of(urlVersion.getName());
String version = urlVersion.getName().replaceAll("\\.", "");
// get Code for version
String body = doGetResponseBodyAsString(getVersionUrl());
String regex = "href=#" + version
// .......1.........................................................2.................
+ ".{8,12}(\r\n|\r|\n).{0,350}href=https://desktop\\.docker\\.com.*?(\\d{5,6}).*\\.exe";
String regex = "## " + Pattern.quote(urlVersion.getName())
+ ".*?\\[Windows]\\(https://desktop\\.docker\\.com/win/main/amd64/(\\d{5,6})/";
Pattern pattern = Pattern.compile(regex, Pattern.DOTALL);
Matcher matcher = pattern.matcher(body);
String code;

if (matcher.find()) {
code = matcher.group(2);
String code = matcher.group(1);
boolean success = doAddVersion(urlVersion,
getDownloadBaseUrl() + "/win/main/amd64/" + code + "/Docker%20Desktop%20Installer.exe", WINDOWS);
if (!success) {
Expand All @@ -58,14 +56,15 @@ protected void addVersion(UrlVersion urlVersion) {
if (WINDOWS_ONLY_VERSIONS.stream().noneMatch(i -> vid.compareVersion(i).isEqual())) {
doAddVersion(urlVersion, getDownloadBaseUrl() + "/mac/main/amd64/" + code + "/Docker.dmg", MAC, X64);
doAddVersion(urlVersion, getDownloadBaseUrl() + "/mac/main/arm64/" + code + "/Docker.dmg", MAC, ARM64);
doAddVersion(urlVersion, getDownloadBaseUrl() + "/linux/main/amd64/" + code + "/docker-desktop-amd64.deb", LINUX);
}
}
}

@Override
protected String getVersionUrl() {

return getVersionBaseUrl() + "/desktop/release-notes/";
return getVersionBaseUrl() + "/desktop/release-notes.md";
}

@Override
Expand Down
Loading