Continuous Deployment, Versioning and Git

So you have heard about continuous delivery and continuous deployment and you are eager to try, but your manager is afraid that you will lose traceability of the project. How can frequent updates of your binaries be tracked in a rapidly progressive environment, where every commit is a potential release?

Artifact Version vs. Commit

The traditional Java community way of version handling with a three digit number, potentially followed by a build number or a qualifier, e.g. 1.2.3.RELEASE, works well if you have long development cycles with the occasional bugfix release in between. The problem is that it does not scale when the frequency of releases increases by a magnitude or two. Imagine what your version control system would look like if every commit had its own release branch, or if you manually had to update your dependencies several times daily. Consequently, the notion of snapshot releases has been adopted to indicate that you are working with a moving target. The tradeoff is that the traceability is lost, because one snapshot version refers to many different versions of the software.

Different workarounds that allow developers to identify specific snapshots have been introduced to overcome this problem. For example, Maven adds a timestamp when the artifact is deployed to a repository and Jenkins can generate a fingerprint for a binary. Both these methods enable tracking of the artifact version which is good, but they do not tell the complete story. Once you have pinpointed the specific artifact, you need to consult the build log to find out the corresponding commit before you can checkout the code and start debugging.

Wouldn’t it be nice if you could get the correct source code from the version control system immediately, without the detour to the build log? With Git, a 40 digit SHA-1 hash sum is calculated for each commit, making it the perfect candidate to add traceability to your project.

buildnumber-maven-plugin

It turns out that the buildnumber-maven-plugin is the tool for the job. Unfortunately, the documentation on its web page is quite scarce, but we can get some more information by executing the plugin itself:

Looks promising, we add the following lines to the <build><plugins> section in our pom file:

And then we attempt to validate the project:

Ok, the plugin seems to be looking for a <scm> configuration tag in the pom (to be more specific, it is actually looking for a <scm><connection> or a <scm><developerConnection> tag), let’s add one. In order to do that, you also need to add the Git URLs to your repository:

Revalidate the project:

Let’s compare with the current Git SHA-1:

Excellent, now we have a Maven ${buildNumber} variable with the current Git SHA-1 value that we can use in our project!

Now, the question arises how we can utilize the ${buildNumber} in the best way. The most important thing is to advertise it to your users, let them be a developer of a dependent module, a tester at the Q/A department, a remote client application, a random Internet user or even yourself. Depending on the project type, different suggestions come to mind. I have created a project on GitHub with working implementations of the examples below.

Manifest Entry

If your users have access to the binary artifact, it can make sense to add the build number as an entry in the project’s MANIFEST.MF file. For example, the maven-war-plugin can be configured to add a Git-SHA-1 entry for a .war project:

Similarly, you can configure the maven-jar-plugin to add manifest entry for a .jar project.

Note, if your artifact is signed, you should not use the key SHA1-Digest as the manifest entry, because it is used during signature validation.

Properties File

Alternatively, a simple buildNumber.properties file may suit your need:

The file must be filtered so that the value gets assigned:

Static Method Call

If you implemented the property file solution above, you might as well create helper class that wraps the build number in a static method call:

Web Service Resource

Sometimes your users do not have direct access to your artifacts, but don’t let that stop you from sharing the build number. With a little [insert your favorite web framework here] magic, you can publish the build number as a web service resource. Below is an example of a Spring Controller:

Put into practice:

Web Projects

If you are a front-end web developer, one option is to embed the build number in the webpage itself, such as in an ever popular about box. A more subtle way is to inline it as an html comment in the index.html file:

Since we are dealing with a web project, the filtering works differently:

Wrap Up

