This is an automated email from the ASF dual-hosted git repository. gnodet pushed a commit to branch maven-4.0.x-test-fixes in repository https://gitbox.apache.org/repos/asf/maven.git
commit 6c192d4d61fecb8829c5de4637d62ea05ad2f029 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 | 70 ++++++++++++---------- .../impl/model/DefaultModelValidatorTest.java | 6 ++ .../profile-with-property-in-repository-url.xml | 68 +++++++++++++++++++++ 3 files changed, 113 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 159ac111d1..79840fd52a 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 @@ -618,39 +618,43 @@ public void validateRawModel(Session s, Model m, int validationLevel, ModelProbl } if (validationLevel > VALIDATION_LEVEL_MINIMAL) { - validateRawRepositories(problems, m.getRepositories(), "repositories.repository.", EMPTY, validationLevel); + validateRawRepositories( + problems, m.getRepositories(), "repositories.repository.", EMPTY, validationLevel, false); validateRawRepositories( problems, m.getPluginRepositories(), "pluginRepositories.pluginRepository.", EMPTY, - validationLevel); + validationLevel, + false); for (Profile profile : m.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 = m.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); } } } @@ -1483,11 +1487,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(); @@ -1516,23 +1521,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); + } } } @@ -1547,17 +1554,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 dc118703bb..1624fd3cb0 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 @@ -910,6 +910,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>
