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

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


The following commit(s) were added to refs/heads/master by this push:
     new 5c7d0bc  [MENFORCER-494] Allow banning dynamic versions in whole tree 
(#294)
5c7d0bc is described below

commit 5c7d0bc55e2e1dfd8d6b8182062f8e511ef5eb0e
Author: Jimmy Axenhus <git...@axenhus.com>
AuthorDate: Sun May 26 13:45:35 2024 +0200

    [MENFORCER-494] Allow banning dynamic versions in whole tree (#294)
    
    * [MENFORCER-494] Allow banning dynamic versions in whole tree
    
    This commit introduces the possibility of banning dynamic versions
    in the entire dependency tree before Maven computes the final
    dependency tree.
    
    * Fix format
    
    ---------
    
    Co-authored-by: Slawomir Jaranowski <s.jaranow...@gmail.com>
---
 .../rules/dependency/BanDynamicVersions.java       | 99 ++++++++++++++--------
 .../enforcer/rules/dependency/ResolverUtil.java    |  4 +-
 .../src/site/apt/banDynamicVersions.apt.vm         |  2 +
 .../menforcer494_dependency-1.0-SNAPSHOT.pom       | 26 ++++++
 .../mrm/repository/menforcer494_dependency-2.0.pom | 26 ++++++
 .../menforcer494_project-1.0-SNAPSHOT.pom          | 34 ++++++++
 .../invoker.properties                             | 18 ++++
 .../projects/ban-dynamic-versions-verbose/pom.xml  | 72 ++++++++++++++++
 .../ban-dynamic-versions-verbose/verify.groovy     | 22 +++++
 9 files changed, 268 insertions(+), 35 deletions(-)

diff --git 
a/enforcer-rules/src/main/java/org/apache/maven/enforcer/rules/dependency/BanDynamicVersions.java
 
b/enforcer-rules/src/main/java/org/apache/maven/enforcer/rules/dependency/BanDynamicVersions.java
index 5a827c8..1aa10ff 100644
--- 
a/enforcer-rules/src/main/java/org/apache/maven/enforcer/rules/dependency/BanDynamicVersions.java
+++ 
b/enforcer-rules/src/main/java/org/apache/maven/enforcer/rules/dependency/BanDynamicVersions.java
@@ -22,11 +22,9 @@ import javax.inject.Inject;
 import javax.inject.Named;
 
 import java.text.ChoiceFormat;
-import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Deque;
 import java.util.List;
 import java.util.Objects;
 import java.util.function.Predicate;
@@ -40,9 +38,12 @@ import org.apache.maven.execution.MavenSession;
 import org.apache.maven.project.MavenProject;
 import org.eclipse.aether.RepositorySystem;
 import org.eclipse.aether.collection.DependencyCollectionException;
+import org.eclipse.aether.graph.DependencyFilter;
 import org.eclipse.aether.graph.DependencyNode;
-import org.eclipse.aether.graph.DependencyVisitor;
-import org.eclipse.aether.util.graph.visitor.TreeDependencyVisitor;
+import org.eclipse.aether.util.graph.manager.DependencyManagerUtils;
+import org.eclipse.aether.util.graph.visitor.PathRecordingDependencyVisitor;
+import org.eclipse.aether.util.version.GenericVersionScheme;
+import org.eclipse.aether.version.InvalidVersionSpecificationException;
 import org.eclipse.aether.version.VersionConstraint;
 
 /**
@@ -110,6 +111,14 @@ public final class BanDynamicVersions extends 
AbstractStandardEnforcerRule {
      */
     private List<String> ignores = null;
 
+    /**
+     * {@code true} if dependencies should be checked before Maven computes 
the final
+     * dependency tree. Setting this property will make the rule check 
dependencies
+     * before any conflicts are resolved. This is similar to the {@code 
verbose}
+     * parameter for the {@code tree} goal for {@code maven-dependency-plugin}.
+     */
+    private boolean verbose;
+
     private final ResolverUtil resolverUtil;
 
     @Inject
@@ -118,9 +127,7 @@ public final class BanDynamicVersions extends 
AbstractStandardEnforcerRule {
         this.resolverUtil = Objects.requireNonNull(resolverUtil);
     }
 
-    private final class BannedDynamicVersionCollector implements 
DependencyVisitor {
-
-        private final Deque<DependencyNode> nodeStack; // all intermediate 
nodes (without the root node)
+    private final class BannedDynamicVersionCollector implements 
DependencyFilter {
 
         private boolean isRoot = true;
 
@@ -128,15 +135,16 @@ public final class BanDynamicVersions extends 
AbstractStandardEnforcerRule {
 
         private final Predicate<DependencyNode> predicate;
 
+        private GenericVersionScheme versionScheme;
+
         public List<String> getViolations() {
             return violations;
         }
 
         BannedDynamicVersionCollector(Predicate<DependencyNode> predicate) {
-            this.nodeStack = new ArrayDeque<>();
             this.predicate = predicate;
-            this.isRoot = true;
             this.violations = new ArrayList<>();
+            this.versionScheme = new GenericVersionScheme();
         }
 
         private boolean isBannedDynamicVersion(VersionConstraint 
versionConstraint) {
@@ -163,30 +171,51 @@ public final class BanDynamicVersions extends 
AbstractStandardEnforcerRule {
         }
 
         @Override
-        public boolean visitEnter(DependencyNode node) {
+        public boolean accept(DependencyNode node, List<DependencyNode> 
parents) {
             if (isRoot) {
                 isRoot = false;
-            } else {
-                getLog().debug("Found node " + node + " with version 
constraint " + node.getVersionConstraint());
-                if (predicate.test(node) && 
isBannedDynamicVersion(node.getVersionConstraint())) {
-                    violations.add("Dependency "
-                            + node.getDependency()
-                            + dumpIntermediatePath(nodeStack)
-                            + " is referenced with a banned dynamic version "
-                            + node.getVersionConstraint());
-                    return false;
+                return false;
+            }
+            getLog().debug("Found node " + node + " with version constraint " 
+ node.getVersionConstraint());
+            if (!predicate.test(node)) {
+                return false;
+            }
+            VersionConstraint versionConstraint = node.getVersionConstraint();
+            if (isBannedDynamicVersion(versionConstraint)) {
+                addViolation(versionConstraint, node, parents);
+                return true;
+            }
+            try {
+                if (verbose) {
+                    String premanagedVersion = 
DependencyManagerUtils.getPremanagedVersion(node);
+                    if (premanagedVersion != null) {
+                        VersionConstraint premanagedContraint = 
versionScheme.parseVersionConstraint(premanagedVersion);
+                        if (isBannedDynamicVersion(premanagedContraint)) {
+                            addViolation(premanagedContraint, node, parents);
+                            return true;
+                        }
+                    }
                 }
-                nodeStack.addLast(node);
+            } catch (InvalidVersionSpecificationException ex) {
+                // This should never happen.
+                throw new RuntimeException("Failed to parse version for " + 
node, ex);
             }
-            return true;
+            return false;
         }
 
-        @Override
-        public boolean visitLeave(DependencyNode node) {
-            if (!nodeStack.isEmpty()) {
-                nodeStack.removeLast();
+        private void addViolation(
+                VersionConstraint versionContraint, DependencyNode node, 
List<DependencyNode> parents) {
+            List<DependencyNode> intermediatePath = new ArrayList<>(parents);
+            if (!intermediatePath.isEmpty()) {
+                // This project is also included in the path, but we do
+                // not want that in the report.
+                intermediatePath.remove(intermediatePath.size() - 1);
             }
-            return true;
+            violations.add("Dependency "
+                    + node.getDependency()
+                    + dumpIntermediatePath(intermediatePath)
+                    + " is referenced with a banned dynamic version "
+                    + versionContraint);
         }
     }
 
@@ -195,7 +224,7 @@ public final class BanDynamicVersions extends 
AbstractStandardEnforcerRule {
 
         try {
             DependencyNode rootDependency =
-                    
resolverUtil.resolveTransitiveDependencies(excludeOptionals, excludedScopes);
+                    resolverUtil.resolveTransitiveDependencies(verbose, 
excludeOptionals, excludedScopes);
 
             List<String> violations = 
collectDependenciesWithBannedDynamicVersions(rootDependency);
             if (!violations.isEmpty()) {
@@ -239,16 +268,19 @@ public final class BanDynamicVersions extends 
AbstractStandardEnforcerRule {
         } else {
             predicate = d -> true;
         }
-        BannedDynamicVersionCollector bannedDynamicVersionCollector = new 
BannedDynamicVersionCollector(predicate);
-        DependencyVisitor depVisitor = new 
TreeDependencyVisitor(bannedDynamicVersionCollector);
-        rootDependency.accept(depVisitor);
-        return bannedDynamicVersionCollector.getViolations();
+        BannedDynamicVersionCollector collector = new 
BannedDynamicVersionCollector(predicate);
+        rootDependency.accept(new PathRecordingDependencyVisitor(collector));
+        return collector.getViolations();
+    }
+
+    public void setVerbose(boolean verbose) {
+        this.verbose = verbose;
     }
 
     @Override
     public String toString() {
         return String.format(
-                "BanDynamicVersions[allowSnapshots=%b, allowLatest=%b, 
allowRelease=%b, allowRanges=%b, allowRangesWithIdenticalBounds=%b, 
excludeOptionals=%b, excludedScopes=%s, ignores=%s]",
+                "BanDynamicVersions[allowSnapshots=%b, allowLatest=%b, 
allowRelease=%b, allowRanges=%b, allowRangesWithIdenticalBounds=%b, 
excludeOptionals=%b, excludedScopes=%s, ignores=%s, verbose=%b]",
                 allowSnapshots,
                 allowLatest,
                 allowRelease,
@@ -256,6 +288,7 @@ public final class BanDynamicVersions extends 
AbstractStandardEnforcerRule {
                 allowRangesWithIdenticalBounds,
                 excludeOptionals,
                 excludedScopes,
-                ignores);
+                ignores,
+                verbose);
     }
 }
diff --git 
a/enforcer-rules/src/main/java/org/apache/maven/enforcer/rules/dependency/ResolverUtil.java
 
b/enforcer-rules/src/main/java/org/apache/maven/enforcer/rules/dependency/ResolverUtil.java
index 774812a..6ccd62b 100644
--- 
a/enforcer-rules/src/main/java/org/apache/maven/enforcer/rules/dependency/ResolverUtil.java
+++ 
b/enforcer-rules/src/main/java/org/apache/maven/enforcer/rules/dependency/ResolverUtil.java
@@ -110,8 +110,8 @@ class ResolverUtil {
         return resolveTransitiveDependencies(false, excludeOptional, 
excludedScopes);
     }
 
-    private DependencyNode resolveTransitiveDependencies(
-            boolean verbose, boolean excludeOptional, List<String> 
excludedScopes) throws EnforcerRuleException {
+    DependencyNode resolveTransitiveDependencies(boolean verbose, boolean 
excludeOptional, List<String> excludedScopes)
+            throws EnforcerRuleException {
 
         try {
             RepositorySystemSession repositorySystemSession = 
session.getRepositorySession();
diff --git a/enforcer-rules/src/site/apt/banDynamicVersions.apt.vm 
b/enforcer-rules/src/site/apt/banDynamicVersions.apt.vm
index 8062cd6..cf8f7b2 100644
--- a/enforcer-rules/src/site/apt/banDynamicVersions.apt.vm
+++ b/enforcer-rules/src/site/apt/banDynamicVersions.apt.vm
@@ -69,6 +69,8 @@ Ban Dynamic Versions
         
         []
 
+  * <<verbose>> - if <<<true>>> the dependency tree is checked before Maven 
computes the final dependency tree. Setting this property will make the rule 
check dependencies before any conflicts are resolved. This is similar to the 
<<<verbose>>> parameter for the <<<tree>>> goal for 
<<<maven-dependency-plugin>>>. Default is <<<false>>>.
+  
   []
 
   Sample Plugin Configuration:
diff --git 
a/maven-enforcer-plugin/src/it/mrm/repository/menforcer494_dependency-1.0-SNAPSHOT.pom
 
b/maven-enforcer-plugin/src/it/mrm/repository/menforcer494_dependency-1.0-SNAPSHOT.pom
new file mode 100644
index 0000000..2f20acf
--- /dev/null
+++ 
b/maven-enforcer-plugin/src/it/mrm/repository/menforcer494_dependency-1.0-SNAPSHOT.pom
@@ -0,0 +1,26 @@
+<?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.plugins.enforcer.its</groupId>
+  <artifactId>menforcer494_dependency</artifactId>
+  <version>1.0-SNAPSHOT</version>
+</project>
\ No newline at end of file
diff --git 
a/maven-enforcer-plugin/src/it/mrm/repository/menforcer494_dependency-2.0.pom 
b/maven-enforcer-plugin/src/it/mrm/repository/menforcer494_dependency-2.0.pom
new file mode 100644
index 0000000..e254d15
--- /dev/null
+++ 
b/maven-enforcer-plugin/src/it/mrm/repository/menforcer494_dependency-2.0.pom
@@ -0,0 +1,26 @@
+<?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.plugins.enforcer.its</groupId>
+  <artifactId>menforcer494_dependency</artifactId>
+  <version>2.0</version>
+</project>
\ No newline at end of file
diff --git 
a/maven-enforcer-plugin/src/it/mrm/repository/menforcer494_project-1.0-SNAPSHOT.pom
 
b/maven-enforcer-plugin/src/it/mrm/repository/menforcer494_project-1.0-SNAPSHOT.pom
new file mode 100644
index 0000000..facb0b0
--- /dev/null
+++ 
b/maven-enforcer-plugin/src/it/mrm/repository/menforcer494_project-1.0-SNAPSHOT.pom
@@ -0,0 +1,34 @@
+<?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.plugins.enforcer.its</groupId>
+  <artifactId>menforcer494_project</artifactId>
+  <version>1.0-SNAPSHOT</version>
+  
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.maven.plugins.enforcer.its</groupId>
+      <artifactId>menforcer494_dependency</artifactId>
+      <version>1.0-SNAPSHOT</version>
+    </dependency>  
+  </dependencies>
+</project>
\ No newline at end of file
diff --git 
a/maven-enforcer-plugin/src/it/projects/ban-dynamic-versions-verbose/invoker.properties
 
b/maven-enforcer-plugin/src/it/projects/ban-dynamic-versions-verbose/invoker.properties
new file mode 100644
index 0000000..58b6526
--- /dev/null
+++ 
b/maven-enforcer-plugin/src/it/projects/ban-dynamic-versions-verbose/invoker.properties
@@ -0,0 +1,18 @@
+# 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.
+
+invoker.buildResult = failure
diff --git 
a/maven-enforcer-plugin/src/it/projects/ban-dynamic-versions-verbose/pom.xml 
b/maven-enforcer-plugin/src/it/projects/ban-dynamic-versions-verbose/pom.xml
new file mode 100644
index 0000000..dc4efcb
--- /dev/null
+++ b/maven-enforcer-plugin/src/it/projects/ban-dynamic-versions-verbose/pom.xml
@@ -0,0 +1,72 @@
+<?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>
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>org.apache.maven.its.enforcer</groupId>
+  <artifactId>ban-dynamic-versions-test</artifactId>
+  <version>1.0</version>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-enforcer-plugin</artifactId>
+        <version>@project.version@</version>
+        <executions>
+          <execution>
+            <id>test</id>
+            <goals>
+              <goal>enforce</goal>
+            </goals>
+            <configuration>
+              <rules>
+                <banDynamicVersions>
+                  <ignores>
+                    <ignore>*:menforcer494_project</ignore>
+                  </ignores>
+                  <verbose>true</verbose>
+                </banDynamicVersions>
+              </rules>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+  <dependencies>
+    <dependency>
+      <!-- banned SNAPSHOT, but ignored (though it's transitive dependency on 
menforcerxxx_dependency is not) -->
+      <groupId>org.apache.maven.plugins.enforcer.its</groupId>
+      <artifactId>menforcer494_project</artifactId>
+      <version>1.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <!-- this overrides the version in menforcerxxx_project, but we still 
want the failure from menforcerxxx_project to show -->
+      <groupId>org.apache.maven.plugins.enforcer.its</groupId>
+      <artifactId>menforcer494_dependency</artifactId>
+      <version>2.0</version>
+    </dependency>
+  </dependencies>
+
+</project>
diff --git 
a/maven-enforcer-plugin/src/it/projects/ban-dynamic-versions-verbose/verify.groovy
 
b/maven-enforcer-plugin/src/it/projects/ban-dynamic-versions-verbose/verify.groovy
new file mode 100644
index 0000000..32b1276
--- /dev/null
+++ 
b/maven-enforcer-plugin/src/it/projects/ban-dynamic-versions-verbose/verify.groovy
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+File buildLog = new File( basedir, 'build.log' )
+assert buildLog.text.contains( '[ERROR] Dependency 
org.apache.maven.plugins.enforcer.its:menforcer494_dependency:jar:1.0-SNAPSHOT 
(compile) via 
org.apache.maven.plugins.enforcer.its:menforcer494_project:jar:1.0-SNAPSHOT is 
referenced with a banned dynamic version 1.0-SNAPSHOT' )
+assert buildLog.text.contains( '[ERROR] Rule 0: 
org.apache.maven.enforcer.rules.dependency.BanDynamicVersions failed with 
message' )
+assert buildLog.text.contains( '[ERROR] Found 1 dependency with dynamic 
versions.' )

Reply via email to