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 <[email protected]> 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,)" ) ); + } + +}
