From fdde2a6803696a84c7bde282483810bce990d78a Mon Sep 17 00:00:00 2001 From: MarvMa Date: Mon, 11 May 2026 11:30:16 +0200 Subject: [PATCH 1/2] #1910: added mvndUrlUpdater --- .../tools/ide/github/GithubRelease.java | 11 +++- .../ide/url/tool/mvnd/MvndUrlUpdater.java | 53 +++++++++++++++++++ .../tools/ide/url/updater/UpdateManager.java | 3 +- 3 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 url-updater/src/main/java/com/devonfw/tools/ide/url/tool/mvnd/MvndUrlUpdater.java diff --git a/cli/src/main/java/com/devonfw/tools/ide/github/GithubRelease.java b/cli/src/main/java/com/devonfw/tools/ide/github/GithubRelease.java index 7e2afcd09f..24b36e9a77 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/github/GithubRelease.java +++ b/cli/src/main/java/com/devonfw/tools/ide/github/GithubRelease.java @@ -6,8 +6,17 @@ * JSON data object for a github release. * * @param name the official name of the release as its version. + * @param prerelease whether this is a pre-release version. */ -public record GithubRelease(String name) implements JsonVersionItem { +public record GithubRelease(String name, boolean prerelease) implements JsonVersionItem { + + /** + * Constructor for backwards compatibility with name only (not a prerelease). + * @param name the official name of the release as its version. + */ + public GithubRelease(String name) { + this(name, false); + } @Override public String version() { diff --git a/url-updater/src/main/java/com/devonfw/tools/ide/url/tool/mvnd/MvndUrlUpdater.java b/url-updater/src/main/java/com/devonfw/tools/ide/url/tool/mvnd/MvndUrlUpdater.java new file mode 100644 index 0000000000..0928f60b58 --- /dev/null +++ b/url-updater/src/main/java/com/devonfw/tools/ide/url/tool/mvnd/MvndUrlUpdater.java @@ -0,0 +1,53 @@ +package com.devonfw.tools.ide.url.tool.mvnd; + +import com.devonfw.tools.ide.url.model.folder.UrlVersion; +import com.devonfw.tools.ide.url.updater.GithubUrlReleaseUpdater; + +/** + * {@link GithubUrlReleaseUpdater} for Maven Daemon (mvnd). Supports both stable releases (1.x) and pre-releases (2.x) to allow users to experiment with the + * latest versions. + */ +public class MvndUrlUpdater extends GithubUrlReleaseUpdater { + + @Override + public String getTool() { + + return "mvnd"; + } + + @Override + protected String getGithubOrganization() { + + return "apache"; + } + + @Override + protected String getGithubRepository() { + + return "maven-mvnd"; + } + + @Override + protected String getDownloadBaseUrl() { + + return "https://dlcdn.apache.org/maven/mvnd/"; + } + + @Override + protected void addVersion(UrlVersion urlVersion) { + // Support both regular releases and pre-releases (e.g., 2.x versions) + // This allows users to test the latest versions before they become stable + String baseUrl = getDownloadBaseUrl() + "${version}/maven-mvnd-${version}-"; + + // Windows + doAddVersion(urlVersion, baseUrl + "windows-amd64.zip", WINDOWS, X64); + + // macOS + doAddVersion(urlVersion, baseUrl + "darwin-amd64.zip", MAC, X64); + doAddVersion(urlVersion, baseUrl + "darwin-aarch64.zip", MAC, ARM64); + + // Linux + doAddVersion(urlVersion, baseUrl + "linux-amd64.zip", LINUX, X64); + doAddVersion(urlVersion, baseUrl + "linux-aarch64.zip", LINUX, ARM64); + } +} diff --git a/url-updater/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java b/url-updater/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java index fcadcb2121..a87e289785 100644 --- a/url-updater/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java +++ b/url-updater/src/main/java/com/devonfw/tools/ide/url/updater/UpdateManager.java @@ -38,6 +38,7 @@ import com.devonfw.tools.ide.url.tool.kotlinc.KotlincUrlUpdater; import com.devonfw.tools.ide.url.tool.lazydocker.LazyDockerUrlUpdater; import com.devonfw.tools.ide.url.tool.mvn.MvnUrlUpdater; +import com.devonfw.tools.ide.url.tool.mvnd.MvndUrlUpdater; import com.devonfw.tools.ide.url.tool.ng.NgUrlUpdater; import com.devonfw.tools.ide.url.tool.node.NodeUrlUpdater; import com.devonfw.tools.ide.url.tool.npm.NpmUrlUpdater; @@ -75,7 +76,7 @@ public class UpdateManager extends AbstractProcessorWithTimeout { new GcViewerUrlUpdater(), new GhUrlUpdater(), new GoUrlUpdater(), new GraalVmCommunityUpdater(), new GraalVmOracleUrlUpdater(), new GradleUrlUpdater(), new HelmUrlUpdater(), new IntellijUrlUpdater(), new JasyptUrlUpdater(), new JavaUrlUpdater(), new JavaAzulUrlUpdater(), new JenkinsUrlUpdater(), new JmcUrlUpdater(), new KotlincUrlUpdater(), - new KotlincNativeUrlUpdater(), new LazyDockerUrlUpdater(), new MvnUrlUpdater(), + new KotlincNativeUrlUpdater(), new LazyDockerUrlUpdater(), new MvnUrlUpdater(), new MvndUrlUpdater(), new NgUrlUpdater(), new NodeUrlUpdater(), new NpmUrlUpdater(), new OcUrlUpdater(), new PgAdminUrlUpdater(), new PipUrlUpdater(), new PycharmUrlUpdater(), new PythonUrlUpdater(), new QuarkusUrlUpdater(), new RustUrlUpdater(), new DockerRancherDesktopUrlUpdater(), new SonarUrlUpdater(), new SquirrelSqlUrlUpdater(), From 351dac84341304e09c0e3f11c9a78c770b82e8cd Mon Sep 17 00:00:00 2001 From: MarvMa Date: Tue, 12 May 2026 08:13:47 +0200 Subject: [PATCH 2/2] 1910: added tests and updated logic for better testability --- .../ide/url/tool/mvnd/MvndUrlUpdater.java | 28 +++++ .../ide/url/updater/AbstractUrlUpdater.java | 25 +++- .../ide/url/tool/mvnd/MvndUrlUpdaterMock.java | 29 +++++ .../ide/url/tool/mvnd/MvndUrlUpdaterTest.java | 112 ++++++++++++++++++ .../MvndUrlUpdater/mvnd-releases.json | 72 +++++++++++ 5 files changed, 260 insertions(+), 6 deletions(-) create mode 100644 url-updater/src/test/java/com/devonfw/tools/ide/url/tool/mvnd/MvndUrlUpdaterMock.java create mode 100644 url-updater/src/test/java/com/devonfw/tools/ide/url/tool/mvnd/MvndUrlUpdaterTest.java create mode 100644 url-updater/src/test/resources/integrationtest/MvndUrlUpdater/mvnd-releases.json diff --git a/url-updater/src/main/java/com/devonfw/tools/ide/url/tool/mvnd/MvndUrlUpdater.java b/url-updater/src/main/java/com/devonfw/tools/ide/url/tool/mvnd/MvndUrlUpdater.java index 0928f60b58..aa8241ddfc 100644 --- a/url-updater/src/main/java/com/devonfw/tools/ide/url/tool/mvnd/MvndUrlUpdater.java +++ b/url-updater/src/main/java/com/devonfw/tools/ide/url/tool/mvnd/MvndUrlUpdater.java @@ -2,6 +2,8 @@ import com.devonfw.tools.ide.url.model.folder.UrlVersion; import com.devonfw.tools.ide.url.updater.GithubUrlReleaseUpdater; +import com.devonfw.tools.ide.version.VersionComparisonResult; +import com.devonfw.tools.ide.version.VersionIdentifier; /** * {@link GithubUrlReleaseUpdater} for Maven Daemon (mvnd). Supports both stable releases (1.x) and pre-releases (2.x) to allow users to experiment with the @@ -9,6 +11,8 @@ */ public class MvndUrlUpdater extends GithubUrlReleaseUpdater { + private static final VersionIdentifier MIN_MVND_VID = VersionIdentifier.of("1.0.2"); + @Override public String getTool() { @@ -33,6 +37,30 @@ protected String getDownloadBaseUrl() { return "https://dlcdn.apache.org/maven/mvnd/"; } + @Override + protected boolean isVersionFiltered() { + // Don't filter pre-releases, we'll handle filtering explicitly in mapVersion() + return false; + } + + @Override + public String mapVersion(String version) { + // Accept pre-release versions (rc, beta, alpha, etc.) first + if (version.contains("-rc") || version.contains("-beta") || version.contains("-alpha")) { + return version; + } + + // For stable versions, require minimum version 1.0.2 + VersionIdentifier vid = VersionIdentifier.of(version); + if (vid.isValid()) { + VersionComparisonResult comparison = vid.compareVersion(MIN_MVND_VID); + if (comparison.isGreater() || comparison.isEqual()) { + return version; + } + } + return null; + } + @Override protected void addVersion(UrlVersion urlVersion) { // Support both regular releases and pre-releases (e.g., 2.x versions) diff --git a/url-updater/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java b/url-updater/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java index 9220373b61..605e43acc5 100644 --- a/url-updater/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java +++ b/url-updater/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java @@ -856,12 +856,14 @@ public String mapVersion(String version) { } String vLower = version.toLowerCase(Locale.ROOT); - if (vLower.contains("alpha") || vLower.contains("beta") || vLower.contains("dev") || vLower.contains("snapshot") || vLower.contains("preview") - || vLower.contains("test") || vLower.contains("tech-preview") // - || vLower.contains("-pre") || vLower.startsWith("ce-") || vLower.contains("-next") || vLower.contains("-rc") - // vscode nonsense - || vLower.startsWith("bad") || vLower.contains("vsda-") || vLower.contains("translation/") || vLower.contains("-insiders")) { - return null; + if (isVersionFiltered()) { + if (vLower.contains("alpha") || vLower.contains("beta") || vLower.contains("dev") || vLower.contains("snapshot") || vLower.contains("preview") + || vLower.contains("test") || vLower.contains("tech-preview") // + || vLower.contains("-pre") || vLower.startsWith("ce-") || vLower.contains("-next") || vLower.contains("-rc") + // vscode nonsense + || vLower.startsWith("bad") || vLower.contains("vsda-") || vLower.contains("translation/") || vLower.contains("-insiders")) { + return null; + } } String filter = getCustomVersionFilter(); @@ -889,6 +891,17 @@ protected String getCustomVersionFilter() { return null; } + /** + * Defines if we want to filter pre-releases and similar non-stable versions by default in {@link #filterVersion(String)}. Can be overridden to disable this + * default filtering. + * + * @return {@code true} if pre-releases and similar non-stable versions should be filtered by default, {@code false} otherwise. + */ + protected boolean isVersionFiltered() { + + return true; + } + /** * @param version the version to add (e.g. "1.0"). * @param versions the {@link Collection} with the versions to collect. diff --git a/url-updater/src/test/java/com/devonfw/tools/ide/url/tool/mvnd/MvndUrlUpdaterMock.java b/url-updater/src/test/java/com/devonfw/tools/ide/url/tool/mvnd/MvndUrlUpdaterMock.java new file mode 100644 index 0000000000..87be1144cb --- /dev/null +++ b/url-updater/src/test/java/com/devonfw/tools/ide/url/tool/mvnd/MvndUrlUpdaterMock.java @@ -0,0 +1,29 @@ +package com.devonfw.tools.ide.url.tool.mvnd; + +import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; + +/** + * Mock of {@link MvndUrlUpdater} to allow integration testing with wiremock. + */ +@SuppressWarnings("unused") +public class MvndUrlUpdaterMock extends MvndUrlUpdater { + + private final String baseUrl; + + MvndUrlUpdaterMock(WireMockRuntimeInfo wireMockRuntimeInfo) { + super(); + this.baseUrl = wireMockRuntimeInfo.getHttpBaseUrl(); + } + + @Override + protected String getVersionBaseUrl() { + return this.baseUrl + "/repos/"; + } + + @Override + protected String getDownloadBaseUrl() { + return this.baseUrl + "/maven/mvnd/"; + } +} + + diff --git a/url-updater/src/test/java/com/devonfw/tools/ide/url/tool/mvnd/MvndUrlUpdaterTest.java b/url-updater/src/test/java/com/devonfw/tools/ide/url/tool/mvnd/MvndUrlUpdaterTest.java new file mode 100644 index 0000000000..c236e0c7f1 --- /dev/null +++ b/url-updater/src/test/java/com/devonfw/tools/ide/url/tool/mvnd/MvndUrlUpdaterTest.java @@ -0,0 +1,112 @@ +package com.devonfw.tools.ide.url.tool.mvnd; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.any; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static com.github.tomakehurst.wiremock.client.WireMock.urlMatching; +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; +import java.nio.file.Path; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import com.devonfw.tools.ide.url.model.folder.UrlRepository; +import com.devonfw.tools.ide.url.updater.AbstractUrlUpdaterTest; +import com.devonfw.tools.ide.url.updater.JsonUrlUpdater; +import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; +import com.github.tomakehurst.wiremock.junit5.WireMockTest; + +/** + * Test of {@link MvndUrlUpdater} + */ +@WireMockTest +@SuppressWarnings("unused") +class MvndUrlUpdaterTest extends AbstractUrlUpdaterTest { + + /** + * Test of {@link JsonUrlUpdater} for the creation of {@link MvndUrlUpdater} download URLs and checksums. + * + * @param tempDir Path to a temporary directory + * @param wmRuntimeInfo the {@link WireMockRuntimeInfo}. + * @throws IOException test fails + */ + @Test + void testMvndJsonUrlUpdaterCreatesDownloadUrlsAndChecksums(@TempDir Path tempDir, WireMockRuntimeInfo wmRuntimeInfo) throws IOException { + + // arrange + stubFor(get(urlMatching("/repos/apache/maven-mvnd/releases")).willReturn( + aResponse().withStatus(200).withBody(getJsonBody(wmRuntimeInfo)))); + + stubFor(any(urlMatching("/maven/mvnd/.*\\.zip")).willReturn(aResponse().withStatus(200).withBody(DOWNLOAD_CONTENT))); + + UrlRepository urlRepository = UrlRepository.load(tempDir); + MvndUrlUpdaterMock updater = new MvndUrlUpdaterMock(wmRuntimeInfo); + + // act + updater.update(urlRepository); + + // assert + assertUrlVersionOsX64MacArm(tempDir.resolve("mvnd").resolve("mvnd").resolve("1.0.5")); + } + + /** + * Test if the {@link JsonUrlUpdater} for {@link MvndUrlUpdater} can handle filtering of old versions. + * + * @param tempDir Path to a temporary directory + * @param wmRuntimeInfo the {@link WireMockRuntimeInfo}. + * @throws IOException test fails + */ + @Test + void testMvndJsonUrlUpdaterFilterOldVersions(@TempDir Path tempDir, WireMockRuntimeInfo wmRuntimeInfo) throws IOException { + + // arrange + stubFor(get(urlMatching("/repos/apache/maven-mvnd/releases")).willReturn( + aResponse().withStatus(200).withBody(getJsonBody(wmRuntimeInfo)))); + + stubFor(any(urlMatching("/maven/mvnd/.*\\.zip")).willReturn(aResponse().withStatus(200).withBody(DOWNLOAD_CONTENT))); + + UrlRepository urlRepository = UrlRepository.load(tempDir); + MvndUrlUpdaterMock updater = new MvndUrlUpdaterMock(wmRuntimeInfo); + + // act + updater.update(urlRepository); + + // assert + Path mvndOldVersionPath = tempDir.resolve("mvnd").resolve("mvnd").resolve("1.0.0"); + assertThat(mvndOldVersionPath).doesNotExist(); + } + + /** + * Test if the {@link JsonUrlUpdater} for {@link MvndUrlUpdater} accepts pre-release versions (rc). + * + * @param tempDir Path to a temporary directory + * @param wmRuntimeInfo the {@link WireMockRuntimeInfo}. + * @throws IOException test fails + */ + @Test + void testMvndJsonUrlUpdaterAcceptsReleaseCandidate(@TempDir Path tempDir, WireMockRuntimeInfo wmRuntimeInfo) throws IOException { + + // arrange + stubFor(get(urlMatching("/repos/apache/maven-mvnd/releases")).willReturn( + aResponse().withStatus(200).withBody(getJsonBody(wmRuntimeInfo)))); + + stubFor(any(urlMatching("/maven/mvnd/.*\\.zip")).willReturn(aResponse().withStatus(200).withBody(DOWNLOAD_CONTENT))); + + UrlRepository urlRepository = UrlRepository.load(tempDir); + MvndUrlUpdaterMock updater = new MvndUrlUpdaterMock(wmRuntimeInfo); + + // act + updater.update(urlRepository); + + // assert + Path mvndRcVersionPath = tempDir.resolve("mvnd").resolve("mvnd").resolve("2.0.0-rc-3"); + assertUrlVersionOsX64MacArm(mvndRcVersionPath); + } + + private static String getJsonBody(WireMockRuntimeInfo wmRuntimeInfo) throws IOException { + return readAndResolve(PATH_INTEGRATION_TEST.resolve("MvndUrlUpdater").resolve("mvnd-releases.json"), wmRuntimeInfo); + } +} diff --git a/url-updater/src/test/resources/integrationtest/MvndUrlUpdater/mvnd-releases.json b/url-updater/src/test/resources/integrationtest/MvndUrlUpdater/mvnd-releases.json new file mode 100644 index 0000000000..30955f4ce2 --- /dev/null +++ b/url-updater/src/test/resources/integrationtest/MvndUrlUpdater/mvnd-releases.json @@ -0,0 +1,72 @@ +[ + { + "url": "${testbaseurl}/repos/apache/maven-mvnd/releases/123456", + "assets_url": "${testbaseurl}/repos/apache/maven-mvnd/releases/123456/assets", + "upload_url": "${testbaseurl}/repos/apache/maven-mvnd/releases/123456/assets{?name,label}", + "html_url": "${testbaseurl}/apache/maven-mvnd/releases/tag/1.0.5", + "id": 123456, + "tag_name": "1.0.5", + "target_commitish": "main", + "name": "1.0.5", + "draft": false, + "prerelease": false, + "created_at": "2026-03-17T12:00:00Z", + "published_at": "2026-03-17T12:09:00Z", + "assets": [], + "body": "Maven Daemon 1.0.5" + }, + { + "url": "${testbaseurl}/repos/apache/maven-mvnd/releases/123457", + "assets_url": "${testbaseurl}/repos/apache/maven-mvnd/releases/123457/assets", + "upload_url": "${testbaseurl}/repos/apache/maven-mvnd/releases/123457/assets{?name,label}", + "html_url": "${testbaseurl}/apache/maven-mvnd/releases/tag/1.0.4", + "id": 123457, + "tag_name": "1.0.4", + "target_commitish": "main", + "name": "1.0.4", + "draft": false, + "prerelease": false, + "created_at": "2026-03-10T12:00:00Z", + "published_at": "2026-03-10T12:09:00Z", + "assets": [], + "body": "Maven Daemon 1.0.4" + }, + { + "url": "${testbaseurl}/repos/apache/maven-mvnd/releases/123458", + "assets_url": "${testbaseurl}/repos/apache/maven-mvnd/releases/123458/assets", + "upload_url": "${testbaseurl}/repos/apache/maven-mvnd/releases/123458/assets{?name,label}", + "html_url": "${testbaseurl}/apache/maven-mvnd/releases/tag/2.0.0-rc-3", + "id": 123458, + "tag_name": "2.0.0-rc-3", + "target_commitish": "main", + "name": "2.0.0-rc-3", + "draft": false, + "prerelease": true, + "created_at": "2026-03-05T12:00:00Z", + "published_at": "2026-03-05T12:09:00Z", + "assets": [], + "body": "Maven Daemon 2.0.0-rc-3" + }, + { + "url": "${testbaseurl}/repos/apache/maven-mvnd/releases/123459", + "assets_url": "${testbaseurl}/repos/apache/maven-mvnd/releases/123459/assets", + "upload_url": "${testbaseurl}/repos/apache/maven-mvnd/releases/123459/assets{?name,label}", + "html_url": "${testbaseurl}/apache/maven-mvnd/releases/tag/1.0.0", + "id": 123459, + "tag_name": "1.0.0", + "target_commitish": "main", + "name": "1.0.0", + "draft": false, + "prerelease": false, + "created_at": "2026-01-01T12:00:00Z", + "published_at": "2026-01-01T12:09:00Z", + "assets": [], + "body": "Maven Daemon 1.0.0" + } +] + + + + + +