Maven Release Plugin: The Final Nail in the Coffin

I updated this article in 2016 as Maven Release Plugin: Dead and Buried

Two and half years ago, I published a very popular article series titled 'Maven Releases on Steroids'. 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.

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

Fast forward to 2013

The original article was written in January 2011. Two and a half years have passed, and boy did thing change in the SCM space! In 2011, CVS was dead and burried, Subversion was well-established and both Git and Mercurial were on the rise and battling it out to be the Next Big Thing™.

And then one thing happened: GitHub's popularity exploded. It triggered a mass-exodus from Sourceforge, Java.net and Google Code. The SCM war had been decided. Git was the new King of the Hill.

Git then trickled down from its open-source roots into the deep dark SCM alleys of the enterprise, effectively relegating Subversion to legacy street.

This change brings a number of advantages, of which one is of particular interest to us today: massively improved initial checkout performance. git clone is orders of magnitude faster than svn checkout !

The Releases on Steroids process comes from a time where an initial checkout was slow. To keep performance in check the practice was to update the working copy with the latest changes instead of checking it out fresh every time. This meant that for the update to succeed the working copy could not contain local changes, as these would cause conflicts.

The Releases on Steroids process went through great lengths (and worked around many Maven bugs) to ensure this.

But now with the performance of Git, things have changed. And in change lies opportunity. So let's revisit Releases on Steroids and bring it into 2013's brave new world!

Revisiting the Release Process

Even though technology has evolved, the purpose of a release hasn't. What we want is 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

Pretty simple, isn't it?

Releases with Maven and Git

Let's see what this translates to in a Git and Maven world:

Checking the software out: git clone Giving it a version: mvn versions:set Building, testing, packaging and deploying it to an artifact repository: mvn deploy Tagging this state in SCM: mvn scm:tag

But first, the POM needs some preparation.

Having the project version come from outside means we can use a simple generic snapshot version for the code checking into SCM. Our pom.xml then looks like this:

<project ...> ... <version>0-SNAPSHOT</version> ... </project>

We can now add our project's Git and artifact repository urls, as well as some basic plugin configuration:

<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> <groupId>org.codehaus.mojo</groupId> <artifactId>versions-maven-plugin</artifactId> <version>2.1</version> </plugin> <plugin> <artifactId>maven-scm-plugin</artifactId> <version>1.8.1</version> <configuration> <tag>${project.artifactId}-${project.version}</tag> </configuration> </plugin> </plugins> </build>

And that's it! Nothing more is required!

Now let's take this 11 and integrate it in a CI environment. Jenkins is the absolute market leader, so that's what we'll use.

Jenkins integration

Prerequisites: Jenkins with a JDK and Maven configured, and both the Git and the Workspace Cleanup plugins installed.

We're going to start by creating a new Maven job and making sure we have a fresh workspace for every build:

We can now point the job to our Git repository:

The next step is to set the version upon checkout. There are two main strategies we can apply:

Create a parametrized build and pass the version in

Let Jenkins assign the version

In this example we're going to use the latter. A good version number is both unique and chronological. We're going to use the Jenkins BUILD_NUMBER as it fulfills both these criteria wonderfully.

So let's set the version in a pre-build step:

We are now ready to build, test, package, deploy (to the artifact repository) and tag (the Git repository):

All that is left now is deciding between

Triggering the release build manually (the classic way)

Polling SCM to release after every commit (the Continuous Delivery way)

Happy releasing with Jenkins!

Conclusion

In this article we've followed the footsteps of the Releases on Steroids articles and taken the concepts to their natural conclusion: the simplest and most effective way to create releases on projects using Maven and Git.

Along the way, we've kept a strong focus on keeping as much knowledge as possible inside the POM. This keeps the release process concise and minimizes dependencies on specific environments or CI servers.

So come with me and wave the Maven Release Plugin goodbye. This was the final nail in its coffin. Good riddance.

P.S.: I've published a companion repository on GitHub to get you started easily: http://github.com/axelfontaine/final-nail