This is an automated email from the ASF dual-hosted git repository.

gnodet pushed a commit to branch ci-issue-11920
in repository https://gitbox.apache.org/repos/asf/maven.git

commit 71d212c7492e6975c314b742c7fa94e0a0e87dc2
Author: Guillaume Nodet <[email protected]>
AuthorDate: Wed May 13 09:24:04 2026 +0200

    Fix #11920: skip expression validation for profile repository URLs
    
    Profile properties are injected after raw model validation, so
    expressions in profile repository URLs/IDs cannot be validated at
    this stage. Skip the uninterpolated expression check for repositories
    inside profiles to allow deferred property interpolation.
    
    Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
---
 .../maven/impl/model/DefaultModelValidator.java    | 69 ++++++++++++----------
 .../impl/model/DefaultModelValidatorTest.java      |  6 ++
 .../profile-with-property-in-repository-url.xml    | 68 +++++++++++++++++++++
 3 files changed, 112 insertions(+), 31 deletions(-)

diff --git 
a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelValidator.java
 
b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelValidator.java
index 72917cd7c6..6e73c57bcc 100644
--- 
a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelValidator.java
+++ 
b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelValidator.java
@@ -668,39 +668,42 @@ && equals(parent.getArtifactId(), model.getArtifactId())) 
{
 
         if (validationLevel > VALIDATION_LEVEL_MINIMAL) {
             validateRawRepositories(
-                    problems, model.getRepositories(), 
"repositories.repository.", EMPTY, validationLevel);
+                    problems, model.getRepositories(), 
"repositories.repository.", EMPTY, validationLevel, false);
 
             validateRawRepositories(
                     problems,
                     model.getPluginRepositories(),
                     "pluginRepositories.pluginRepository.",
                     EMPTY,
-                    validationLevel);
+                    validationLevel,
+                    false);
 
             for (Profile profile : model.getProfiles()) {
                 String prefix = "profiles.profile[" + profile.getId() + "].";
 
                 validateRawRepositories(
-                        problems, profile.getRepositories(), prefix, 
"repositories.repository.", validationLevel);
+                        problems, profile.getRepositories(), prefix, 
"repositories.repository.", validationLevel, true);
 
                 validateRawRepositories(
                         problems,
                         profile.getPluginRepositories(),
                         prefix,
                         "pluginRepositories.pluginRepository.",
-                        validationLevel);
+                        validationLevel,
+                        true);
             }
 
             DistributionManagement distMgmt = 
model.getDistributionManagement();
             if (distMgmt != null) {
                 validateRawRepository(
-                        problems, distMgmt.getRepository(), 
"distributionManagement.repository.", "", true);
+                        problems, distMgmt.getRepository(), 
"distributionManagement.repository.", "", true, false);
                 validateRawRepository(
                         problems,
                         distMgmt.getSnapshotRepository(),
                         "distributionManagement.snapshotRepository.",
                         "",
-                        true);
+                        true,
+                        false);
             }
         }
     }
