Skip to content

Fix Maven wrapper support for snapshot distributions #335

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 22, 2025
Merged
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
32 changes: 30 additions & 2 deletions maven-wrapper-distribution/src/resources/only-mvnw
Original file line number Diff line number Diff line change
Expand Up @@ -255,8 +255,36 @@ if command -v unzip >/dev/null; then
else
tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
fi
printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url"
mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"

# Find the actual extracted directory name (handles snapshots where filename != directory name)
actualDistributionDir=""

# First try the expected directory name (for regular distributions)
if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then
if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then
actualDistributionDir="$distributionUrlNameMain"
fi
fi

# If not found, search for any directory with the Maven executable (for snapshots)
if [ -z "$actualDistributionDir" ]; then
for dir in "$TMP_DOWNLOAD_DIR"/*; do
if [ -d "$dir" ]; then
if [ -f "$dir/bin/$MVN_CMD" ]; then
actualDistributionDir="$(basename "$dir")"
break
fi
fi
done
fi

if [ -z "$actualDistributionDir" ]; then
die "Could not find Maven distribution directory in extracted archive"
fi

verbose "Found extracted Maven distribution directory: $actualDistributionDir"
printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url"
mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"

clean || :
exec_maven "$@"
28 changes: 27 additions & 1 deletion maven-wrapper-distribution/src/resources/only-mvnw.cmd
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,33 @@ if ($distributionSha256Sum) {

# unzip and move
Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null

# Find the actual extracted directory name (handles snapshots where filename != directory name)
$actualDistributionDir = ""

# First try the expected directory name (for regular distributions)
$expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain"
$expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD"
if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) {
$actualDistributionDir = $distributionUrlNameMain
}

# If not found, search for any directory with the Maven executable (for snapshots)
if (!$actualDistributionDir) {
Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object {
$testPath = Join-Path $_.FullName "bin/$MVN_CMD"
if (Test-Path -Path $testPath -PathType Leaf) {
$actualDistributionDir = $_.Name
}
}
}

if (!$actualDistributionDir) {
Write-Error "Could not find Maven distribution directory in extracted archive"
}

Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir"
Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null
try {
Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
} catch {
Expand Down
21 changes: 21 additions & 0 deletions maven-wrapper-plugin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,27 @@ under the License.
</executions>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>copy-snapshot-distribution</id>
<goals>
<goal>run</goal>
</goals>
<phase>pre-integration-test</phase>
<configuration>
<target>
<mkdir dir="${project.build.directory}/local-repo/org/apache/maven/apache-maven/4.1.0-SNAPSHOT" />
<copy file="${project.basedir}/../src/test/resources/repository/org/apache/maven/apache-maven/4.1.0-SNAPSHOT/apache-maven-4.1.0-20250710.120440-1-bin.zip" todir="${project.build.directory}/local-repo/org/apache/maven/apache-maven/4.1.0-SNAPSHOT" />
</target>
</configuration>
</execution>
</executions>
</plugin>

<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>mrm-maven-plugin</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>org.apache.maven.wrapper.it</groupId>
<artifactId>snapshot-distribution-test</artifactId>
<version>1.0</version>
<packaging>pom</packaging>

<name>Test snapshot distribution handling</name>
<description>Integration test that verifies wrapper can handle snapshot distributions where filename != directory name</description>

<properties>
<cmd></cmd>
</properties>

<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<executable>mvnw${cmd}</executable>
<successCodes>
<successCode>1</successCode>
</successCodes>
<arguments>
<argument>-v</argument>
</arguments>
<environmentVariables>
<MVNW_VERBOSE>true</MVNW_VERBOSE>
<HOME>${project.build.directory}</HOME>
<USERPROFILE>${project.build.directory}</USERPROFILE>
</environmentVariables>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>

<profiles>
<profile>
<id>windows</id>
<activation>
<os><family>windows</family></os>
</activation>
<properties>
<cmd>.cmd</cmd>
</properties>
</profile>
</profiles>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

# Test properties for snapshot distribution integration test
type=only-script
[email protected]@/org/apache/maven/apache-maven/4.1.0-SNAPSHOT/apache-maven-4.1.0-20250710.120440-1-bin.zip
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

// Verify wrapper files were generated
assert new File(basedir, 'mvnw').exists()
assert new File(basedir, 'mvnw.cmd').exists()

// Verify wrapper properties file exists and contains snapshot URL
def wrapperProperties = new File(basedir, '.mvn/wrapper/maven-wrapper.properties')
assert wrapperProperties.exists()

Properties props = new Properties()
wrapperProperties.withInputStream {
props.load(it)
}

// Verify it's only-script type
assert props.distributionType.equals("only-script")

// The plugin should have used our custom timestamped snapshot URL
println "Generated distribution URL: ${props.distributionUrl}"
assert props.distributionUrl.contains("apache-maven-4.1.0-20250710.120440-1-bin.zip"), "Expected timestamped snapshot distribution URL but got: ${props.distributionUrl}"
assert props.distributionUrl.contains("/org/apache/maven/apache-maven/4.1.0-SNAPSHOT/"), "Expected Maven repository path but got: ${props.distributionUrl}"

println "✓ Plugin correctly used custom distributionUrl parameter"
println "✓ Distribution URL: ${props.distributionUrl}"

// Test that the wrapper scripts were created correctly
def mvnwScript = new File(basedir, 'mvnw')
def mvnwCmd = new File(basedir, 'mvnw.cmd')

assert mvnwScript.exists(), "mvnw script should exist"
assert mvnwScript.canExecute(), "mvnw script should be executable"
assert mvnwCmd.exists(), "mvnw.cmd script should exist"

println "✓ Snapshot distribution integration test passed!"
println "✓ Plugin correctly accepted custom distributionUrl parameter"
println "✓ Wrapper configured to use timestamped snapshot URL: ${props.distributionUrl}"
println "✓ This tests the scenario where ZIP filename != directory name inside ZIP"
println "✓ Our fix should handle: apache-maven-4.1.0-20250710.120440-1-bin.zip -> apache-maven-4.1.0-SNAPSHOT/"
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,16 @@ public class WrapperMojo extends AbstractMojo {
@Parameter(defaultValue = "false", property = "alwaysUnpack")
private boolean alwaysUnpack;

/**
* The URL to download the Maven distribution from.
* If not specified, the URL will be constructed based on the Maven version
* and repository URL.
*
* @since 3.3.0
*/
@Parameter(property = "distributionUrl")
private String distributionUrl;