Given that you now have a tool for managing artifact versions, should you stop making formal releases of your software? Front end web development is one area where the traditional release management may be questioned. The build server can continuously deploy successful builds to production and yet it is easy to find out exactly which commit that is currently used. Framework development on the other hand is a completely different story, considering the nature of the Java ecosystem where all dependency management rely on stable releases. Additionally, if you need a build number in a human readable format, the Git SHA-1 with its 40-digit string is probably not what you are looking for. The good news is that one solution does not exclude the other. If you would like beta testers to verify critical bug fixes or developers to start using the latest bleeding edge features in a snapshot release, you can benefit from using both the traditional version numbering as well as a commit specific identifier.

References

Edit

Oct 15th, 2012: Updated the blog post to include the version 1.1 of the buildnumber-maven-plugin because the 1.0 version had a bug that caused it to fail if executed from another process, e.g. within an IDE. Additionally, the reference project was updated to include all the latest versions of the dependencies and plugins.

16 Comments

  1. Mikael Karlsson

    Interesting stuff and nice examples!

  2. Thanks that was exactly what I was looking for – glad that the information came from Jayway :-)

  3. robbiepl

    use Manifest class…

    final InputStream manifestStream =
    Thread.currentThread().getContextClassLoader().getResourceAsStream(“META-INF/MANIFEST.MF”);
    Manifest manifest = new Manifest(manifestStream);
    System.printf(“%1$-20s | %2$s%n”,”git-sha-1″,manifest.getMainAttributes().getValue(“git-sha-1″));

    • Mariusz Przydatek

      Slight modification to pull the manifest file based on the request’s (HttpServletRequest) context. This way you’ll make sure you’re reading always the correct manifest file.

      ServletContext context = request.getSession().getServletContext();
      InputStream manifestStream = context.getResourceAsStream(“/META-INF/MANIFEST.MF”);
      Manifest manifest = new Manifest(manifestStream);

      Thanks for pointing in the right direction.

  4. Awesome post!! Just what I was looking for. Thanks!!

    • Mattias Severson

      @Aniruddh: Glad you liked it! Please note that I have just updated the blog to include the 1.1 version of the buildnumber-maven-plugin, because I encountered this bug in a project recently. Luckily, the problem could be solved easily by just bumping the version number.

  5. Mariusz Przydatek

    Fantastic post!!

  6. Thanks – this is the best write-up on this topic that I could find. Just to add my 2 cents – in my case I decided to propagate the build number to my JUnit code through a parameter for the forked VM used by the surefire plugin. My brief write-up about it can be found at http://feraldeveloper.blogspot.com/2013/05/including-or-referring-to-git-revision.html

  7. KK

    I am having issues where i am not getting the build number and timestamp. the fields are empty in the manifest file. i have updated version but still does not work

  8. Great post!
    I know its a bit old but I was looking for this and it really solve my problem.

    I’m using the filter to add parameter to my js files (to make sure that in each build the browser gets them new) and I was wondering if there is an easy way to take only 5 or 6 chars from the hash.

    Do you know a way?

    Thanx again for the great post,
    LT

    • Mattias Severson

      @LinkTree: Thanks!

      Since you are using JavaScript, one option would be to use substring, e.g.

      Alternatively, you can configure the shortRevisionLength of the buildnumber-maven-plugin, see the documentation (scroll down), e.g.

  9. Chris

    nice post.
    In a CD environment, you would ideally also like to use the commit id in your artifact naming scheme but this is not possible with the build number plugin.
    I wonder how you solved that problem?

    • Mattias Severson

      @Chris: The versions-maven-plugin (specifically the versions:set goal) allows you to specifically set the project <version>, and you can pass the ${buildNumber} as an argument. However, by doing so you will break the Maven version number format which is fine, as long as you are aware of the trade-offs:

      • It will not be possible to tell which release is the latest just by looking at the <version> tag, neither by humans, nor by tools like the Maven versions:display-dependency-updates goal.
      • If you are using an artifact repository such as Nexus or Sonar and you only have little disk space, you should consider to configure it to discard old artifacts. In case an old build is requested, you can always pull out the corresponding source code from the version control system and re-create your artifact.
  10. Beau Jackson

    Very nice example. Thank you!

Leave a Reply