Maven build pipeline without SNAPSHOTs

Introduction

After reading Continuous Delivery (CD) a while back, I began to think about how Maven could be used to support a Continuous Delivery deployment pipeline. One of the first things that gets in the way of CD principles is the concept of SNAPSHOTs in Maven. In the pipeline, every commit build needs to produce final artifacts that could be deployed into production if all tests pass, QA approves and the application features are complete enough for use by the business.

So I posted a question and the solutions started to roll in. Eventually I got around to trying out a combination of the recommended approaches and ran into some issues. This article describes how I went back to the beginning and came up with what I think is a better way to eliminate SNAPSHOTs from Maven for a deployment pipeline.

Maven SNAPSHOTs are not suitable for deployment into production and the Maven release plugin really doesn’t fit into a deployment pipeline – I won’t be rehashing the reasons why this is so. This has already been very well covered by others including James Betteley, Steve Smith and Axel Fontaine.

First Attempt

My initial effort involved combining James Betteley’s version expression approach with Axel Fontaine’s pom filtering. The version expression goes into the version element of the pom.xml and then the properties involved are set within the continuous integration server job execution. The relevant section of the pom.xml file looks something like the excerpt shown below:

<modelVersion>4.0.0</modelVersion>
<groupId>com.acme</groupId>
<artifactId>foo-bar</artifactId>
<packaging>jar</packaging>
<version>${main.version}-${build.number}</version>
<description>Description about this application</description>
<properties>
<svn.revision>LOCAL</svn.revision>
<build.number>${svn.revision}</build.number>
<main.version>5.0.2</main.version>
</properties>
. . .

The default value of the version expression in the example above is 5.0.2-LOCAL. This is the version a developer would see when building on a workstation. When the developer commits to Subversion and a job is triggered on a continuous integration server (e.g., Jenkins) the svn.revision property will be assigned from the environment. For Jenkins this would utilize SVN_REVISION which is one of the environmental variables available in a job.

The pom filtering technique involves the use of resource filtering to create a copy of the pom.xml in which all of the variables have been substituted with actual values so that the pom.xml that is deployed to the Maven repository will contain the actual version instead of the version expression. This pom filtering technique is covered in Axel Fontaine’s article.

So what’s not to like?

At this point I had Maven build jobs running in Jenkins that were deploying artifacts to my Nexus Maven repository with versions made up from main.version and build.number, so why wasn’t I all done at this point?

It turns out that this set of techniques lead to some issues:

  • Using a version expression in Maven 3 results in a warning. Although it’s just a warning and lots of input has been made to liberalize/improve Maven’s ability to deal with version expressions, the issue still remains unfixed.
  • While Nexus properly tracks the release version numbers in the releases repository and the uploaded POMs have the correct version, the filtering technique involves two uploads of the pom.xml files: one which contains the version expression and a second which provides the filtered pom.xml file. This won’t work unless I let down Nexus’s guard against overwriting the pom.xml files for a deploy.
  • The filtered pom.xml file has ALL of its properties filtered which includes properties such as target directory location which looks a bit chunky in the deployed pom.xml.
  • For a multimodule Maven project transitive dependencies for modules do not get resolved so I had to redundantly specify each module’s coordinates.
  • The pom.xml that gets written into the jar or war packaged Maven artifact still contains the version expression. Yes, I know that might seem a bit picky, but sometimes QA people get worked up if they see that sort of thing – I’m just sayin’.

What to do about it?

In other encounters with Maven I generally have found that it’s better to “fight fire with fire”, so inspired by this idea, I decided that I would try to use the Maven versions plugin to set the version in the POMs as part of the Jenkins job execution in a pre-build step. Of course, this needs to happen before Maven needs to use the version in a regular build, and after the Subversion checkout of the project source. In addition, I want to preserve the version in the pom.xml as stored in Subversion in the general form (i.e., <version>5.0.2-LOCAL</version> ) and not end up with Subversion conflicts on subsequent checkouts in the Jenkins job.

With this as a starting point, I set out to come up with a complete solution. In the next post, we’ll cover how this works in detail.

Getting started

This blog will be a compilation of technical tidbits that I have collected and found to be interesting enough to share.

Stay tuned for the first article about how to make no-SNAPSHOT Maven builds play nicely in a Continuous Delivery-style build pipeline…

Design a site like this with WordPress.com
Get started