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

gnodet pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven.git


The following commit(s) were added to refs/heads/master by this push:
     new cb5ee55803 Fix [unknown project] messages in error output (#11324)
cb5ee55803 is described below

commit cb5ee55803a7f2acf49b86945d7df7101312b6aa
Author: Guillaume Nodet <[email protected]>
AuthorDate: Tue Oct 28 18:09:27 2025 +0100

    Fix [unknown project] messages in error output (#11324)
    
    Resolves issue #11292 where Maven shows '[unknown project]' in error
    messages when using -e -X flags, particularly in CI environments.
    
    The issue occurred because DefaultProjectBuilder was creating
    DefaultProjectBuildingResult with null project information when
    ModelBuilderResult.getEffectiveModel() returned null, resulting in
    empty projectId and causing ProjectBuildingException.createMessage()
    to display '[unknown project]' as a fallback.
    
    This fix extracts project identification from available model data
    (rawModel or fileModel) when effectiveModel is null, following the
    same pattern used in ModelBuilderException.getModelId().
    
    Changes:
    - Added extractProjectId() helper method that falls back to rawModel
      or fileModel when effectiveModel is null
    - Modified project building result creation to use extracted projectId
      and POM file information instead of null values
    - Maintains backward compatibility: when all models are null, still
      returns empty string to preserve '[unknown project]' fallback for
      truly unknown projects
    
    This provides better error messages showing meaningful project
    identification like 'com.example:my-project:jar:1.0.0' even when
    project building fails, while maintaining existing error handling
    patterns.
---
 .../maven/project/DefaultProjectBuilder.java       |  29 ++-
 .../maven/project/DefaultProjectBuilderTest.java   | 214 +++++++++++++++++++++
 2 files changed, 242 insertions(+), 1 deletion(-)

diff --git 
a/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java
 
b/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java
index 245d85117d..5b2576eb9d 100644
--- 
a/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java
+++ 
b/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java
@@ -581,7 +581,13 @@ private List<ProjectBuildingResult> build(File pomFile, 
boolean recursive) {
                     results.add(new DefaultProjectBuildingResult(
                             project, convert(r.getProblemCollector()), 
resolutionResult));
                 } else {
-                    results.add(new DefaultProjectBuildingResult(null, 
convert(r.getProblemCollector()), null));
+                    // Extract project identification even when effective 
model is null
+                    String projectId = extractProjectId(r);
+                    File sourcePomFile = r.getSource() != null && 
r.getSource().getPath() != null
+                            ? r.getSource().getPath().toFile()
+                            : null;
+                    results.add(new DefaultProjectBuildingResult(
+                            projectId, sourcePomFile, 
convert(r.getProblemCollector())));
                 }
             }
             return results;
@@ -1013,6 +1019,27 @@ private static ModelSource 
createStubModelSource(Artifact artifact) {
         return new StubModelSource(xml, artifact);
     }
 
+    /**
+     * Extracts project identification from ModelBuilderResult, falling back 
to rawModel or fileModel
+     * when effectiveModel is null, similar to 
ModelBuilderException.getModelId().
+     */
+    private static String extractProjectId(ModelBuilderResult result) {
+        Model model = null;
+        if (result.getEffectiveModel() != null) {
+            model = result.getEffectiveModel();
+        } else if (result.getRawModel() != null) {
+            model = result.getRawModel();
+        } else if (result.getFileModel() != null) {
+            model = result.getFileModel();
+        }
+
+        if (model != null) {
+            return model.getId();
+        }
+
+        return "";
+    }
+
     static String getGroupId(Model model) {
         String groupId = model.getGroupId();
         if (groupId == null && model.getParent() != null) {
diff --git 
a/impl/maven-core/src/test/java/org/apache/maven/project/DefaultProjectBuilderTest.java
 
b/impl/maven-core/src/test/java/org/apache/maven/project/DefaultProjectBuilderTest.java
new file mode 100644
index 0000000000..7532aebbc1
--- /dev/null
+++ 
b/impl/maven-core/src/test/java/org/apache/maven/project/DefaultProjectBuilderTest.java
@@ -0,0 +1,214 @@
+/*
+ * 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.project;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Method;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+
+import org.apache.maven.api.model.Model;
+import org.apache.maven.api.model.Profile;
+import org.apache.maven.api.services.ModelBuilderRequest;
+import org.apache.maven.api.services.ModelBuilderResult;
+import org.apache.maven.api.services.ModelProblem;
+import org.apache.maven.api.services.ModelSource;
+import org.apache.maven.api.services.ProblemCollector;
+import org.apache.maven.api.services.Source;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+/**
+ * Test for {@link DefaultProjectBuilder} extractProjectId method.
+ */
+@SuppressWarnings("deprecation")
+class DefaultProjectBuilderTest {
+
+    /**
+     * Test the extractProjectId method to ensure it properly falls back to 
rawModel or fileModel
+     * when effectiveModel is null, addressing issue #11292.
+     */
+    @Test
+    void testExtractProjectIdFallback() throws Exception {
+        // Use reflection to access the private extractProjectId method
+        Method extractProjectIdMethod =
+                
DefaultProjectBuilder.class.getDeclaredMethod("extractProjectId", 
ModelBuilderResult.class);
+        extractProjectIdMethod.setAccessible(true);
+
+        // Create a mock ModelBuilderResult with null effectiveModel but 
available rawModel
+        ModelBuilderResult mockResult = new MockModelBuilderResult(
+                null, // effectiveModel is null
+                createMockModel("com.example", "test-project", "1.0.0"), // 
rawModel is available
+                null // fileModel is null
+                );
+
+        String projectId = (String) extractProjectIdMethod.invoke(null, 
mockResult);
+
+        assertNotNull(projectId, "Project ID should not be null");
+        assertEquals(
+                "com.example:test-project:jar:1.0.0",
+                projectId,
+                "Should extract project ID from rawModel when effectiveModel 
is null");
+    }
+
+    /**
+     * Test extractProjectId with fileModel fallback when both effectiveModel 
and rawModel are null.
+     */
+    @Test
+    void testExtractProjectIdFileModelFallback() throws Exception {
+        Method extractProjectIdMethod =
+                
DefaultProjectBuilder.class.getDeclaredMethod("extractProjectId", 
ModelBuilderResult.class);
+        extractProjectIdMethod.setAccessible(true);
+
+        ModelBuilderResult mockResult = new MockModelBuilderResult(
+                null, // effectiveModel is null
+                null, // rawModel is null
+                createMockModel("com.example", "test-project", "1.0.0") // 
fileModel is available
+                );
+
+        String projectId = (String) extractProjectIdMethod.invoke(null, 
mockResult);
+
+        assertNotNull(projectId, "Project ID should not be null");
+        assertEquals(
+                "com.example:test-project:jar:1.0.0",
+                projectId,
+                "Should extract project ID from fileModel when effectiveModel 
and rawModel are null");
+    }
+
+    /**
+     * Test extractProjectId returns empty string when all models are null.
+     */
+    @Test
+    void testExtractProjectIdAllModelsNull() throws Exception {
+        Method extractProjectIdMethod =
+                
DefaultProjectBuilder.class.getDeclaredMethod("extractProjectId", 
ModelBuilderResult.class);
+        extractProjectIdMethod.setAccessible(true);
+
+        ModelBuilderResult mockResult = new MockModelBuilderResult(null, null, 
null);
+
+        String projectId = (String) extractProjectIdMethod.invoke(null, 
mockResult);
+
+        assertNotNull(projectId, "Project ID should not be null");
+        assertEquals("", projectId, "Should return empty string when all 
models are null");
+    }
+
+    private Model createMockModel(String groupId, String artifactId, String 
version) {
+        return Model.newBuilder()
+                .groupId(groupId)
+                .artifactId(artifactId)
+                .version(version)
+                .packaging("jar")
+                .build();
+    }
+
+    /**
+     * Mock implementation of ModelBuilderResult for testing.
+     */
+    private static class MockModelBuilderResult implements ModelBuilderResult {
+        private final Model effectiveModel;
+        private final Model rawModel;
+        private final Model fileModel;
+
+        MockModelBuilderResult(Model effectiveModel, Model rawModel, Model 
fileModel) {
+            this.effectiveModel = effectiveModel;
+            this.rawModel = rawModel;
+            this.fileModel = fileModel;
+        }
+
+        @Override
+        public Model getEffectiveModel() {
+            return effectiveModel;
+        }
+
+        @Override
+        public Model getRawModel() {
+            return rawModel;
+        }
+
+        @Override
+        public Model getFileModel() {
+            return fileModel;
+        }
+
+        @Override
+        public ModelBuilderRequest getRequest() {
+            return null;
+        }
+
+        // Other required methods with minimal implementations
+        @Override
+        public ModelSource getSource() {
+            return new ModelSource() {
+                @Override
+                public Path getPath() {
+                    return Paths.get("test-pom.xml");
+                }
+
+                @Override
+                public String getLocation() {
+                    return "test-pom.xml";
+                }
+
+                @Override
+                public InputStream openStream() throws IOException {
+                    return null;
+                }
+
+                @Override
+                public Source resolve(String relative) {
+                    return null;
+                }
+
+                @Override
+                public ModelSource resolve(ModelSource.ModelLocator 
modelLocator, String relative) {
+                    return null;
+                }
+            };
+        }
+
+        @Override
+        public Model getParentModel() {
+            return null;
+        }
+
+        @Override
+        public List<Profile> getActivePomProfiles() {
+            return List.of();
+        }
+
+        @Override
+        public List<Profile> getActiveExternalProfiles() {
+            return List.of();
+        }
+
+        @Override
+        public ProblemCollector<ModelProblem> getProblemCollector() {
+            return null;
+        }
+
+        @Override
+        public List<? extends ModelBuilderResult> getChildren() {
+            return List.of();
+        }
+    }
+}

Reply via email to