Maven Releases on Steroids (2): Preparing the POMs

Update: I updated this article in 2013 as Maven Release Plugin: The Final Nail in the Coffin
In Part 1, I wrote about the problems with the Maven Release Plugin and what could be a better way to perform releases with Maven.

In this part, we will make the necessary adjustments to the POMs to make it all work.

Our goal is to have a single command to:
  • Build and Test the SCM revision currently checked out
  • Deploy the binary artifacts to the Artifact Repository
  • Tag the SCM revision with the version number of the artifacts

As we only want one commit instead of 3 per release, the version number can not be contained inside the POM anymore, as this would make it impossible to update it without updating the POM itself.
We have to balance this against the need for a default version number when performing local, non-release builds, as we don't want to pass in a version number for every build.

This means we have the following requirements:
  • Version number settable from outside for release builds
  • Default version number for local, non-release builds

This can be solved by introducing a VERSION_NUMBER property:

Parent POM:
<project ...>
...
<version>${VERSION_NUMBER}</version>
...
<properties>
...
<VERSION_NUMBER>1.0-SNAPSHOT</VERSION_NUMBER>
...
</properties>
...

Child POMs:
<project ...>
...
<parent>
...
<version>${VERSION_NUMBER}</version>
...
</parent>
...
<version>${VERSION_NUMBER}</version>
...

The VERSION_NUMBER property now determines the version of our project.

By default it is set to 1.0-SNAPSHOT for local, non-release builds.
It can also be set externally using -DVERSION_NUMBER=... for release builds.

And normally, this should be it!

Unfortunately, Maven has a minefield of bugs we need to work around. What it basically boils down to, is that Maven neglects to replace variables in installed (local repo) and deployed (remote repo) POMs. This means our POMs get deployed with <version>${VERSION_NUMBER}</version>, which causes problems at runtime.

We will need to overwrite the broken POMs with a new version that has its variables replaced with their values.

The first thing we need to add (only to our parent POM) is a way to distinguisch between snapshot and release builds:
<project ...>
...
<properties>
...
<releaseRepoUrl>http://my.release.repo</releaseRepoUrl>
<snapshotRepoUrl>http://my.snapshot.repo</snapshotRepoUrl>
<deployRepoUrl>${releaseRepoUrl}</deployRepoUrl>
<isRelease>true</isRelease>
...
</properties>
...
<profiles>
<profile>
<id>snapshot-deploy-url-override</id>
<activation>
<property>
<name>!VERSION_NUMBER</name>
</property>
</activation>
<properties>
<deployRepoUrl>${snapshotRepoUrl}</deployRepoUrl>
<isRelease>false</isRelease>
</properties>
</profile>
</profiles>
...
<distributionManagement>
<snapshotRepository>
<id>snapshots-repo</id>
<name>Snapshots Repo</name>
<url>${snapshotRepoUrl}</url>
</snapshotRepository>
<repository>
<id>releases-repo</id>
<name>Releases Repo</name>
<url>${releaseRepoUrl}</url>
</repository>
</distributionManagement>
...

And now comes the real juicy part (only necessary in the parent POM):
  • Fixing the POM by substituting the variables
  • Overwriting the existing POM in the local repo
  • Overwriting the existing POM in the remote repo
<project ...>
...
<build>
...
<plugins>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>2.4.3</version>
<executions>
<execution>
<id>replace-pom-placeholder</id>
<phase>package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<resources>
<resource>
<directory>${basedir}</directory>
<includes>
<include>pom.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
<outputDirectory>${project.build.directory}/pom-install-deploy-fix</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>

<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.3.1</version>
<executions>
<execution>
<id>overwrite-pom</id>
<phase>install</phase>
<goals>
<goal>install-file</goal>
</goals>
<configuration>
<packaging>pom</packaging>
<file>target/pom-install-deploy-fix/pom.xml</file>
<pomFile>target/pom-install-deploy-fix/pom.xml</pomFile>
<version>${project.version}</version>
</configuration>
</execution>
</executions>
</plugin>

<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.5</version>
<executions>
<execution>
<id>overwrite-pom</id>
<phase>deploy</phase>
<goals>
<goal>deploy-file</goal>
</goals>
<configuration>
<packaging>pom</packaging>
<file>target/pom-install-deploy-fix/pom.xml</file>
<pomFile>target/pom-install-deploy-fix/pom.xml</pomFile>
<url>${deployRepoUrl}</url>
<version>${project.version}</version>
<updateReleaseInfo>${isRelease}</updateReleaseInfo>
<uniqueVersion>false</uniqueVersion>
</configuration>
</execution>
</executions>
</plugin>
...
</plugins>
...
</build>
...

If you also want to deploy source artifacts, check out the Maven Source Plugin.

So far, so good: we now have taken care of the deployment in the Artifact Repository!



Let's add the final bit to enable SCM tagging...

First add the scm section to your parent POM:
<project ...>
...
<scm>
<connection>scm:my-provider:my-read-url</connection>
<developerConnection>scm:my-provider:my-read-write-url</developerConnection>
</scm>
...

And now add the scm plugin to the plugins section of the parent POM:
<plugin>
<artifactId>maven-scm-plugin</artifactId>
<version>1.4</version>
<configuration>
<tag>${project.artifactId}-${VERSION_NUMBER}</tag>
</configuration>
</plugin>

Done!



We can now
  • publish new snapshots using
    mvn clean deploy
  • release and tag new versions using
    mvn clean deploy scm:tag -DVERSION_NUMBER=1.2.3

In Part 3, we conclude our adventure by choosing a version number strategy and looking at Jenkins integration.

 


Axel

About Axel Fontaine

Axel Fontaine is the founder and CEO of Boxfuse the easiest way to deploy JVM and Node.js applications to AWS.

Axel is also the creator and project lead of Flyway, the open-source tool that makes database migration easy.

He is a Continuous Delivery and Immutable Infrastructure expert, a Java Champion, a JavaOne Rockstar and a regular speaker at many large international conferences including JavaOne, Devoxx, Jfokus, JavaZone, QCon, JAX, ...

You can follow him on Twitter at @axelfontaine

 

Architecting for Continuous Delivery and Zero Downtime

Two day intensive on-site training with Axel Fontaine

Upcoming dates

Iasi, Romania (May 10-11, 2017)
Oslo, Norway (Oct 16-17, 2017)

« Maven Releases on Steroids: Adios Release Plugin!
Maven Releases on Steroids (3): Rounding it up with Jenkins »
Browse complete blog archive
Subscribe to the feed