perNyfelt commented on code in PR #182:
URL: 
https://github.com/apache/maven-resolver-ant-tasks/pull/182#discussion_r3856954155


##########
src/main/java/org/apache/maven/resolver/internal/ant/AntRepoSys.java:
##########
@@ -752,6 +753,13 @@ public CollectResult collectDependencies(
             }
 
             if (dependencies != null) {
+                if (dependencies.getPom() != null) {
+                    // Maven collects for the project artifact; without a root 
the project cannot be told apart
+                    // from its own dependencies when it shows up in its graph.
+                    Model model = dependencies.getPom().getModel(task);
+                    collectRequest.setRootArtifact(

Review Comment:
   **low** — two things about the new root artifact.
   
   (a) The rationale in the comment above ("without a root the project cannot 
be told apart from its own dependencies when it shows up in its graph") doesn't 
hold for this resolver. In `maven-resolver-impl` 2.0.21, 
`request.getRootArtifact()` is consumed in exactly two places — building the 
root `DefaultDependencyNode`, and seeding `DefaultDependencyCollectionContext` 
— and no shipped `DependencySelector`, `DependencyManager` or 
`DependencyTraverser` ever reads `DependencyCollectionContext.getArtifact()`. 
It changes nothing about cycle detection.
   
   (b) Where it *is* visible, it diverges from Maven: the extension is 
hard-coded to `"pom"`, whereas `DefaultProjectDependenciesResolver` uses 
`RepositoryUtils.toArtifact(project.getArtifact())`, i.e. the project's 
packaging. A POM with `<packaging>jar</packaging>` now logs its graph root as 
`g:a:pom:v` under verbose `<resolve>` output where Maven shows `g:a:jar:v`, and 
a user-supplied graph transformer inspecting the root sees the wrong extension.
   
   Since parity is the point of the commit, `model.getPackaging()` is the value 
to use.



##########
src/test/java/org/apache/maven/resolver/internal/ant/MavenParityTest.java:
##########
@@ -0,0 +1,173 @@
+/*
+ * 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.resolver.internal.ant;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.TreeSet;
+
+import junit.framework.JUnit4TestAdapter;
+import org.apache.maven.resolver.internal.ant.types.Dependencies;
+import org.apache.maven.resolver.internal.ant.types.Pom;
+import org.apache.maven.resolver.internal.ant.types.RemoteRepositories;
+import org.apache.maven.resolver.internal.ant.types.RemoteRepository;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.collection.CollectResult;
+import org.eclipse.aether.graph.DependencyFilter;
+import org.eclipse.aether.graph.DependencyNode;
+import org.eclipse.aether.util.filter.DependencyFilterUtils;
+import org.eclipse.aether.util.graph.visitor.NodeListGenerator;
+import 
org.eclipse.aether.util.graph.visitor.PreorderDependencyNodeConsumerVisitor;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Resolves a POM through the Ant tasks and compares the outcome with what 
{@code mvn dependency:list}
+ * reports for the same POM, per classpath scope.
+ * <p>
+ * The expectations next to the fixture POM are recorded Maven output, not 
hand-written; their headers carry
+ * the Maven version they came from and the command that re-records them. A 
difference here is a difference
+ * between the two tools, which is the thing worth failing a build over.
+ */
+public class MavenParityTest {
+    public static junit.framework.Test suite() {
+        return new JUnit4TestAdapter(MavenParityTest.class);
+    }
+
+    private static final File FIXTURE_DIR =
+            new File(new File("").getAbsoluteFile(), 
"src/test/resources/ant/MavenParity");
+
+    private Project project;
+
+    private Task task;
+
+    @Before
+    public void setUp() {

Review Comment:
   **low** — `setUp` leaves `user.home` pointing at the real home directory and 
never neutralizes the developer's `~/.m2/settings.xml`. Its sibling 
`PomRepositoriesTest` deliberately installs an empty `settings.xml` for exactly 
this reason.
   
   The test then resolves 15+ artifacts from Central and asserts exact 
versions. A developer or CI agent whose settings carry 
`<offline>true</offline>`, or a mirror that doesn't proxy Central, gets three 
failures whose message claims Ant and Maven disagree about the classpath — 
pointing at a parity bug that isn't there.
   
   Applying the same `setUserSettings` / `setGlobalSettings` isolation as 
`PomRepositoriesTest` (the fixture already exists) makes the test measure what 
it claims to measure.



##########
src/test/java/org/apache/maven/resolver/internal/ant/MavenParityTest.java:
##########
@@ -0,0 +1,173 @@
+/*
+ * 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.resolver.internal.ant;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.TreeSet;
+
+import junit.framework.JUnit4TestAdapter;
+import org.apache.maven.resolver.internal.ant.types.Dependencies;
+import org.apache.maven.resolver.internal.ant.types.Pom;
+import org.apache.maven.resolver.internal.ant.types.RemoteRepositories;
+import org.apache.maven.resolver.internal.ant.types.RemoteRepository;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.collection.CollectResult;
+import org.eclipse.aether.graph.DependencyFilter;
+import org.eclipse.aether.graph.DependencyNode;
+import org.eclipse.aether.util.filter.DependencyFilterUtils;
+import org.eclipse.aether.util.graph.visitor.NodeListGenerator;
+import 
org.eclipse.aether.util.graph.visitor.PreorderDependencyNodeConsumerVisitor;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Resolves a POM through the Ant tasks and compares the outcome with what 
{@code mvn dependency:list}
+ * reports for the same POM, per classpath scope.
+ * <p>
+ * The expectations next to the fixture POM are recorded Maven output, not 
hand-written; their headers carry
+ * the Maven version they came from and the command that re-records them. A 
difference here is a difference
+ * between the two tools, which is the thing worth failing a build over.
+ */
+public class MavenParityTest {
+    public static junit.framework.Test suite() {
+        return new JUnit4TestAdapter(MavenParityTest.class);
+    }
+
+    private static final File FIXTURE_DIR =
+            new File(new File("").getAbsoluteFile(), 
"src/test/resources/ant/MavenParity");
+
+    private Project project;
+
+    private Task task;
+
+    @Before
+    public void setUp() {
+        project = new Project();
+        project.setProperty("user.home", System.getProperty("user.home"));
+        project.setProperty(
+                "maven.repo.local",
+                new File(new File("").getAbsoluteFile(), 
"target/ant/local-repo").getAbsolutePath());
+
+        task = new Task() {};
+        task.setProject(project);
+    }
+
+    @Test
+    public void testCompileClasspathMatchesMaven() throws IOException {
+        assertParity("compile");
+    }
+
+    @Test
+    public void testRuntimeClasspathMatchesMaven() throws IOException {
+        assertParity("runtime");
+    }
+
+    @Test
+    public void testTestClasspathMatchesMaven() throws IOException {
+        assertParity("test");
+    }
+
+    private void assertParity(String classpath) throws IOException {
+        List<String> expected = readExpectation(classpath);
+        List<String> actual = resolve(classpath);
+
+        assertEquals(
+                "the Ant tasks and Maven disagree about the " + classpath + " 
classpath of "
+                        + new File(FIXTURE_DIR, "pom.xml"),
+                String.join("\n", expected),
+                String.join("\n", actual));
+    }
+
+    private List<String> readExpectation(String classpath) throws IOException {

Review Comment:
   **low** — `readExpectation` preserves the fixture's line order, while 
`resolve()` returns a `TreeSet` in Java's natural `String` order, and the 
assertion compares the two joined strings. The recorded fixtures happen to 
agree today, but the re-record command in each expectation header is a bare 
`sort`, which is locale-collated.
   
   Re-recording under `en_US.UTF-8` (the default on most dev machines) can emit 
an order `TreeSet` won't reproduce — punctuation-sensitive pairs like 
`…:junit-jupiter-api:…` vs `…:junit-jupiter:jar:…` are exactly the shape that 
diverges. The failure then reads "the Ant tasks and Maven disagree about the 
test classpath", for a parity bug that doesn't exist.
   
   Sorting `lines` before returning (or documenting `LC_ALL=C sort` in the 
headers) removes the trap.



##########
src/main/java/org/apache/maven/resolver/internal/ant/AntRepoSys.java:
##########
@@ -791,69 +799,37 @@ private void populateCollectRequest(
 
         if (dependencies.getPom() != null) {
             Model model = dependencies.getPom().getModel(task);
-            if (model.getDependencyManagement() != null) {
-                for (org.apache.maven.model.Dependency manDep :
-                        model.getDependencyManagement().getDependencies()) {
-                    Dependency dependency = new Dependency();
-                    dependency.setArtifactId(manDep.getArtifactId());
-                    dependency.setClassifier(manDep.getClassifier());
-                    dependency.setGroupId(manDep.getGroupId());
-                    dependency.setScope(manDep.getScope());
-                    dependency.setType(manDep.getType());
-                    dependency.setVersion(manDep.getVersion());
-                    if (manDep.getSystemPath() != null
-                            && !manDep.getSystemPath().isEmpty()) {
-                        
dependency.setSystemPath(task.getProject().resolveFile(manDep.getSystemPath()));
-                    }
-                    for (org.apache.maven.model.Exclusion exc : 
manDep.getExclusions()) {
-                        Exclusion exclusion = new Exclusion();
-                        exclusion.setGroupId(exc.getGroupId());
-                        exclusion.setArtifactId(exc.getArtifactId());
-                        exclusion.setClassifier("*");
-                        exclusion.setExtension("*");
-                        dependency.addExclusion(exclusion);
-                    }
-                    collectRequest.addManagedDependency(
-                            ConverterUtils.toManagedDependency(dependency, 
globalExclusions, session));
-                }
+            ArtifactDescriptorResult descriptor = 
ConverterUtils.toArtifactDescriptor(session, model);
+
+            if (!descriptor.getRepositories().isEmpty()) {
+                collectRequest.setRepositories(getRemoteRepoMan()

Review Comment:
   **medium** — the POM's repositories are aggregated unconditionally, 
including when the caller passed an explicit `<remoterepos>` / 
`resolver.repositories`.
   
   Because the effective model inherits the super POM, 
`descriptor.getRepositories()` always contains Maven Central even for a POM 
that declares none — `PomRepositoriesTest` pins exactly that. So the guard 
`!descriptor.getRepositories().isEmpty()` is effectively always true.
   
   Concrete scenario: an internal build sets `resolver.repositories` to only a 
corporate Nexus and has no `<mirrorOf>*</mirrorOf>`. Today a coordinate missing 
from Nexus fails the build; after this change the collect request also carries 
Central and the artifact is fetched from there instead. That is new network 
egress and a dependency-confusion exposure for builds that were previously 
bounded — a `<resolve>` that reached no external host may now do so.
   
   The PR description flags this already. If it ships as-is it wants a property 
gate, or at minimum a release note stating that an explicit `<remoterepos>` is 
no longer a bound on resolution.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to