Java

Using Jenkins Build Version With Maven

Knowing which version of your application has been deployed on a certain environment is essential for today’s software development and maintenance. Have you ever needed to support a customer which reported a bug in a feature for which you already provided a patch in version control? That you asked yourself why the heck that fix isn’t on production and you don’t even know where or how to verify this?

There are many ways of versioning (which by itself is a very broad concept) your software but a simple mechanism to track what’s being deployed could be;

  1. Use a buildtool (e.g. a continuous integration server such as Atlassian Bamboo or Hudson/Jenkins) to create your build. Use it to relate the build with the changes and code in it.
  2. Identify the version and make this visible in the application.

You don’t create builds from your local workspace these days, do you? 🙂

Below a simple outline of achieving this with Jenkins, creating a Mavenized webapplication (WAR), passing along a build number and reading it back in application code e.g. a JSF backing bean.

Using a build number

Define a property in your Maven pom.xml which holds the build number which we can overridde externally later on:

<properties>
	<build.number>SNAPSHOT</build.number>
</properties>

Create a manifest

If your application is packaged as a WAR (<packaging>war</packaging>) you can instruct the Maven War plugin to create a manifest file. This is a META-INF/MANIFEST.MF file which we can pass along some meta information about a JAR or in this case, the WAR.

<build>
	<plugins>
		<plugin>
			<groupId>org.apache.maven.plugins</groupId>
			<artifactId>maven-war-plugin</artifactId>
			<configuration>
				<manifest>
					<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
				</manifest>
				<archive>
					<manifestEntries>
						<Specification-Title>${project.name}</Specification-Title>
						<Specification-Version>${project.version}</Specification-Version>
						<Implementation-Version>${build.number}</Implementation-Version>
					</manifestEntries>
				</archive>
			</configuration>
		</plugin>
	</plugins>
</build>

You could leave out some stuff, but the essential part is Implementation-Version which uses the ${build.number} property. This will create a MANIFEST.MF which sort of looks like the following:

Manifest-Version: 1.0
Implementation-Version: SNAPSHOT
Built-By: jenkins
Build-Jdk: 1.6.0_31
Specification-Title: Example Project Name
Created-By: Apache Maven
Specification-Version: 0.0.1
Archiver-Version: Plexus Archiver

If you don’t overridde build.number it’ll default to SNAPSHOT. Well, for now this doens’t add much value, but now we can have Jenkins set it for us too.

Jenkins Environment Variables

If you would translate the above mvn commandline to a Jenkins Maven job you can use several of Jenkins’ environment variables to pass along in properties. E.g.

  • BUILD_NUMBER – The current build number, such as “153”
  • BUILD_ID – The current build id, such as “2005-08-22_23-59-59” (YYYY-MM-DD_hh-mm-ss)
  • SVN_REVISION – For Subversion-based projects, this variable contains the revision number of the module.

You can take anything from the list (if applicable, such as SVN_REVISION when using Subversion) and pass it along as property. Let’s take BUILD_NUMBER as our build.number property: mvn clean install -Dbuild.number=${BUILD_NUMBER}

In Jenkins it would look like

Jenkins Maven Build With Properties

If you would run the Jenkins job again, its current build bumber would be used and end up in the manifest file.

Manifest-Version: 1.0
Implementation-Version: 153
Built-By: jenkins

Reading the version

To wrap up, you obviously need to make this version visible in a running application. You could add the version to the HTML header in comments or display it on some sort of health page. For working with manifest files you can read the Java Manifest Files Tutorial. To read from it use java.util.jar.Manifest and java.util.jar.Attributes as you can see in the (JSF) example below.

ExternalContext application = FacesContext.getCurrentInstance().getExternalContext();
InputStream inputStream = application.getResourceAsStream("/META-INF/MANIFEST.MF");
Manifest manifest = new Manifest(inputStream);

Attributes attributes = manifest.getMainAttributes();
String version = attributes.getValue("Implementation-Version");

Conclusion

There’s obviously more to it than meets the eye…or your requirements. You can now see which build of the application is running on a environment, and going back to your CI server – which generated the build number – and relate it to the code inside the build. We haven’t used Maven’s own project version, release plugins or other fancy tools yet, but if you start simple you can always expand into more elaborate schemes to suit your needs.