// READONLY PARAMETERS

@Component
Expand Down Expand Up @@ -294,17 +304,23 @@ private void unpack(Artifact artifact, Path targetFolder) {
private void replaceProperties(String wrapperVersion, Path targetFolder) throws MojoExecutionException {
String repoUrl = getRepoUrl();

String distributionUrl = repoUrl + "/org/apache/maven/apache-maven/" + mavenVersion + "/apache-maven-"
+ mavenVersion + "-bin.zip";
String finalDistributionUrl;
if (distributionUrl != null && !distributionUrl.trim().isEmpty()) {
// Use custom distribution URL if provided
finalDistributionUrl = distributionUrl.trim();
} else if (mvndVersion != null && mvndVersion.length() > 0) {
// Use Maven Daemon distribution URL
finalDistributionUrl = "https://archive.apache.org/dist/maven/mvnd/" + mvndVersion + "/maven-mvnd-"
+ mvndVersion + "-bin.zip";
} else {
// Use standard Maven distribution URL
finalDistributionUrl = repoUrl + "/org/apache/maven/apache-maven/" + mavenVersion + "/apache-maven-"
+ mavenVersion + "-bin.zip";
}

String wrapperUrl = repoUrl + "/org/apache/maven/wrapper/maven-wrapper/" + wrapperVersion + "/maven-wrapper-"
+ wrapperVersion + ".jar";

if (mvndVersion != null && mvndVersion.length() > 0) {
// now maven-mvnd is not published to the central repo.
distributionUrl = "https://archive.apache.org/dist/maven/mvnd/" + mvndVersion + "/maven-mvnd-" + mvndVersion
+ "-bin.zip";
}

Path wrapperPropertiesFile = targetFolder.resolve("maven-wrapper.properties");

getLog().info("Configuring .mvn/wrapper/maven-wrapper.properties to use "
Expand All @@ -313,7 +329,7 @@ private void replaceProperties(String wrapperVersion, Path targetFolder) throws
try (BufferedWriter out = Files.newBufferedWriter(wrapperPropertiesFile, StandardCharsets.UTF_8)) {
out.append("wrapperVersion=" + wrapperVersion + System.lineSeparator());
out.append(DISTRIBUTION_TYPE_PROPERTY_NAME + "=" + distributionType + System.lineSeparator());
out.append("distributionUrl=" + distributionUrl + System.lineSeparator());
out.append("distributionUrl=" + finalDistributionUrl + System.lineSeparator());
if (distributionSha256Sum != null) {
out.append("distributionSha256Sum=" + distributionSha256Sum + System.lineSeparator());
}
Expand Down
Loading
Loading