Archive for the ‘Continuous Integration’ Category

When you want to do Continuous Integration, Delivery and Deployment, you sometimes want to use the latest available artifacts in your builds. Just as with all things in life, there are people who oppose and people who advocate this approach. Of course, this will not be the silver bullet, but for certain projects it can be useful. In those kind of projects you just want to reference a range of versions for a certain artifact instead of relying on a static version number. Let me illustrate this with an example.

Let’s consider the following scenario. Suppose you work on a web-project which depends on a smaller library. Let’s assume it is a simple, plain JAR file with tests etc. This JAR file is of course subject to frequent change. You can build your build-pipeline in such way that your JAR is compiled, tested and delivered on every change. When a new artifact is uploaded to the repository, you can automatically trigger a new build of your web-project.

When you use maven, the difficult question is how you set a plausible version number without checking the code back in (which would trigger a loop in your build-process). Then we must solve the question about how the web-project will reference the library. Most likely, your project is stuck on the dependency in version X.Y.Z. The version number is not increased on every build because it is not patched nor did the functionality change. Moreover, you don’t want to check out the web-project every time to increase the version number of the depending library manually.

A solution here is to enhance your versioning system with a build number and use ranges. Both needed features are supported by Maven itself (time of writing 3.2.3) and the versions-plugins.

The Version Problem

Maven is not that great when it comes to CI. The way Maven handles the versions is not very flexible. But, if you really think about the problem, you’ll realize that it is the way how software is versioned that seems to be problematic. The classical X.Y.Z scheme is particularly helpful for humans and human controlled workflows. The decision which number (major, minor, patch) has to be increased, remains a human decision. This has definitely advantages. Through this version-scheme, you communicate with the outside world how drastically your software has changes.

But it is very impractical for a CI system because it can’t automate this way of thinking. The CI only knows about the number of builds it has made. The CI just knows build #1, #2, etc etc.

You don’t want to loose the version-information nor the build number. A possible solution is to combine the two as one. You can change the versioning scheme to X.Y.Z-buildnumber.

The Library POM

Using the above scheme is nice. But, there is one trade-off. You don’t want to store the buildnumber in the source-code. This has been done in the past, as I can tell out of my own experience and to a certain extent this was plausible and functioned well. A drawback was that the Maven Release Plugin stored data in SVN itself.

Our goal is to keep the POM as clean and simple as possible. We don’t want to pollute the version-number either. The scheme X.Y.Z-buildnumber is useful in the world of CI but not in your local environment where the X.Y.Z scheme is the one which is under your control. So, let’s keep it simple and use the X.Y.Z scheme in the source-code and during development.

There is no need to have a build number locally nor in your source-code.

Building the Library

When the CI builds the library, it can add the build-number on the fly. This example uses the GO pipeline counter as the build number we want to add to the version-number. Unfortunately you cannot append it easily to the POM. You first have to read the version, add the build-number yourself and use it in the POM. Luckily we can use the power of Linux to put this in one line:

mvn versions:set -DnewVersion=$(mvn help:evaluate -Dexpression=project.version 2>/dev/null| grep -v "^\[")-$GO_PIPELINE_COUNTER

The version number is not checked in. Committing the code back to Git would trigger a new build and put us in a loop. We gave this a lot of thought and since there is no real use-case for this, we decided not to do it. It doesn’t solve nor create problems.

A note on placeholders

In earlier days it was possible to use placeholders in the version-tag. In Maven3 this is prohibited except for three fields. When you use the version-tag in the source-code, you locally have a problem because you have to provide the information which is cumbersome and error-prone. So, we decided not to use this system although you could easily set them using the CLI in your CI-environment.

The POM with the Dependency

In our example, we want to automatically build a dependent project when changes in the above library occurs. You have to be aware that this approach has advantages but could also have some disadvantages. You could easily break the software when there are non-backward-compatible changes. In our philosophy, that is a good thing. We need to keep our software up and running with the newest libraries and this involves break and incompatibilities. We need to detect failures as soon as possible. Of course, this is not the wanted behavior for all kinds of projects. You need to make the decision yourself if you want this system or not.

Let’s continue. In the depending project, you define a dependency but not with a static version but with a range:

   <version>[1.0.0, )</version>

This defines the range of version you want to compile with. This is an open end version, so we will try to compile with every new version of the library. When the compilation or tests fail, we will be informed so we can fix the issue as soon as possible.

Normally seen, Maven should be able to resolve the ranges, but due to some bugs (currently we use Maven 3.2.3), the range do not gives the correct artifact. But there is a work-around with the versions-plugin. In a first step, you resolve the ranges:

mvn versions:resolve-ranges

This will replace the ranges with the latest version found in the repository. Your artifact will be built and tested with the latest dependency. Eventually you can auto-deploy your artifact directly.