You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by gn...@apache.org on 2026/08/26 22:45:34 UTC

(maven) branch forward-port-mng-8709 created (now 84d35f57be)

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

gnodet pushed a change to branch forward-port-mng-8709
in repository https://gitbox.apache.org/repos/asf/maven.git


      at 84d35f57be [MNG-8709] Use active profile properties for consumer POM validation

This branch includes the following new commits:

     new 84d35f57be [MNG-8709] Use active profile properties for consumer POM validation

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(maven) 01/01: [MNG-8709] Use active profile properties for consumer POM validation

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

gnodet pushed a commit to branch forward-port-mng-8709
in repository https://gitbox.apache.org/repos/asf/maven.git

commit 84d35f57bee87313905f073551b15d2d771fbaa8
Author: Goutam Adwant <86...@users.noreply.github.com>
AuthorDate: Wed Aug 26 15:43:32 2026 -0700

    [MNG-8709] Use active profile properties for consumer POM validation
    
    * [MNG-8709] Fix consumer POM profile property validation
    
    Use properties from profiles already active in the built project when validating the consumer model. Preserve user-property precedence and keep the generated consumer POM profile-backed.
    
    Add a Core IT for dependency versions supplied by an active-by-default profile.
    
    * [MNG-8709] Add unit tests and fix IT version range
    
    Address review feedback:
    - Add unit test verifying active profile properties are injected into
      the BUILD_CONSUMER request's user properties
    - Add unit test verifying session user properties (-D flags) override
      profile-defined properties (precedence correctness)
    - Fix IT version range from [4.0.0-rc-6,) to [4.0.0-rc-7,) since
      rc-6 was released without this fix
    
    Co-Authored-By: Claude Opus 4.6 <no...@anthropic.com>
    
    ---------
    
    Co-authored-by: Guillaume Nodet <gn...@gmail.com>
    Co-authored-by: Claude Opus 4.6 <no...@anthropic.com>
---
 .../impl/DefaultConsumerPomBuilder.java            |  11 ++-
 .../impl/ConsumerPomBuilderTest.java               | 109 +++++++++++++++++++++
 ...MavenITmng8709ProfileDependencyVersionTest.java |  51 ++++++++++
 .../mng-8709-profile-dependency-version/pom.xml    |  49 +++++++++
 4 files changed, 219 insertions(+), 1 deletion(-)

