Dependency Convergence

This rule requires that dependency versions are the same everywhere in the tree. If a project has two dependencies, A and B, both depending on the same artifact, C, this rule will fail the build if A depends on one version of C and B depends on a different version of C.

Here is a concrete example.

A project containing these two dependencies does not converge because org.jdom:jdom:1.1.3 depends on Jaxen 1.1.3 and org.org.jenkins-ci.main:jenkins-core:2.492 depends on Jaxen 2.0.0:

  <dependencies>
    <dependency>
      <groupId>org.jdom</groupId>
      <artifactId>jdom</artifactId>
      <version>1.1.3</version>
    </dependency>
    <dependency>
      <groupId>org.org.jenkins-ci.main</groupId>
      <artifactId>jenkins-core</artifactId>
      <version>2.492</groupId>
    </dependency>
  </dependencies>  

This is not obvious from the dependencies themselves. However, if the dependencyConvergence rule is activated in the project, this message will be logged during compilation:

[ERROR]
Dependency convergence error for jaxen:jaxen. Paths to dependency are:
+-org.myorg:my-project:1.0.0-SNAPSHOT
  +-org.jdom:jdom:1.1.3
    +-jaxen:jaxen:1.1.3
and
+-org.myorg:my-project:1.0.0-SNAPSHOT
  +-org.org.jenkins-ci.main:jenkins-core:2.492
    +-jaxen:jaxen:2.0.0

However, if you exclude JDOM's dependency on Jaxen in your own pom, then only one version of Jaxen is added to the classpath and the rule passes:

    <dependency>
      <groupId>org.jdom</groupId>
      <artifactId>jdom</artifactId>
      <version>1.1.3</version>
      <exclusions>
        <exclusion>
          <groupId>jaxen</groupId>
          <artifactId>jaxen</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>org.org.jenkins-ci.main</groupId>
      <artifactId>jenkins-core</artifactId>
      <version>2.492</groupId>
    </dependency>

You can also use the dependencyManagement element or a "bill of materials" (BOM) to uniquely specify a single version for all transitive dependencies with the same group ID, artifact ID, and classifier. For example, this dependencyManagement element pins the project's Jaxen version to 2.0.0, regardless of which versions may appear in transitive dependencies:

  <dependencyManagement>
    <dependency>
      <groupId>jaxen</groupId>
      <artifactId>jaxen</artifactId>
      <version>2.0.0</groupId>
    </dependency>
  </dependencyManagement>

Here is how a project should be set up to use this rule:

<project>
  ...
  <build>
    <plugins>
      ...
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-enforcer-plugin</artifactId>
        <version>3.6.0</version>
        <executions>
          <execution>
            <id>enforce</id>
            <configuration>
              <rules>
                <dependencyConvergence/>
              </rules>
            </configuration>
            <goals>
              <goal>enforce</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
      ...
    </plugins>
  </build>
  ...
</project>

Timestamped Version

By default, the non-unique versions are matched, which means the X.Y-SNAPSHOT instead of the timestamped versions. If you want to use the unique versions of the dependencies, set the uniqueVersions property to true:

      <dependencyConvergence>
        <uniqueVersions>true</uniqueVersions> 
      </dependencyConvergence>

Filtering Dependency Errors

By default, all dependency convergence errors are reported, and any single error will fail the build. If you want to tune which dependency errors are reported and fail the build, you can add the following optional parameters:

  • includes - A list of artifacts for which dependency convergence should be enforced. Not specifying any includes is interpreted the same as including all artifacts.
  • excludes - A list of artifacts for which dependency convergence should not be enforced. These are exceptions to the includes.
  • excludedScopes - A list of scopes of artifacts for which dependency convergence should not be enforced. Not specifying any scopes is interpreted as having the following scopes excluded: provided, test.

The format for artifacts is groupId[:artifactId][:version][:type][:scope][:classifier] where artifactId, version, type, scope, and classifier are optional. Wildcards may replace an entire section or just parts of a section. This rule uses the Enforcer version range syntax to define allowed versions.

      <dependencyConvergence>
        <excludedScopes>
          <scope>compile</scope>
          <scope>runtime</scope>
        </excludedScopes>
        <includes>
          <include>org.slf4j</include>
          <include>org.apache.commons</include>
        </includes>
        <excludes>
          <exclude>org.slf4j:slf4j-jdk14</exclude>
          <exclude>org.apache.commons:*:[3.4]</exclude>
        </excludes>
      </dependencyConvergence>