You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by rf...@apache.org on 2021/03/21 13:14:49 UTC

[maven] 01/01: [MNG-7122] Require specific Maven compatibility version for plugins

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

rfscholte pushed a commit to branch MNG-7122
in repository https://gitbox.apache.org/repos/asf/maven.git

commit 313f847b9bbed3be3d6178f7893bbf6fb48b7c77
Author: rfscholte <rf...@apache.org>
AuthorDate: Sun Mar 21 14:14:11 2021 +0100

    [MNG-7122] Require specific Maven compatibility version for plugins
---
 .../DefaultPluginDependenciesResolver.java         | 26 ++++++-
 .../internal/IncompatibleDependencyException.java  | 37 ++++++++++
 .../plugin/internal/MavenCompatibilityChecker.java | 80 ++++++++++++++++++++
 .../internal/MavenCompatibilityCheckerTest.java    | 85 ++++++++++++++++++++++
 4 files changed, 227 insertions(+), 1 deletion(-)

diff --git a/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultPluginDependenciesResolver.java b/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultPluginDependenciesResolver.java
index 709bd72..cc90837 100644
--- a/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultPluginDependenciesResolver.java
+++ b/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultPluginDependenciesResolver.java
@@ -60,7 +60,11 @@ import org.eclipse.aether.util.filter.AndDependencyFilter;
 import org.eclipse.aether.util.filter.ScopeDependencyFilter;
 import org.eclipse.aether.util.graph.manager.DependencyManagerUtils;
 import org.eclipse.aether.util.graph.selector.AndDependencySelector;
+import org.eclipse.aether.util.graph.transformer.ChainedDependencyGraphTransformer;
 import org.eclipse.aether.util.repository.SimpleArtifactDescriptorPolicy;
