Maven Release Plugin: Dead and Buried

This is the second major update to a very popular article series titled 'Maven Releases on Steroids' which I originally published in 2011. The goal of the series was to provide a simple and efficient way to produce releases on a Maven project. Not only that, but it did so in a way that is compatible with Continuous Delivery.

The original series was written for Subversion. By 2013 Git had taken over the market and so I published an update called Maven Release Plugin: The Final Nail in the Coffin that integrated both Git itself and the improvements it provided into the workflow.

With its 3.2.1 release, Maven finally introduced support for Continuous Delivery friendly versions. While not perfect, even this minimal implementation warranted this long overdue update to my previous articles.

The improvements so far

Maven has traditionally required the version number to be present in the project descriptor (pom.xml). This concept permeates through all aspects of the Maven ecosystem, culminating with the monstrosity commonly referred to as the Release Plugin.

So how big exactly was the improvement of Releases On Steroids and Final Nail over the Release Plugin? See for yourself:

Releases on Steroids Release Plugin Clean/Compile/Test cycle 1 3 POM transformations 0 2 Commits 0 2 SCM revisons 1 3

The reduction in the number of Clean/Compile/Test cycles alone results in a 3x build time improvement! And this doesn't even take into consideration the other adavantages like increased reliability and recoverability. Quite the win indeed!

Turning it up to 11

Both original article series and the Final Nail update required the use of the Versions Maven Plugin to dynamically update the generic version contained in the POM to the new version assigned dynamically for the current build. This resulted in the need to run Maven twice: once for updating the version and the again with the updated version.

With the Maven 3.2.1 release and the newly introduced support for Continuous Delivery friendly versions we can now finally eliminate this extra step and get down to a single execution.

At its core the purpose of producing a release is nothing more than being able to link a version of the software as deployed onto a machine back to the matching revision of the source code in SCM. This is essential to reproduce bugs and revert to previous versions deterministically if required.

To accomplish this we have to go through a number of steps. At a minimum these include:

checking out the software as it is

giving it a version so it can be uniquely identified

building, testing and packaging it

deploying it to an artifact repository where it can then be picked for actual roll out on target machines

tagging this state in SCM so it can be associated with the matching artifact

Surprisingly simple, isn't it?

Eliminating the Versions plugin

Now let's revisit the ideas presented in Final Nail and see how they can be improved with Maven's new Continuous Delivery friendly versions support.

For this we need to ensure our POM accepts versions set externally. As this will usually only be the case on the CI machine (the one and only machine you should ever build releases on), we also need a sane default for local development to avoid having to pass a dummy version for every single local build.

Taking that into account our pom.xml now looks like this:

<project ...> ... <version>${revision}</version> <properties> <!-- Sane default when no revision property is passed in from the commandline --> <revision>0-SNAPSHOT</revision> </properties> <scm> <connection>scm:git:<<your-git-repo-url>></connection> </scm> <distributionManagement> <repository> <id>artifact-repository</id> <url><<your-artifact-repo-url>></url> </repository> </distributionManagement> <build> <plugins> <plugin> <artifactId>maven-scm-plugin</artifactId> <version>1.9.4</version> <configuration> <tag>${project.artifactId}-${project.version}</tag> </configuration> </plugin> </plugins> </build> ... </project>

And that's it! You can now produce releases on your CI server very easily by invoking:

mvn deploy scm:tag -Drevision=$BUILD_NUMBER

Where BUILD_NUMBER is the environment variable provided by your CI server to identify the current build number for the project.

For services and deliverables consumed by other teams and external parties you can also easily combine this technique with sematic versioning by prefixing the version tag in your POM with the correct semantic version. You can then automatically produce releases internally and manually updated this semantic version before each external delivery.

Example:

<version>1.2.34.${revision}</version>

Conclusion

In this article we have taken what was already a gigantic improvement over the Maven Release Plugin and simplified it even further by finally getting it down to a single Maven execution:

This article Releases on Steroids Release Plugin Clean/Compile/Test cycle 1 1 3 POM transformations 0 0 2 Commits 0 0 2 SCM revisons 1 1 3 Maven Executions 1 2 2

This is by far the simplest and most effective way to produce releases on projects using Maven and Continuous Delivery.

Along the way, we strongly focused on keeping as much knowledge as possible inside the POM. This keeps the release process concise, provides us with sane defaults and minimizes dependencies on specific environments or CI servers.

So do yourself a favor and join me in finally burying the Release Plugin for good. It's been dead for a while now anyway.