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
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
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;
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
* latest versions.
*/
public class MvndUrlUpdater extends GithubUrlReleaseUpdater {

private static final VersionIdentifier MIN_MVND_VID = VersionIdentifier.of("1.0.2");

@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 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")) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Surely we want to include the release candidates but should IDEasy really encourage users to use alpha (or beta) releases?

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)
// This allows users to test the latest versions before they become stable
String baseUrl = getDownloadBaseUrl() + "${version}/maven-mvnd-${version}-";
Comment thread
hohwille marked this conversation as resolved.

// 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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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")) {
Comment on lines +859 to +864
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not instead moving the condition to isFilterVersion(String version) so you can override it?
BTW: You are overriding the entire method so this change and the new isVersionFiltered() method is kind of pointless here.

return null;
}
}

String filter = getCustomVersionFilter();
Expand Down Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,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;
Expand Down Expand Up @@ -76,7 +77,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(),
Expand Down
Original file line number Diff line number Diff line change
@@ -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/";
}
}


Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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"
}
]






Loading