Short story
it is (still) "possible" to determine whether there are transitive dependencies with open version range:
...
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-assertions-generator</artifactId>
<version>2.1.0</version>
</dependency>
...
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<!-- version makes sense -->
<version>2.6</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
...
% mvn -X assembly:single -Dassembly.dryRun=true| grep 'setting version to'
[DEBUG] org.assertj:assertj-core:jar:2.9.1:compile
(setting version to: 2.9.1 from range: [2.1.0,2.99.0])
Long story
maven 3
had adopted Aether project and, unfortunately, there is no option to intercept or influence on dependency resolution process, basically "project object model" provides information about direct dependencies, but exhaustive information about transitive dependencies is hidden behind aether
, that is the reason why you didn't find desired functionality among maven plugins.
I succeeded to get some relevant information from maven-assembly-plugin
just because it's old versions are still compatible with modern maven, so, technically it is still possible to implement a plugin with required functionality or even take advantage of gmavenplus-plugin
and write groovy scriptlet:
<plugin>
<groupId>org.codehaus.gmavenplus</groupId>
<artifactId>gmavenplus-plugin</artifactId>
<version>1.13.1</version>
<executions>
<execution>
<phase>initialize</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<bindAllProjectProperties>true</bindAllProjectProperties>
<scripts>
<script><![CDATA[
def resolver = session.container.lookup(org.apache.maven.artifact.resolver.ArtifactResolver.class)
def artifacts = resolver.resolveTransitively(
project.dependencyArtifacts,
project.artifact,
project.managedVersionMap,
session.getLocalRepository(),
project.remoteArtifactRepositories,
null
).artifacts.findAll {
it.versionRange && it.versionRange.restrictions
&& !it.versionRange.recommendedVersion
&& (it.versionRange.restrictions.size() > 1
|| it.versionRange.restrictions[0].lowerBound
|| it.versionRange.restrictions[0].upperBound
)
}.each {
log.error("Found bad guy: $it -> $it.versionRange")
}
]]></script>
</scripts>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>3.0.9</version>
<type>pom</type>
<scope>runtime</scope>
</dependency>
</dependencies>
</plugin>
% mvn initialize
[INFO] Scanning for projects...
...
[INFO] Using plugin classloader, includes GMavenPlus and project classpath.
[INFO] Using Groovy 3.0.9 to perform execute.
Found bad guy: org.assertj:assertj-core:jar:2.9.1:compile -> [2.1.0,2.99.0]
UPD.
The idea of locking versions of all transitive dependencies in dependencyManagement
from my perspective seems to be wrong. At first glance it looks attractive to run mvn dependency:list
and put all it's output into dependencyManagement
, no doubts, at next mvn package
we will get the same artifact, but we also need to think about consequences of such "solution":
- output of
mvn dependency:list
is far from ideal: maven
tries to do it's best, but it relies on numbers mentioned in version
tag and knowns nothing about compatibility, bugs and security issues - we should not blindly trust that output, instead we always need to check everything manually, and the problem is maven
answers the question What will we get?
when the actual question is Why did we get that?
.
- By locking versions of transitive dependencies in
dependencyManagement
we are taking responsibility for the things which we do not actually manage, the question is: how we are going to update versions of dependencies if we have locked versions of their dependencies? Why do we think we know better what to do than the developer of dependency?