diff --git a/apache-maven/src/assembly/maven/bin/mvn b/apache-maven/src/assembly/maven/bin/mvn index 8559d47af557..d11120d72c89 100755 --- a/apache-maven/src/assembly/maven/bin/mvn +++ b/apache-maven/src/assembly/maven/bin/mvn @@ -168,23 +168,40 @@ find_file_argument_basedir() { # concatenates all lines of a file and replaces variables concat_lines() { if [ -f "$1" ]; then - # First convert all CR to LF using tr - tr '\r' '\n' < "$1" | \ - sed -e '/^$/d' -e 's/#.*$//' | \ - # Replace LF with NUL for xargs - tr '\n' '\0' | \ - # Split into words and process each argument - # Use -0 with NUL to avoid special behaviour on quotes - xargs -n 1 -0 | \ - while read -r arg; do - # Replace variables first - arg=$(echo "$arg" | sed \ - -e "s@\${MAVEN_PROJECTBASEDIR}@$MAVEN_PROJECTBASEDIR@g" \ - -e "s@\$MAVEN_PROJECTBASEDIR@$MAVEN_PROJECTBASEDIR@g") - - echo "$arg" - done | \ - tr '\n' ' ' + result="" + # Read the file line by line + # Do not use `xargs -0` as this is not POSIX-compliant + while IFS= read -r line || [ -n "$line" ]; do + # Convert CR to LF + line=$(echo "$line" | tr '\r' '\n') + # Remove comments + line=$(echo "$line" | sed 's/#.*$//') + # Skip empty lines + [ -z "$(echo "$line" | tr -d ' \t')" ] && continue + + # Process each argument in the line using eval to handle quotes + eval "set -- $line" + for arg in "$@"; do + # Replace variables + arg=$(echo "$arg" | sed \ + -e "s@\${MAVEN_PROJECTBASEDIR}@$MAVEN_PROJECTBASEDIR@g" \ + -e "s@\$MAVEN_PROJECTBASEDIR@$MAVEN_PROJECTBASEDIR@g") + + # Quote the argument if it contains spaces or special shell characters + case "$arg" in + *[\ \|\&\;\<\>\(\)\$\`\\\"\'\~\*\?\[\]\#\~\=]*) + arg="\"$arg\"" + ;; + esac + + if [ -n "$result" ]; then + result="$result $arg" + else + result="$arg" + fi + done + done < "$1" + echo "$result" fi } diff --git a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITgh11485AtSignInJvmConfigTest.java b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITgh11485AtSignInJvmConfigTest.java new file mode 100644 index 000000000000..63e3dc98bd02 --- /dev/null +++ b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITgh11485AtSignInJvmConfigTest.java @@ -0,0 +1,86 @@ +/* + * 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. + */ +package org.apache.maven.it; + +import java.io.File; +import java.util.Properties; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * This is a test set for GH-11485: + * Verify that @ character in .mvn/jvm.config values is handled correctly. + * This is important for Jenkins workspaces like workspace/project_PR-350@2 + */ +public class MavenITgh11485AtSignInJvmConfigTest extends AbstractMavenIntegrationTestCase { + + @Test + public void testAtSignInJvmConfig() throws Exception { + File testDir = extractResources("/gh-11485-at-sign"); + + Verifier verifier = newVerifier(testDir.getAbsolutePath()); + verifier.addCliArgument( + "-Dexpression.outputFile=" + new File(testDir, "target/pom.properties").getAbsolutePath()); + verifier.setForkJvm(true); // custom .mvn/jvm.config + verifier.addCliArgument("validate"); + verifier.execute(); + verifier.verifyErrorFreeLog(); + + Properties props = verifier.loadProperties("target/pom.properties"); + String expectedPath = testDir.getAbsolutePath().replace('\\', '/'); + assertEquals( + expectedPath + "/workspace@2/test", + props.getProperty("project.properties.pathWithAtProp").replace('\\', '/'), + "Path with @ character should be preserved"); + assertEquals( + "value@test", + props.getProperty("project.properties.propWithAtProp"), + "Property value with @ character should be preserved"); + } + + @Test + public void testAtSignInCommandLineProperty() throws Exception { + File testDir = extractResources("/gh-11485-at-sign"); + + Verifier verifier = newVerifier(testDir.getAbsolutePath()); + verifier.addCliArgument( + "-Dexpression.outputFile=" + new File(testDir, "target/pom.properties").getAbsolutePath()); + verifier.setForkJvm(true); // custom .mvn/jvm.config + // Pass a path with @ character via command line (simulating Jenkins workspace) + String jenkinsPath = testDir.getAbsolutePath().replace('\\', '/') + "/jenkins.workspace/proj@2"; + verifier.addCliArgument("-Dcmdline.path=" + jenkinsPath); + verifier.addCliArgument("-Dcmdline.value=test@value"); + verifier.addCliArgument("validate"); + verifier.execute(); + verifier.verifyErrorFreeLog(); + + Properties props = verifier.loadProperties("target/pom.properties"); + assertEquals( + jenkinsPath, + props.getProperty("project.properties.cmdlinePath").replace('\\', '/'), + "Command-line path with @ character should be preserved"); + assertEquals( + "test@value", + props.getProperty("project.properties.cmdlineValue"), + "Command-line value with @ character should be preserved"); + } +} + diff --git a/its/core-it-suite/src/test/resources/gh-11485-at-sign/.mvn/jvm.config b/its/core-it-suite/src/test/resources/gh-11485-at-sign/.mvn/jvm.config new file mode 100644 index 000000000000..6b5301311118 --- /dev/null +++ b/its/core-it-suite/src/test/resources/gh-11485-at-sign/.mvn/jvm.config @@ -0,0 +1,2 @@ +-Dpath.with.at=${MAVEN_PROJECTBASEDIR}/workspace@2/test +-Dprop.with.at=value@test \ No newline at end of file diff --git a/its/core-it-suite/src/test/resources/gh-11485-at-sign/pom.xml b/its/core-it-suite/src/test/resources/gh-11485-at-sign/pom.xml new file mode 100644 index 000000000000..9fdbc2444b6e --- /dev/null +++ b/its/core-it-suite/src/test/resources/gh-11485-at-sign/pom.xml @@ -0,0 +1,70 @@ + + + + 4.0.0 + + org.apache.maven.its.gh11485 + test + 1.0 + pom + + Test @ character in jvm.config + + Verify that @ character in jvm.config values is handled correctly. + This is important for Jenkins workspaces like workspace/project_PR-350@2 + + + + ${path.with.at} + ${prop.with.at} + ${cmdline.path} + ${cmdline.value} + + + + + + org.apache.maven.its.plugins + maven-it-plugin-expression + 2.1-SNAPSHOT + + + validate + + eval + + + target/pom.properties + + project/properties/pathWithAtProp + project/properties/propWithAtProp + project/properties/cmdlinePath + project/properties/cmdlineValue + + + + + + + + +