Thursday, December 10, 2009

Robustly enforcing a minimum Maven version for your project

Maven user's quickly realize that it is a good idea to force a fixed (or at least minimum) version of Maven to be used to build their Maven project. Initially one may gravitate towards using the following pom snippet:

<prerequisites>
  <maven>2.0.10</maven>
</prerequisites>

Looks good, except there are a couple gotchas
1. Typos. If you make one then you will get no warning or complaints from maven. You might as well have this.

<prerequisites>
  <maven>Maven treats this string as older than any Maven version you might use</maven>
</prerequisites>

A bug is even more likely if you do this:

<prerequisites>
  <maven>${maven.min-version}</maven>
</prerequisites>

Let's say that property can't be resolved for whatever reason - typos? - again Maven will treat that 'version' as older than any valid Maven version you could be executing the POM with. This is actually not a bug but by design since Maven does not force project's ( in this case Maven itself) to a strict version number specification.

2. Another problem is that section is not even used at all by the next major version of Maven 3.x as of today. So even if you did have it right, it is not future proof.

3. Finally maven considers versions like "1.0-alpha-2" to be newer than "1.0-alpha-15" as of Maven 2.x series at least. A bug indeed in the version compare logic - the failing test cases being commented out in the source code. Although Maven 3.x ignores prerequisites in pom, at least the version comparison noted here is fixed.

4. Lastly the <maven> element does not handle version ranges. So when it sees [2.0.6,2.0.8], we are just back to problem 1.

So what to do?

Use the maven-enforcer-plugin instead. The benefits are:

1. Many different rules supported, not just Maven version.
2. Write your own rules.
3. Version ranges are supported.
4. Version comparisons are more robust.
5. Bind the version check to Maven lifecycle phases or profiles.

Below is an example configuration for the plugin to require a minimum Maven version 2.0.9 and above. The unique thing about this example is that there are three executions, one for each pre-defined Maven build lifecycle. Usually you just see the first execution only, which is bound to the validate phase of the default lifecycle. However if one were to do
mvn clean
or
mvn site
then the version validation would not be enforced since those goals do not have a validate phase. Having all three executions covers all the bases.

The other best practice is define a property to hold the Maven versions so that it can be changed in one place only or manipulated on the cmd line.

<properties>
  <maven.min-version>2.0.9</maven.min-version>
</properties>

<plugin>
  <artifactId>maven-enforcer-plugin</artifactId>
  <executions>
    <execution>
      <id>enforce-default</id>
      <goals>
        <goal>enforce</goal>
      </goals>
      <configuration>
        <rules>
          <requireMavenVersion>
            <version>[${maven.min-version},)</version>
          </requireMavenVersion>
        </rules>
      </configuration>
    </execution>
    <execution>
      <id>enforce-clean</id>
      <phase>pre-clean</phase>
      <goals>
        <goal>enforce</goal>
      </goals>
      <configuration>
        <rules>
          <requireMavenVersion>
            <version>[${maven.min-version},)</version>
          </requireMavenVersion>
        </rules>
      </configuration>
    </execution>
    <execution>
      <id>enforce-site</id>
      <phase>pre-site</phase>
      <goals>
        <goal>enforce</goal>
      </goals>
      <configuration>
        <rules>
          <requireMavenVersion>
            <version>[${maven.min-version},)</version>
          </requireMavenVersion>
        </rules>
      </configuration>
    </execution>
  </executions>
</plugin>

2 comments:

Anonymous said...

Looks like prerequisites is not a good choice. Just a sidenote. Maven folks seem to propose the tag. If I run

mvn versions:display-plugin-updates

i am adviced to use prerequisites with maven version set to 2.2.1.

Stephen Hartley said...

This is really useful stuff, thanks! Only update I can suggest is to point 2 of the 'gotchas'. Maven 3.0.4 does now appear to use

<prerequisites>
  <maven>${maven.min-version}</maven>
</prerequisites>

however on testing it looks like it also inherits the limitations of the other 'gotchas'.

So using maven 3.0.4 and <prerequisites> would fix the previous poster's issue with mvn versions:display-plugin-updates, although it's clearly still not as bullet-proof as Peter's original post...

Post a Comment