2

I am wondering if there is a maven enforcer rule or something similar to check my project for any 'opened' (not fixed) version in project (transitive) dependencies.

I would like to archive a stable reproducible build with maven, but I cannot guarantee this if a dependency of mine e.g. declares an open-ended version range for one of its dependencies.

A new release of that transitive dependencies would change the output of my 'otherwise' untouched build.

I haven't found any property or enforcer rule which fits this requirement.

Does anybody know how such a requirement can be done with maven?

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
Matze
  • 93
  • 2
  • 10
  • 1
    I would try to replace such dependencies. You could overwrite the transitive versions via dependencymanagement in your own project... No there is no such enforcer rule... You could https://www.mojohaus.org/versions-maven-plugin/resolve-ranges-mojo.html but that will not help here... – khmarbaise Jul 20 '22 at 13:56
  • Maybe possible to create your own enforcer rules : https://maven.apache.org/enforcer/enforcer-api/writing-a-custom-rule.html ? – sbernard Jul 21 '22 at 12:40
  • This is my intention, to fix the version number in den dependency-management. But I have to recognize not fixed version numbers in all dependencies. That is where the enforcer rule comes in. – Matze Jul 22 '22 at 06:27

3 Answers3

0

Best bet would be to take the mvn dependency:list and fix all those versions in <dependencyManagement>

J Fabian Meier
  • 33,516
  • 10
  • 64
  • 142
  • That is not what TC requested, `mvn dependency:list` displays resolved version ranges. – Andrey B. Panfilov Jul 20 '22 at 15:06
  • The request was a stable, reproducible build, and that is what you get in this way. – J Fabian Meier Jul 20 '22 at 16:08
  • This is likely a first step to have a complete list with fixed versions of all (transitive) dependencies. But if I update a dependency, I have to check if new transitive dependencies were introduced. Therefore, it would be nice to have some kind of enforcer rule to tell me if there are any not dependency-management mentioned dependencies with unfixed versions. – Matze Jul 22 '22 at 06:24
0

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":

  1. 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?.
  2. 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?
Andrey B. Panfilov
  • 4,324
  • 2
  • 12
  • 18
0

No transitive dependency way

To have a reproducible build, you should probably fix version of all your direct and indirect dependencies in dependencyManagement.

This will allow :

To not forget flatten all your dependencies in dependencyManagement you can use banTransitiveDependencies rules from maven-enforcer-plugin.

If you have lot of dependency this could be painful to manage but maybe you can create a script to generate dependencyManagement section from mvn dependency:list

I created a new feature request for maven-dependency-plugin about this : https://issues.apache.org/jira/browse/MDEP-811

(See also : https://stackoverflow.com/a/35849405/5088764)

Fix range version only ?

Solution above works, but ideally we want to only fix version for :

  • convergence issue.
  • range version dependency.

You can add rules for dependency-convergence but AFAIK there is no kind of noRangeVersion rules.

I created a new feature request for maven-enforcer-plugin about this : https://issues.apache.org/jira/browse/MENFORCER-427

But waiting maybe this is possible to create your own rule : https://maven.apache.org/enforcer/enforcer-api/writing-a-custom-rule.html (or maybe somebody already do that ?)

dependency-lock-maven-plugin ?

I didn't test this but maybe dependency-lock-maven-plugin could help to solve this issue.

See : https://stackoverflow.com/a/54580971/5088764

sbernard
  • 444
  • 3
  • 16
  • I will have an eye on the issues you have opened. I think those enhancements will make sense for many others too. Furthermore, I will have a look at dependency-lock-maven-plugin and maybe give it a try. Thanks for your suggestions – Matze Jul 22 '22 at 07:01