diff --git a/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/DefaultConsumerPomBuilder.java b/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/DefaultConsumerPomBuilder.java
index 8a9e57e646..05bb586d7c 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/DefaultConsumerPomBuilder.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/DefaultConsumerPomBuilder.java
@@ -408,7 +408,16 @@ private ModelBuilderResult buildModel(RepositorySystemSession session, MavenProj
         request.source(src);
         request.locationTracking(false);
         request.systemProperties(iSession.getSystemProperties());
-        request.userProperties(iSession.getUserProperties());
+        Map<String, String> userProperties = new LinkedHashMap<>();
+        // BUILD_CONSUMER does not reactivate project profiles, so expose properties from profiles
+        // that were already active when the project model was built.
+        if (project != null && project.getActiveProfiles() != null) {
+            for (org.apache.maven.model.Profile profile : project.getActiveProfiles()) {
+                userProperties.putAll(profile.getDelegate().getProperties());
+            }
+        }
+        userProperties.putAll(iSession.getUserProperties());
+        request.userProperties(userProperties);
         request.lifecycleBindingsInjector(lifecycleBindingsInjector::injectLifecycleBindings);
         // Pass remote repositories so that the model builder can resolve BOM imports
         // from non-central repositories (e.g., repositories defined in settings.xml profiles).
diff --git a/impl/maven-core/src/test/java/org/apache/maven/internal/transformation/impl/ConsumerPomBuilderTest.java b/impl/maven-core/src/test/java/org/apache/maven/internal/transformation/impl/ConsumerPomBuilderTest.java
index 76a9752f55..a601a09947 100644
--- a/impl/maven-core/src/test/java/org/apache/maven/internal/transformation/impl/ConsumerPomBuilderTest.java
+++ b/impl/maven-core/src/test/java/org/apache/maven/internal/transformation/impl/ConsumerPomBuilderTest.java
@@ -24,6 +24,7 @@
 import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
 import org.apache.maven.api.DependencyCoordinates;
 import org.apache.maven.api.Node;
@@ -276,4 +277,112 @@ void testConsumerPomPassesProjectRepositoriesToModelBuilder() throws Exception {
                 consumerRequest.getRepositories().stream().anyMatch(r -> "custom-repo".equals(r.getId()));
         assertTrue(hasCustomRepo, "Consumer POM model builder request should include the project's custom repository");
     }
+
+    /**
+     * Verifies that the consumer POM builder injects properties from the project's
+     * active profiles into the BUILD_CONSUMER request's user properties.
+     * <p>
+     * Without the fix in {@code DefaultConsumerPomBuilder.buildModel()}, the
+     * {@code ModelBuilderRequest} only carries session user properties. When a
+     * dependency version is defined via a profile property (e.g.
+     * {@code <version>${my.version}</version>}), model validation would reject the
+     * unresolved expression as an invalid version.
+     * <p>
+     * Session user properties ({@code -D} flags) must still take precedence over
+     * profile-defined properties.
+     */
+    @Test
+    void testConsumerPomIncludesActiveProfileProperties() throws Exception {
+        setRootDirectory("trivial");
+        Path file = Paths.get("src/test/resources/consumer/trivial/child/pom.xml");
+
+        MavenProject project = getEffectiveModel(file);
+
+        // Create an active profile with a property, simulating a profile that defines
+        // a dependency version (the scenario fixed by MNG-8709).
+        org.apache.maven.api.model.Profile apiProfile = org.apache.maven.api.model.Profile.newBuilder()
+                .id("version-profile")
+                .properties(Map.of("dep.version", "1.0.0"))
+                .build();
+        org.apache.maven.model.Profile modelProfile = new org.apache.maven.model.Profile(apiProfile);
+        project.setActiveProfiles(List.of(modelProfile));
+
+        // Spy on the ModelBuilderSession to capture the ModelBuilderRequest
+        ModelBuilder.ModelBuilderSession originalMbs = modelBuilder.newSession();
+        ModelBuilder.ModelBuilderSession spyMbs = Mockito.spy(originalMbs);
+        InternalSession.from(session).getData().set(SessionData.key(ModelBuilder.ModelBuilderSession.class), spyMbs);
+
+        // Build the consumer POM
+        builder.build(session, project, Sources.buildSource(file));
+
+        // Capture the ModelBuilderRequest passed to the ModelBuilderSession
+        ArgumentCaptor<ModelBuilderRequest> requestCaptor = ArgumentCaptor.forClass(ModelBuilderRequest.class);
+        Mockito.verify(spyMbs, Mockito.atLeastOnce()).build(requestCaptor.capture());
+
+        // Find the BUILD_CONSUMER request
+        ModelBuilderRequest consumerRequest = requestCaptor.getAllValues().stream()
+                .filter(r -> r.getRequestType() == ModelBuilderRequest.RequestType.BUILD_CONSUMER)
+                .findFirst()
+                .orElse(null);
+
+        assertNotNull(consumerRequest, "Expected a BUILD_CONSUMER request to be made");
+
+        // Verify that user properties contain the profile property
+        Map<String, String> userProps = consumerRequest.getUserProperties();
+        assertNotNull(userProps, "User properties should not be null");
+        assertTrue(
+                userProps.containsKey("dep.version"), "User properties should contain properties from active profiles");
+        assertTrue(
+                "1.0.0".equals(userProps.get("dep.version")),
+                "Profile property 'dep.version' should have value '1.0.0'");
+    }
+
+    /**
+     * Verifies that session user properties ({@code -D} flags) take precedence
+     * over profile-defined properties in the BUILD_CONSUMER request.
+     */
+    @Test
+    void testConsumerPomSessionPropertiesOverrideProfileProperties() throws Exception {
+        MavenExecutionRequest execRequest = setRootDirectory("trivial");
+        Path file = Paths.get("src/test/resources/consumer/trivial/child/pom.xml");
+
+        MavenProject project = getEffectiveModel(file);
+
+        // Create an active profile with a property
+        org.apache.maven.api.model.Profile apiProfile = org.apache.maven.api.model.Profile.newBuilder()
+                .id("version-profile")
+                .properties(Map.of("dep.version", "1.0.0"))
+                .build();
+        org.apache.maven.model.Profile modelProfile = new org.apache.maven.model.Profile(apiProfile);
+        project.setActiveProfiles(List.of(modelProfile));
+
+        // Set the same property as a session user property (simulating -Ddep.version=2.0.0)
+        execRequest.getUserProperties().setProperty("dep.version", "2.0.0");
+
+        // Spy on the ModelBuilderSession to capture the ModelBuilderRequest
+        ModelBuilder.ModelBuilderSession originalMbs = modelBuilder.newSession();
+        ModelBuilder.ModelBuilderSession spyMbs = Mockito.spy(originalMbs);
+        InternalSession.from(session).getData().set(SessionData.key(ModelBuilder.ModelBuilderSession.class), spyMbs);
+
+        // Build the consumer POM
+        builder.build(session, project, Sources.buildSource(file));
+
+        // Capture the BUILD_CONSUMER request
+        ArgumentCaptor<ModelBuilderRequest> requestCaptor = ArgumentCaptor.forClass(ModelBuilderRequest.class);
+        Mockito.verify(spyMbs, Mockito.atLeastOnce()).build(requestCaptor.capture());
+
+        ModelBuilderRequest consumerRequest = requestCaptor.getAllValues().stream()
+                .filter(r -> r.getRequestType() == ModelBuilderRequest.RequestType.BUILD_CONSUMER)
+                .findFirst()
+                .orElse(null);
+
+        assertNotNull(consumerRequest, "Expected a BUILD_CONSUMER request to be made");
+
+        // Session user properties must override profile properties
+        Map<String, String> userProps = consumerRequest.getUserProperties();
+        assertTrue(
+                "2.0.0".equals(userProps.get("dep.version")),
+                "Session user property should override profile property; expected '2.0.0' but got '"
+                        + userProps.get("dep.version") + "'");
+    }
 }
