How do I make Maven warn about choosing an arbitrary version?

In the Maven project, we have used a third party artifact (specifically spring-data-jpa 1.1.0.RELEASE) that depends on another artifact (spring-core) allowing any version in the range (to be exact: [3.0.7.RELEASE , 4.0.0.RELEASE), see its pom file ). We had no direct dependency on spring-core.

So one day our build picked 3.1.2.RELEASE, but when 3.2.0.RC1 was released, our build suddenly picked up that version.

However, we would like to have repetitive builds: when we deliver a patch for a year, we don't want to use an updated version of spring-core or any other indirect dependency without at least knowing it.

(I know that we can guide Maven to choose one specific version for spring-core, for example using <dependencyManagement>

, but I mean that there can be arbitrary options in indirect dependencies, and I would like Maven to tell us about that. without having to check it manually regularly.)

Question: How can we get Maven to warn us if it makes an arbitrary version selection for any indirect dependency?

+3


source to share


1 answer


As you've discovered, version ranges are evil.

The real problem is that version ranges are a siren that tempts people to think it's a good idea.

The version range should really be seen as a hint for the developer so that the developer can pick the version they want from the version set.

The mistake in Maven was to define version ranges within pom.xml

, in the first place, as it allows people to publish their artifacts with version ranges in them.

Once you have a dependency on an artifact that has transitive dependencies that use version ranges, there are really only two ways to solve the problem for your build (and one of them is a fancier version of the other).

  • Add your own transitive dependency, but with a pinned version instead of a range ... eg.

    <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-jpa</artifactId>
      <version>1.1.0.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-orm</artifactId>
      <version>3.1.2.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>3.1.2.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>3.1.2.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>3.1.2.RELEASE</version>
      <exclusions>
        <exclusion>
          <groupId>commons-logging</groupId>
          <artifactId>commons-logging</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    
          

    You don't need to list dependencies <optional>true</optional>

    as they are not transitive, and in a similar way, you don't need to list dependencies <scope>provided</scope>

    for the same reason.

  • As with the above, but being safer by adding exceptions to the dependency in the first place like

    <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-jpa</artifactId>
      <version>1.1.0.RELEASE</version>
      <exclusions>
        <exclusion>
          <groupId>org.springframework</groupId>
          <artifactId>spring-orm</artifactId>
        </exclusion>
        <exclusion>
          <groupId>org.springframework</groupId>
          <artifactId>spring-tx</artifactId>
        </exclusion>
        <exclusion>
          <groupId>org.springframework</groupId>
          <artifactId>spring-beans</artifactId>
        </exclusion>
        <exclusion>
          <groupId>org.springframework</groupId>
          <artifactId>spring-core</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-orm</artifactId>
      <version>3.1.2.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>3.1.2.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>3.1.2.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>3.1.2.RELEASE</version>
      <exclusions>
        <exclusion>
          <groupId>commons-logging</groupId>
          <artifactId>commons-logging</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    
          

Of the two, I prefer the last one as it at least gives people a clue as to why these dependencies are explicitly mentioned.



So, to get back to your original question, you need to customize this dependency tree when adding or updating dependencies in pom.xml

.

If spring-data-jpa:1.1.1.RELEASE

has a completely different transitive dependency tree with different coordinates, this is when you edit pom.xml

to update the version where you have to fix the transits as well.

There are currently no compliance rules, as far as I know, to back up what you require.

I would recommend writing an enforcement rule, which I would call the following: ensureTransitiveVersionRangesArePinned

This rule should do the following:

  • Scan the list of project dependencies
  • Compute the transitive dependencies provided by each project dependency.
  • If any of these transitive dependencies are version ranges, then
    • confirm that exists exclusion

      for this transitive dependency
    • Please confirm that there is a pinned version of the transitive dependency as a direct project dependency (it cannot fail if there is no pinned version, as you can add an equivalent artifact that is in another GAV, or you may not need the dependencies) ... anyway if the dependency is not being added back, chances are the unit tests should catch this by running the CNFE so that this check is probably not strictly required, but it should probably print a warning.

I can't remember if there are tools out there to check to <exclusions>

actually rule out any transitive dependencies, so you might need to investigate that.

+2


source







All Articles