Version names with Maven: Creating the version name

In many cases, it may be useful to be able to extract the current version number of an application: You can immediately see which version is deployed on which stage, the version information can avoid misunderstandings in error reports, etc.

Apart from the version number, it is often even more useful to add further information, such as build number, time stamp, branch and commit ID, to the application during development.
This article shows how this can be done automatically with Maven.
The next article in this series – “Version names with Maven: reading the version name” – which will shortly be published in the blog, describes how the reading works in different kinds of applications.

Basics

In the following, the designation “version name” is used to name the version number which contains further information.
The general mechanism for writing the version name into the application is as follows: The Maven versionName is created, which is written into the files of the application using resource filtering. This article demonstrates this procedure for a properties file, an HTML file and the manifest file.
The simplest version name consists of the Maven version which is specified in the <version> tag:

<version>1.0-SNAPSHOT</version>
<properties>
    <!-- Printable version name -->
    <versionName>${project.version}</versionName>
</properties>

As an example, the versionName property thus receives the following value during the build:
1.0-SNAPSHOT.
In the following, this version name is expanded by a time stamp, SCM information (branch and commit), build number and a special name for releases.

Writing version names into a properties file

First of all, the version name must be written into files with Maven resource filtering. From there, it can be read by the application. A universally applicable procedure is to write the version name into a properties file. For example, a src/main/resources/app.properties file is created which contains the versionName property described above:

versionName=${versionName}

During the Maven build, the placeholder is replaced as follows:

<build>
    <resources>
        <resource>
            <!-- Filter for version name in properties -->
            <directory>src/main/resources</directory>
            <filtering>true</filtering>
            <includes>
                <include>app.properties</include>
            </includes>
        </resource>
    </resources>
</build>

The properties file which is stored in the Maven targetdirectory during the build looks as follows:

versionName=1.0-SNAPSHOT

This applies to Maven projects which are zipped as JAR files and for projects which are zipped as WAR files.

Writing version names into an HTML file

This procedure is even easier in web applications. The version name is directly written into an HTML file which is delivered to the client. For example, a src/main/webapp/index.html file is created which contains the versionName property defined above:

<html>
  <body>
    <h2>${versionName}</h2>
  </body>
</html>

During the Maven build, the placeholder is replaced as follows using the resource filtering of the maven-war-plugins:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-war-plugin</artifactId>
            <version>3.0.0</version>
            <configuration>
                <!-- Filter for version name in html-->
                <webResources>
                    <resource>
                        <directory>src/main/webapp</directory>
                        <filtering>true</filtering>
                        <includes>
                            <include>index.html</include>
                        </includes>
                    </resource>
                </webResources>
            </configuration>
        </plugin>
    </plugins>
</build>

The HTML file which is stored in the Maven targetdirectory during the build looks as follows:

<html>
  <body>
    <h2>1.0-SNAPSHOT</h2>
  </body>
</html>

Writing version names into the manifest

When a JAR file is delivered, there is one more possibility: to write the version name into the manifest file. This has the advantage that there is no need to create another properties file. This can either be realized using the maven-jar-plugin or the maven-assembly-plugin, whereby the respective <configuration> tag looks the same. The following example shows how it looks using the maven-assembly-plugin:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-assembly-plugin</artifactId>
            <version>2.6</version>
            <configuration>
                <archive>
                    <!-- Add version name to manifest -->
                    <manifestEntries>
                        <versionName>${versionName}</versionName>
                    </manifestEntries>
                    <manifest>
                        <mainClass>${mainClass}</mainClass>
                    </manifest>
                </archive>
            </configuration>
        </plugin>
    </plugins>
</build>

The manifest file which is created during the build within the JAR file under META-INF/MANIFEST.MF, can look as follows:

Manifest-Version: 1.0 
Archiver-Version: Plexus Archiver 
Built-By: jschnatterer 
versionName: 1.0-SNAPSHOT 
Created-By: Apache Maven 3.3.9 
Build-Jdk: 1.8.0_101 
Main-Class: de.triology.versionname.App

Expanding the version name by a time stamp

The following shows how the version name can successively be expanded by further information. A time stamp can be added without additional plugins. To do so, the maven.build.timestamp.format property must be defined first and then the maven.build.timestamp property must be used in the versionName property:

<properties>
    <!-- Printable version name -->
    <maven.build.timestamp.format>yyyy-MM-dd HH:mm</maven.build.timestamp.format>
    <versionName>${project.version} (${maven.build.timestamp})</versionName>
</properties>

