diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index c2acecfcb..d44c54afd 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -6,6 +6,9 @@ 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/692[#692]: "Latest" version of Docker causes installation problems + + The full list of changes for this release can be found in https://github.com/devonfw/IDEasy/milestone/24?closed=1[milestone 2025.03.002]. == 2025.03.001 diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/repository/AbstractToolRepository.java b/cli/src/main/java/com/devonfw/tools/ide/tool/repository/AbstractToolRepository.java index 086ed9681..bb9b679dd 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/repository/AbstractToolRepository.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/repository/AbstractToolRepository.java @@ -27,7 +27,7 @@ public abstract class AbstractToolRepository implements ToolRepository { private static final int MAX_TEMP_DOWNLOADS = 9; - + /** The owning {@link IdeContext}. */ protected final IdeContext context; @@ -135,7 +135,11 @@ protected String createDownloadFilename(String tool, String edition, VersionIden StringBuilder sb = new StringBuilder(32); sb.append(tool); sb.append("-"); - sb.append(version); + if (VersionIdentifier.LATEST.equals(version)) { + sb.append("latest"); + } else { + sb.append(version); + } if (!edition.equals(tool)) { sb.append("-"); sb.append(edition); diff --git a/cli/src/main/java/com/devonfw/tools/ide/url/model/folder/AbstractUrlFolder.java b/cli/src/main/java/com/devonfw/tools/ide/url/model/folder/AbstractUrlFolder.java index 2daa4f1c8..fed985b67 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/url/model/folder/AbstractUrlFolder.java +++ b/cli/src/main/java/com/devonfw/tools/ide/url/model/folder/AbstractUrlFolder.java @@ -58,6 +58,9 @@ public int getChildCount() { public C getChild(String name) { load(false); + if ("*".equals(name)) { + return this.childMap.get("latest"); + } return this.childMap.get(name); } diff --git a/url-updater/src/main/java/com/devonfw/tools/ide/url/tool/docker/DockerDesktopUrlUpdater.java b/url-updater/src/main/java/com/devonfw/tools/ide/url/tool/docker/DockerDesktopUrlUpdater.java index 924a21deb..6000fcbcc 100644 --- a/url-updater/src/main/java/com/devonfw/tools/ide/url/tool/docker/DockerDesktopUrlUpdater.java +++ b/url-updater/src/main/java/com/devonfw/tools/ide/url/tool/docker/DockerDesktopUrlUpdater.java @@ -1,5 +1,7 @@ package com.devonfw.tools.ide.url.tool.docker; +import static java.lang.String.format; + import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -17,6 +19,21 @@ public class DockerDesktopUrlUpdater extends WebsiteUrlUpdater { VersionIdentifier.of("4.4.3"), VersionIdentifier.of("4.4.4"), VersionIdentifier.of("4.17.1"), VersionIdentifier.of("4.5.1")); + private final static String DOCKER_RELEASE_NOTES_URL = "https://docs.docker.com/desktop/release-notes/"; + + private final static String REGEX_FOR_DOCKER_VERSION = + "href=#%s" // Find the href with the readable version - %s provided by urlVersion + + ".{0,300}" // We have to look in close range for the next part (docker lists a summary at top. If this range is to big + // we'll find the latest listed version with download links that doesn't match the version we are looking for + + "href=https://desktop\\.docker\\.com" // Start of download link + + ".*?" // We don't care if its windows or mac - match as least as possible characters + + "(\\d{5,6})"; // Associated docker-version to readable version we are looking for + private final static String REGEX_FOR_DOWNLOAD_URLS = "https://desktop.docker.com/%s/main/%s/%s/"; + private final static String WIN_VERSION = "win"; + private final static String MAC_VERSION = "mac"; + private final static String AMD_ARCH_TYPE = "amd64"; + private final static String ARM_ARCH_TYPE = "arm64"; + @Override protected String getTool() { @@ -29,31 +46,79 @@ protected void addVersion(UrlVersion urlVersion) { VersionIdentifier vid = VersionIdentifier.of(urlVersion.getName()); String version = urlVersion.getName().replaceAll("\\.", ""); // get Code for version - String body = doGetResponseBodyAsString("https://docs.docker.com/desktop/release-notes/"); - String regex = "href=#" + version - // .......1.........................................................2................. - + ".{8,12}(\r\n|\r|\n).{0,350}href=https://desktop\\.docker\\.com.*?(\\d{5,6}).*\\.exe"; - Pattern pattern = Pattern.compile(regex, Pattern.DOTALL); - Matcher matcher = pattern.matcher(body); + String body = doGetResponseBodyAsString(DOCKER_RELEASE_NOTES_URL); + String regexForDockerVersion = format(REGEX_FOR_DOCKER_VERSION, version); + Pattern patternForDockerVersion = Pattern.compile(regexForDockerVersion, Pattern.DOTALL); + Matcher matcherForDockerVersion = patternForDockerVersion.matcher(body); String code; - if (matcher.find()) { - code = matcher.group(2); - boolean success = doAddVersion(urlVersion, - "https://desktop.docker.com/win/main/amd64/" + code + "/Docker%20Desktop%20Installer.exe", WINDOWS); - if (!success) { - return; - } + if (matcherForDockerVersion.find()) { + code = matcherForDockerVersion.group(1); + addVersionsForWindows(urlVersion, code, body); if (!WINDOWS_ONLY_VERSIONS.stream().anyMatch(i -> vid.compareVersion(i).isEqual())) { - doAddVersion(urlVersion, "https://desktop.docker.com/mac/main/amd64/" + code + "/Docker.dmg", MAC, X64); - doAddVersion(urlVersion, "https://desktop.docker.com/mac/main/arm64/" + code + "/Docker.dmg", MAC, ARM64); + addVersionsForMac(urlVersion, code, body); } } } + /** + * Adds the windows versions for docker if they exist + * + * @param urlVersion the readable version e.g. 4332 (4.33.2) + * @param dockerVersion the associated docker version to readable version e.g. 179689 + * @param body the html body to search in + */ + private void addVersionsForWindows(UrlVersion urlVersion, String dockerVersion, String body) { + boolean versionExists = checkIfVersionExists(dockerVersion, body, WIN_VERSION, AMD_ARCH_TYPE); + if (versionExists) { + doAddVersion(urlVersion, "https://desktop.docker.com/win/main/amd64/" + dockerVersion + "/Docker%20Desktop%20Installer.exe", WINDOWS, X64); + } + versionExists = checkIfVersionExists(dockerVersion, body, WIN_VERSION, ARM_ARCH_TYPE); + if (versionExists) { + doAddVersion(urlVersion, "https://desktop.docker.com/win/main/arm64/" + dockerVersion + "/Docker%20Desktop%20Installer.exe", WINDOWS, ARM64); + } + } + + /** + * Adds the mac versions for docker if they exist + * + * @param urlVersion the readable version e.g. 4332 (4.33.2) + * @param dockerVersion the associated docker version to readable version e.g. 179689 + * @param body the html body to search in + */ + private void addVersionsForMac(UrlVersion urlVersion, String dockerVersion, String body) { + boolean versionExists = checkIfVersionExists(dockerVersion, body, MAC_VERSION, AMD_ARCH_TYPE); + if (versionExists) { + doAddVersion(urlVersion, "https://desktop.docker.com/mac/main/amd64/" + dockerVersion + "/Docker.dmg", MAC, X64); + } + versionExists = checkIfVersionExists(dockerVersion, body, MAC_VERSION, ARM_ARCH_TYPE); + if (versionExists) { + doAddVersion(urlVersion, "https://desktop.docker.com/mac/main/arm64/" + dockerVersion + "/Docker.dmg", MAC, ARM64); + } + } + + /** + * As docker is very inconsistent by releasing versions we have to check every single one if the download link exists to prevent failing downloads + * (403-errors) E.g. for only amd64 windows download link provided- 4.24.1 E.g. for no + * download links provided - 4.24.2 E.g. for only mac download links provided - 4.36.1 + * + * @param dockerVersion the associated docker version to readable version e.g. 179689 (4.33.2) + * @param body the html body to search in + * @param osVersion the os versions - win or mac + * @param archType the archType - amd64 or arm64 + * @return true if the version exists - false if not + */ + private boolean checkIfVersionExists(String dockerVersion, String body, String osVersion, String archType) { + String regexForDownloadUrlS = format(REGEX_FOR_DOWNLOAD_URLS, osVersion, archType, dockerVersion); + Pattern patternForDownloadUrls = Pattern.compile(regexForDownloadUrlS, Pattern.DOTALL); + Matcher matcherForDownloadUrls = patternForDownloadUrls.matcher(body); + return matcherForDownloadUrls.find(); + } + @Override protected String getVersionUrl() { - return "https://docs.docker.com/desktop/release-notes/"; + return DOCKER_RELEASE_NOTES_URL; } @Override diff --git a/url-updater/src/test/java/com/devonfw/tools/ide/url/tool/UrlUpdaterMockSingle.java b/url-updater/src/test/java/com/devonfw/tools/ide/url/tool/UrlUpdaterMockSingle.java index 3ca4b4407..e0697f668 100644 --- a/url-updater/src/test/java/com/devonfw/tools/ide/url/tool/UrlUpdaterMockSingle.java +++ b/url-updater/src/test/java/com/devonfw/tools/ide/url/tool/UrlUpdaterMockSingle.java @@ -13,7 +13,7 @@ */ public class UrlUpdaterMockSingle extends UrlUpdaterMock { - private static final Set versions = new HashSet<>(List.of("1.0")); + private static Set versions = new HashSet<>(List.of("1.0")); /** * The constructor @@ -30,6 +30,15 @@ protected Set getVersions() { return versions; } + /** + * Enables the possibility to change the version which should be tested. + * + * @param newVersion the new Version to be set. + */ + protected void setVersion(final String newVersion) { + versions = Set.of(newVersion); + } + @Override protected void addVersion(UrlVersion urlVersion) { doAddVersion(urlVersion, wmRuntimeInfo.getHttpBaseUrl() + "/os/windows_x64_url.tgz", WINDOWS, X64, "123"); diff --git a/url-updater/src/test/java/com/devonfw/tools/ide/url/tool/UrlUpdaterTest.java b/url-updater/src/test/java/com/devonfw/tools/ide/url/tool/UrlUpdaterTest.java index e6abb0db0..9692fe5fd 100644 --- a/url-updater/src/test/java/com/devonfw/tools/ide/url/tool/UrlUpdaterTest.java +++ b/url-updater/src/test/java/com/devonfw/tools/ide/url/tool/UrlUpdaterTest.java @@ -280,4 +280,26 @@ public void testUrlUpdaterWithTextContentTypeWillNotCreateStatusJson(@TempDir Pa } + /** + * Tests if the {@link com.devonfw.tools.ide.url.updater.UrlUpdater} will handle the literally latest version of a tool correctly + * + * @param tempDir Temporary directory + * @param wmRuntimeInfo wireMock server on a random port + */ + @Test + public void testUrlUpdaterWithOnlyLatestVersion(@TempDir Path tempDir, WireMockRuntimeInfo wmRuntimeInfo) { + //given + stubFor(any(urlMatching("/os/.*")).willReturn(aResponse().withStatus(200).withBody("aBody"))); + UrlRepository urlRepository = UrlRepository.load(tempDir); + UrlUpdaterMockSingle updater = new UrlUpdaterMockSingle(wmRuntimeInfo); + updater.setVersion("latest"); + + // when + updater.update(urlRepository); + + // then + Path versionsPath = tempDir.resolve("mocked").resolve("mocked").resolve("latest"); + assertThat(versionsPath.resolve("status.json")).exists(); + } + }