@@ -1575,11 +1578,12 @@ private void validateRawRepositories(
             List<Repository> repositories,
             String prefix,
             String prefix2,
-            int validationLevel) {
+            int validationLevel,
+            boolean skipExpressionCheck) {
         Map<String, Repository> index = new HashMap<>();
 
         for (Repository repository : repositories) {
-            validateRawRepository(problems, repository, prefix, prefix2, 
false);
+            validateRawRepository(problems, repository, prefix, prefix2, 
false, skipExpressionCheck);
 
             String key = repository.getId();
 
@@ -1608,23 +1612,25 @@ private void validateRawRepository(
             Repository repository,
             String prefix,
             String prefix2,
-            boolean allowEmptyUrl) {
+            boolean allowEmptyUrl,
+            boolean skipExpressionCheck) {
         if (repository == null) {
             return;
         }
         if (validateStringNotEmpty(
                 prefix, prefix2, "id", problems, Severity.ERROR, Version.V20, 
repository.getId(), null, repository)) {
-            // Check for uninterpolated expressions in ID - these should have 
been interpolated by now
-            Matcher matcher = 
EXPRESSION_NAME_PATTERN.matcher(repository.getId());
-            if (matcher.find()) {
-                addViolation(
-                        problems,
-                        Severity.ERROR,
-                        Version.V40,
-                        prefix + prefix2 + "[" + repository.getId() + "].id",
-                        null,
-                        "contains an uninterpolated expression.",
-                        repository);
+            if (!skipExpressionCheck) {
+                Matcher matcher = 
EXPRESSION_NAME_PATTERN.matcher(repository.getId());
+                if (matcher.find()) {
+                    addViolation(
+                            problems,
+                            Severity.ERROR,
+                            Version.V40,
+                            prefix + prefix2 + "[" + repository.getId() + 
"].id",
+                            null,
+                            "contains an uninterpolated expression.",
+                            repository);
+                }
             }
         }
 
@@ -1639,17 +1645,18 @@ && validateStringNotEmpty(
                         repository.getUrl(),
                         null,
                         repository)) {
-            // Check for uninterpolated expressions in URL - these should have 
been interpolated by now
-            Matcher matcher = 
EXPRESSION_NAME_PATTERN.matcher(repository.getUrl());
-            if (matcher.find()) {
-                addViolation(
-                        problems,
-                        Severity.ERROR,
-                        Version.V40,
-                        prefix + prefix2 + "[" + repository.getId() + "].url",
-                        null,
-                        "contains an uninterpolated expression.",
-                        repository);
+            if (!skipExpressionCheck) {
+                Matcher matcher = 
EXPRESSION_NAME_PATTERN.matcher(repository.getUrl());
+                if (matcher.find()) {
+                    addViolation(
+                            problems,
+                            Severity.ERROR,
+                            Version.V40,
+                            prefix + prefix2 + "[" + repository.getId() + 
"].url",
+                            null,
+                            "contains an uninterpolated expression.",
+                            repository);
+                }
             }
         }
     }
diff --git 
a/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultModelValidatorTest.java
 
b/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultModelValidatorTest.java
index df38b262f7..24ed0596ed 100644
--- 
a/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultModelValidatorTest.java
+++ 
b/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultModelValidatorTest.java
@@ -943,6 +943,12 @@ void repositoryWithUninterpolatedId() throws Exception {
                         && error.contains("contains an uninterpolated 
expression")));
     }
 
+    @Test
+    void profileWithPropertyInRepositoryUrl() throws Exception {
+        SimpleProblemCollector result = 
validateRaw("raw-model/profile-with-property-in-repository-url.xml");
+        assertViolations(result, 0, 0, 0);
+    }
+
     @Test
     void profileActivationWithAllowedExpression() throws Exception {
         SimpleProblemCollector result = validateRaw(
diff --git 
a/impl/maven-impl/src/test/resources/poms/validation/raw-model/profile-with-property-in-repository-url.xml
 
b/impl/maven-impl/src/test/resources/poms/validation/raw-model/profile-with-property-in-repository-url.xml
new file mode 100644
index 0000000000..e954139c2c
--- /dev/null
+++ 
b/impl/maven-impl/src/test/resources/poms/validation/raw-model/profile-with-property-in-repository-url.xml
@@ -0,0 +1,68 @@
+<?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.validation</groupId>
+  <artifactId>project</artifactId>
+  <version>1.0.0-SNAPSHOT</version>
+
+  <profiles>
+    <profile>
+      <id>maven-mirror</id>
+      <activation>
+        <property><name>env.MAVEN_MIRROR_URL</name></property>
+      </activation>
+      <repositories>
+        <repository>
+          <id>maven-mirror</id>
+          <url>${env.MAVEN_MIRROR_URL}</url>
+        </repository>
+      </repositories>
+    </profile>
+    <profile>
+      <id>snapshots-and-staging</id>
+      <properties>
+        
<asf.staging>https://repository.apache.org/content/groups/staging/</asf.staging>
+        
<asf.snapshots>https://repository.apache.org/content/repositories/snapshots/</asf.snapshots>
+      </properties>
+      <pluginRepositories>
+        <pluginRepository>
+          <id>ASF-Staging</id>
+          <url>${asf.staging}</url>
+        </pluginRepository>
+        <pluginRepository>
+          <id>ASF-Snapshots</id>
+          <url>${asf.snapshots}</url>
+          <snapshots>
+            <enabled>true</enabled>
+          </snapshots>
+          <releases>
+            <enabled>false</enabled>
+          </releases>
+        </pluginRepository>
+      </pluginRepositories>
+    </profile>
+  </profiles>
+
+</project>

Reply via email to