As an example, the versionName property thus receives the following value during the build:
1.0-SNAPSHOT (2016-09-26 09:07).

Expanding the version name by SCM information

If additional information is to be displayed from the source code management (SCM), the buildnumber-maven-plugin can be used. It works with the SCMs subversion, git, mercurial and perforce. After the execution of the goal create (which is by default in the initialize phase of the Maven build cycle), the properties buildnumber and scmBranch are available. The following example shows how this may look for a multi module project using git:

<build>
    <plugins>
        <!-- Write the current git revision into ${buildnumber} and populate ${scmBranch} -->
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>buildnumber-maven-plugin</artifactId>
            <version>1.4</version>
            <executions>
                <execution>
                    <goals>
                        <goal>create</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <!-- Get the scm revision once for all modules -->
                <getRevisionOnlyOnce>true</getRevisionOnlyOnce>
                <!-- Don't fail on modified local resources -->
                <doCheck>false</doCheck>
                <!-- Don't update SCM -->
                <doUpdate>false</doUpdate>
                <!-- Use short version of git revision -->
                <shortRevisionLength>7</shortRevisionLength>
            </configuration>
        </plugin>
    </plugins>
</build>

In the version name, the properties which are provided by the plugin can be used:

<properties>
    <!-- Printable version name -->
    <versionName>${project.version} (${maven.build.timestamp}, branch ${scmBranch}, commit ${buildNumber})</versionName>
</properties>

As an example, the versionName property thus receives the following value during the build:
1.0-SNAPSHOT (2016-09-26 09:07, branch master, commit e2f18bb).

Expanding version names by a build number

When using continuous integration (CI) tools, such as Jenkins, the build number can additionally be included in the version name. This enables the distinction between the local developer build and CI builds. To be able to distinguish the CI build from others in Maven, a Maven profile can be used.Jenkins injects the current build number of the job into the BUILD_NUMBER environment variable. This can be used both for profile activation and in the build number itself. The following snippet leads to Jenkins additionally adding the build number into the version name without further configurations:

<profiles>
    <profile>
        <!-- Profile that extends the printable version number by an optional build.
            It is activated when an environment variable called BUILD_NUMBER exists (as in Jenkins) -->
        <id>versionNameBuildNumber</id>
        <activation>
            <property>
                <name>env.BUILD_NUMBER</name>
            </property>
        </activation>
        <properties>
            <versionName>${project.version} build #${env.BUILD_NUMBER} (${maven.build.timestamp}, branch ${scmBranch}, commit ${buildNumber})
            </versionName>
        </properties>
    </profile>
</profiles>

As an example, the versionName property receives the following value during the build:
1.0-SNAPSHOT build #17 (2016-09-27T07:55:43Z, branch master, commit 4dd3cf5)

For local builds, the version name described in the previous section remains the same.

Creating a special version name for a release

Due to the expansions mentioned above, the version name is rather long now and contains information which is primarily of interest to developers. In production, the version number of an application alone identifies it uniquely. This is why this additional information is not necessarily required for the release. So, if you want to make a distinction between development and release versions, this can also be done using Maven profiles. The maven-release-plugin, for example, sets the performRelease attribute when the release is created (Goal release:perform). At this point, this can be used as best practice approach for activating the profile:

<profiles>
    <profile>
        <!-- For releases, use a simple version name, consisting of the version number.
             Don't add timestamp, SCM info, build number, etc. -->
        <id>versionNameForRelease</id>
        <activation>
            <property>
                <name>performRelease</name>
            </property>
        </activation>
        <properties>
            <versionName>${project.version}</versionName>
        </properties>
    </profile>
</profiles>

If you do not want to use the plugin, the attribute can also be set manually during release: mvn <goals> -DperformRelease

As an example, the versionName property thus receives the following value during the build:
1.0-SNAPSHOT

Conclusion and outlook

This article shows how a version name can be written into different file types using Maven, from where it can be used in the application. Based on this, several possibilities are demonstrated for how to expand the version name with information which is useful during development. Finally, the article shows how the simple version number can be used as a version name again during the release build.

The examples shown are summarised in an executable example at GitHub. This also contains the programmatic reading of the version name, which is described in detail in the next article in this series– “Version names with Maven: reading the version name”.

Share this article

Kommentare

  1. Pingback: Maven: Create a more sophisticated build number | IT affinity!

Johannes Schnatterer
Solution Architect
With a special focus on quality, open source enthusiasm, a touch of pedantry and Boy Scout Rule under his belt, he is trying to make the world of IT a little better every day.