Maven releases with Git

I’ve started to put various snippets of code up into GitHub, partly because they may be useful to other people, partly so that they are more accessible when I do not have my personal laptop with me. Yes, Virginia, I could put it all on a USB stick (and I probably will), but that poses another problem of keeping that content up to date. And I’m not keen on sticking my stick into random and unpredictably unhygienic places.

The model that I’m looking at is chosen because I’m comfortable and familiar with it, not necessarily because it’s the ‘best’ nor bleeding edge:

  • Version control is managed with Git, using the general semantics of pushing clean code to GitHub and in-progress code locally;
  • Code is modified through the Eclipse IDE;
  • Dependency management is done with Maven;
  • Builds, tests and code compliance checks are run via Maven – on-the-fly through the Eclipse IDE while code is fluid, from the command line when it washes up on islands of stability;
  • Maven collaborates with Git to prepare and tag a version for release;
  • Maven pushes to my personal Artifactory instance.

As I’ve written about before, in this world I’m keen on placing declarative road blocks on the build road to ensure that basic CheckStyle and code coverage expectations are met. I firmly believe that these kind of checks are equivalent to spelling and grammar checkers for written natural language. They do not guarantee good or correct code, but they do assist in picking up silly mistakes and promoting consistent style.

One of the things I dislike about Maven is the poor and hard-to-find documentation around plugins. Even the ‘official’ core plugins are poorly documented, with a strong emphasis on the ‘what’ instead of the ‘how’ and ‘why’. Please, when writing documentation, don’t simply catalogue your API or interface: the result is like giving someone a dictionary when they want to learn to speak English.

As a result of the poor documentation, a lot of the time we need to rely on samizdat and hope to find a blog or similar written by somebody who has already figured out the documentation. Case in point here is this rather nice piece by Axel Fontaine on how to integrate Maven and Git. There are still a few missing links in that document, so let me try to fill in the blanks.

There are three key bits that need to go into your pom.xml to get this working. First you need to include a <scm/> section, which I like to put up at the top of the pom.xml along with the other general project meta-data:

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

<modelVersion>4.0.0</modelVersion>

<groupId>net.parttimepolymath.cache</groupId>
<artifactId>SimpleLRU</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>

<name>SimpleLRU</name>
<description>Simple least-recently-used cache.</description>
<url>https://github.com/TheBellman/simplelru</url>

<scm>
  <connection>scm:git:git@github.com:TheBellman/simplelru.git</connection>
</scm>

I can never remember this stuff, and rely on cloning it from project to project. It is rather calming, like a religious ceremony or meditation. The important part here is the <scm/> tag, which tells Maven where it can pull and push code from and to. You also need to tell Maven where it will store released artifacts, using the rather poorly named <distributionManagement/> segment. I usually put this just below the <scm/> tag:

<distributionManagement>
  <repository>
    <id>central</id>
    <name>ip-172-31-6-67-releases</name>
    <url>http://54.209.160.169:8081/artifactory/libs-release-local</url>
  </repository>

  <snapshotRepository>
    <id>snapshots</id>
    <name>ip-172-31-6-67-snapshots</name>
    <url>http://54.209.160.169:8081/artifactory/libs-snapshot-local</url>
  </snapshotRepository>
</distributionManagement>

By including a <snapshotRepository/> it is possible to share snapshot or beta builds using the maven deploy operation, which I wont cover off here. One of the annoying things to trip over is access control to the destination repository, which needs to go into the settings.xml in the local user’s .m2 Maven directory:

<servers>
  <server>
    <username>robert</username>
    <password>...</password>
    <id>central</id>
  </server>
  <server>
    <username>robert</username>
    <password>...</password>
    <id>snapshots</id>
  </server>
</servers>

The documentation is opaque around this, and it is not obvious that the <id/> in the <repository/> for the distribution management is used to look up the login credentials in the <servers/> section of the settings.xml. While I appreciate the benefits of not wiring credentials into the pom.xml directly, it is easy for these two pieces of information to get out of synch, and easy to forget the existence of the settings.xml when your release falls over with cryptic errors because it can’t login to Artifactory.

The final bit of wiring goes into the <plugins/> section:

<build>
  <plugins>
    <plugin>
      <groupId>org.codehaus.mojo</groupId>
      <artifactId>versions-maven-plugin</artifactId>
      <version>2.2</version>
    </plugin>

    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-scm-plugin</artifactId>
      <version>1.9.4</version>
      <configuration>
        <connectionType>connection</connectionType>
        <tag>${project.artifactId}-${project.version}</tag>
      </configuration>
    </plugin>
  </plugins>
</build>

The first of these, versions-maven-plugin, is used during the release process to fiddle with the version of your released artifact, and maven-scm-plugin wires the release process back to the source code repository defined in <scm/>. Note that there are a couple of different ways to define the source code repository, and there are corresponding and roughly similarly named things that go in this configuration connectionType. The documentation can more-or-less help you here.

Assuming that you have committed and are in the required branch, then the process becomes pretty simple:

  1. mvn versions:set
  2. mvn deploy
  3. mvn scm:tag
  4. mvn versions:set
  5. commit and push to Git

The mvn versions:set as I’ve used it above is interactive, but if you have a look at the documentation you will find a variety of different automagic ways of using it without interaction – the article by Axel Fontaine for instance is a good explanation of how to wire this process into Jenkins/Hudson.

In the set of steps I’ve just outlined, steps 4 and 5 are post-release stages, where I can set the version in the pom.xml back to a snapshot, and get the new snapshot version preserved in Git. I recommend doing this at the time that the release is being done, rather than doing it the next time that work is done on the project, for two reasons. First, this echoes the behaviour of the maven-release-plugin. Secondly it reduces the chance of forgetting to set the version at a later time.

Leave a Reply

Your email address will not be published. Required fields are marked *