+import org.eclipse.aether.util.version.GenericVersionScheme;
+import org.eclipse.aether.version.InvalidVersionSpecificationException;
+import org.eclipse.aether.version.VersionRange;
 
 /**
  * Assists in resolving the dependencies of a plugin. <strong>Warning:</strong> This is an internal utility class that
@@ -75,6 +79,7 @@ import org.eclipse.aether.util.repository.SimpleArtifactDescriptorPolicy;
 public class DefaultPluginDependenciesResolver
     implements PluginDependenciesResolver
 {
+    private static final String MAVENCORE_COMPATIBILITY_VERSIONS = "(,)";
 
     private static final String REPOSITORY_CONTEXT = "plugin";
 
@@ -83,6 +88,23 @@ public class DefaultPluginDependenciesResolver
 
     @Inject
     private RepositorySystem repoSystem;
+    
+    private final VersionRange mavenCompatibilityVersionRange;
+    
+    public DefaultPluginDependenciesResolver() 
+    {
+        try
+        {
+            // o.a.m.plugins:maven-compiler-plugin:jar:3.1 depends on o.a.m:maven-toolchain:jar:1.0
+            // maven-its:mng-4666 depends on o.a.m:maven-model:0.1-stub
+            this.mavenCompatibilityVersionRange =
+                new GenericVersionScheme().parseVersionRange( MAVENCORE_COMPATIBILITY_VERSIONS );
+        }
+        catch ( InvalidVersionSpecificationException e )
+        {
+            throw new RuntimeException( e );
+        }
+    }
 
     private Artifact toArtifact( Plugin plugin, RepositorySystemSession session )
     {
@@ -178,7 +200,9 @@ public class DefaultPluginDependenciesResolver
 
             DefaultRepositorySystemSession pluginSession = new DefaultRepositorySystemSession( session );
             pluginSession.setDependencySelector( selector );
-            pluginSession.setDependencyGraphTransformer( session.getDependencyGraphTransformer() );
+            pluginSession.setDependencyGraphTransformer( ChainedDependencyGraphTransformer.newInstance( 
+                                                session.getDependencyGraphTransformer(),
+                                                new MavenCompatibilityChecker( mavenCompatibilityVersionRange ) ) );
 
             CollectRequest request = new CollectRequest();
             request.setRequestContext( REPOSITORY_CONTEXT );
diff --git a/maven-core/src/main/java/org/apache/maven/plugin/internal/IncompatibleDependencyException.java b/maven-core/src/main/java/org/apache/maven/plugin/internal/IncompatibleDependencyException.java
new file mode 100644
index 0000000..6a1d074
--- /dev/null
+++ b/maven-core/src/main/java/org/apache/maven/plugin/internal/IncompatibleDependencyException.java
@@ -0,0 +1,37 @@
+package org.apache.maven.plugin.internal;
+
+/*
+ * 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.
+ */
+
+import org.eclipse.aether.graph.DependencyNode;
+
+class IncompatibleDependencyException extends Exception
+{
+    private final DependencyNode node;
+    
+    IncompatibleDependencyException( DependencyNode node )
+    {
+        this.node = node;
+    }
+    
+    DependencyNode getNode()
+    {
+        return node;
+    }
+}
diff --git a/maven-core/src/main/java/org/apache/maven/plugin/internal/MavenCompatibilityChecker.java b/maven-core/src/main/java/org/apache/maven/plugin/internal/MavenCompatibilityChecker.java
new file mode 100644
index 0000000..44b3c5a
--- /dev/null
+++ b/maven-core/src/main/java/org/apache/maven/plugin/internal/MavenCompatibilityChecker.java
@@ -0,0 +1,80 @@
+package org.apache.maven.plugin.internal;
+
+/*
+ * 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.
+ */
+
+import org.eclipse.aether.RepositoryException;
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.collection.DependencyGraphTransformationContext;
+import org.eclipse.aether.collection.DependencyGraphTransformer;
+import org.eclipse.aether.graph.DependencyNode;
+import org.eclipse.aether.version.VersionRange;
+
+/**
+ * Ensure that Maven core dependencies fit within the versionRange
+ * 
+ * @since 4.0.0
+ */
+class MavenCompatibilityChecker implements DependencyGraphTransformer
+{
+    private final VersionRange versionRange;
+    
+    MavenCompatibilityChecker( VersionRange versionRange )
+    {
+        this.versionRange = versionRange;
+    }
+
+    @Override
+    public DependencyNode transformGraph( DependencyNode node, DependencyGraphTransformationContext context )
+        throws RepositoryException
+    {
+        try 
+        {
+            validateNode( node );
+        }
+        catch ( IncompatibleDependencyException e )
+        {
+            throw new RepositoryException( 
+                       String.format( "%s depends on %s, which does not match the required Maven versionrange of %s",
+                                      node.getArtifact(), e.getNode().getArtifact(), versionRange ) );
+        }
+
+        return node;
+    }
+    
+    private void validateNode( DependencyNode node )
+        throws IncompatibleDependencyException
+    {
+        if ( isCoreArtifact( node.getArtifact() ) && !versionRange.containsVersion( node.getVersion() ) ) 
+        {
+            throw new IncompatibleDependencyException( node );
+        }
+        
+        for ( DependencyNode child : node.getChildren() )
+        {
+            validateNode( child );
+        }
+    }
+    
+    private static boolean isCoreArtifact( Artifact artifact )
+    {
+        return artifact.getArtifactId().startsWith( "maven-" )
+            && artifact.getGroupId().equals( "org.apache.maven" );
+    }
+}
diff --git a/maven-core/src/test/java/org/apache/maven/plugin/internal/MavenCompatibilityCheckerTest.java b/maven-core/src/test/java/org/apache/maven/plugin/internal/MavenCompatibilityCheckerTest.java
new file mode 100644
index 0000000..5b4bc29
--- /dev/null
+++ b/maven-core/src/test/java/org/apache/maven/plugin/internal/MavenCompatibilityCheckerTest.java
@@ -0,0 +1,85 @@
+package org.apache.maven.plugin.internal;
+
+/*
+ * 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.
+ */
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.util.Collections;
+
+import org.eclipse.aether.RepositoryException;
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.artifact.DefaultArtifact;
+import org.eclipse.aether.graph.DefaultDependencyNode;
+import org.eclipse.aether.graph.Dependency;
+import org.eclipse.aether.util.version.GenericVersionScheme;
+import org.eclipse.aether.version.VersionScheme;
+import org.junit.jupiter.api.Test;
+
+class MavenCompatibilityCheckerTest
+{
+    private final VersionScheme versionScheme = new GenericVersionScheme();
+
+    @Test
+    void compatible() throws Exception
+    {
+        MavenCompatibilityChecker checker =
+            new MavenCompatibilityChecker( versionScheme.parseVersionRange( "[3.0,)" ) );
+        
+        Artifact pluginArtifact = new DefaultArtifact( "o.a.m.p:plugin:1.0" );
+        Dependency plugin = new Dependency( pluginArtifact, "compile" );
+        DefaultDependencyNode node = new DefaultDependencyNode( plugin );
+        node.setVersion( versionScheme.parseVersion( "1.0" ) );
+
+        Artifact coreArtifact = new DefaultArtifact( "org.apache.maven:maven-core:3.0" );
+        Dependency core = new Dependency( coreArtifact, "compile" );
+        DefaultDependencyNode coreNode = new DefaultDependencyNode( core );
+        coreNode.setVersion( versionScheme.parseVersion( "3.0" ) );
+        
+        node.setChildren(Collections.singletonList( coreNode ) );
+        
+        assertThat( checker.transformGraph( node, null ), is(node));
+    }
+
+    @Test
+    void incompatible() throws Exception
+    {
+        MavenCompatibilityChecker checker =
+                        new MavenCompatibilityChecker( versionScheme.parseVersionRange( "[3.0,)" ) );
+                    
+        Artifact pluginArtifact = new DefaultArtifact( "o.a.m.p:plugin:1.0" );
+        Dependency plugin = new Dependency( pluginArtifact, "compile" );
+        DefaultDependencyNode node = new DefaultDependencyNode( plugin );
+        node.setVersion( versionScheme.parseVersion( "1.0" ) );
+
+        Artifact coreArtifact = new DefaultArtifact( "org.apache.maven:maven-core:2.0" );
+        Dependency core = new Dependency( coreArtifact, "compile" );
+        DefaultDependencyNode coreNode = new DefaultDependencyNode( core );
+        coreNode.setVersion( versionScheme.parseVersion( "2.0" ) );
+        
+        node.setChildren(Collections.singletonList( coreNode ) );
+        
+        RepositoryException exception = assertThrows( RepositoryException.class, () -> checker.transformGraph( node, null ) );
+        assertThat( exception.getMessage(), is( "o.a.m.p:plugin:jar:1.0 depends on org.apache.maven:maven-core:jar:2.0, "
+            + "which does not match the required Maven versionrange of [3.0,)" ) );
+    }
+
+}