diff --git a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng8709ProfileDependencyVersionTest.java b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng8709ProfileDependencyVersionTest.java
new file mode 100644
index 0000000000..009c189d50
--- /dev/null
+++ b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng8709ProfileDependencyVersionTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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.nio.file.Files;
+import java.nio.file.Path;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class MavenITmng8709ProfileDependencyVersionTest extends AbstractMavenIntegrationTestCase {
+
+    MavenITmng8709ProfileDependencyVersionTest() {
+        super("[4.0.0-rc-7,)");
+    }
+
+    @Test
+    void dependencyVersionFromActiveProfileIsValidForConsumerPom() throws Exception {
+        Path basedir = extractResources("/mng-8709-profile-dependency-version")
+                .getAbsoluteFile()
+                .toPath();
+
+        Verifier verifier = newVerifier(basedir.toString());
+        verifier.addCliArgument("install");
+        verifier.execute();
+        verifier.verifyErrorFreeLog();
+
+        Path consumerPom = Path.of(verifier.getArtifactPath(
+                "org.apache.maven.its.mng8709", "profile-version", "1.0", "pom"));
+        String content = Files.readString(consumerPom);
+        assertTrue(content.contains("<activeByDefault>true</activeByDefault>"));
+        assertTrue(content.contains("<version>${junit.version}</version>"));
+    }
+}
diff --git a/its/core-it-suite/src/test/resources/mng-8709-profile-dependency-version/pom.xml b/its/core-it-suite/src/test/resources/mng-8709-profile-dependency-version/pom.xml
new file mode 100644
index 0000000000..911a7986c3
--- /dev/null
+++ b/its/core-it-suite/src/test/resources/mng-8709-profile-dependency-version/pom.xml
@@ -0,0 +1,49 @@
+<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>org.apache.maven.its.mng8709</groupId>
+  <artifactId>profile-version</artifactId>
+  <version>1.0</version>
+
+  <profiles>
+    <profile>
+      <id>default-versions</id>
+      <activation>
+        <activeByDefault>true</activeByDefault>
+      </activation>
+      <properties>
+        <junit.version>5.11.0</junit.version>
+      </properties>
+    </profile>
+  </profiles>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.junit.jupiter</groupId>
+      <artifactId>junit-jupiter-api</artifactId>
+      <version>${junit.version}</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+</project>