A while back, I wrote about how to deploy a Spring Boot application to Heroku. Since I have automatic deployment from master branch activated for the application at Heroku, I want to be able to see right away from which commit the running application has been built. So in this blog post, I’m going to show how to add a link to a commit on GitHub to a web application. I’m reusing the application from last time, so all examples will use Apache maven as build tool and Spring Boot as application framework. Furthermore I’ll show how to configure the build so that it works locally and when deploying an application to Heroku, since some special configuration is required for that.

Adding a static link

The most straightforward way to get this kind of information into the web views is to use Thymeleaf’s internationalization facilities. This way we don’t have to add any code to the controllers in order to display the commit link in the application. Spring Boot will automatically pick up a file called messages.properties from the root of the resources directory and make it available to Thymeleaf. To get started I’ll just add some static information.

I don’t want the complete SHA-1 commit hash to be displayed in my application, so I’ve abbreviated it in the template. Furthermore I’ve decided to construct the complete link to the commit on GitHub in the properties file and not in the view template. This way I can reuse it everywhere without leaking the information about how commit links are constructed into the views. To display this information in the Thymeleaf view, the home.html template has to be extended:

After starting the application the next time, the static commit link will already be displayed:

Extracting the commit hash via maven

Adding a static link to our messages.properties is nice, but the displayed information will be wrong after the next commit. For this reason we need to set the information dynamically during the build process. This can be easily be achieved using the buildnumber-maven-plugin in conjunction with resource filtering. I’ve added the following configuration to the maven build to make the buildNumber build property available in the build (the scm configuration is needed to make the buildnumber-maven-plugin work):

The resources configuration looks a bit strange, doesn’t it? But it is important to do it like this. What I’m doing here is activating filtering only for the messages.properties file, while disabling it for any other files in the src/main/resources directory. Otherwise the Thymeleaf views would be filtered as well! Since Thymeleaf’s expression language also uses the ${...} notation it is possible (although unlikely) that expressions will be replaced which really shouldn’t be. With the new build configuration in place, the messages.properties file can be changed to:

Note: This blog post covers Spring Boot 1.2.x. Starting with version 1.3 the @ symbol has to be used as delimiter for build properties. For this reason when using version 1.3 it has to be git.commit.hash=@buildNumber@. For more information see Spring Boot 1.3 Release Notes.

Encapsulating SCM information in the build

Looking at the value of the git.commit.link property we can see that it duplicates information from the scm section of the POM. For this reason it makes sense to construct the value using the value of the project.scm.url build property. To do this we could change the value of git.commit.link to ${project.scm.url}/commit/${buildNumber}. It’s okay to do it like this, however I like to construct all build time properties in a central location, that is the POM. For this reason I chose to add the following to my POM:

This way the information about which SCM hosting service the project is using is encapsulated in the build. After applying this change, the messages.properties file looks like this:

Abbreviating the commit hash

After starting the application the next time, we can convince ourselves that the commit link is rendered.(*) But what do we see? The complete commit hash is rendered into the page, making it pretty unreadable.

Let’s see what we can do about that! One way to abbreviate the commit hash would be to fall back to Java: We could use the HomeController for formatting the value and adding it to the model. I’ve implemented it in this little gist. This solution isn’t feasible for several reasons:

The model is polluted by information which has nothing to do with displaying Records (which currently is the only responsibility of the HomeController).

Reusing the commit hash in different views becomes complicated since it is now coupled to the HomeController.

In MVC it is be the responsibility of the view to decide how to render values, not the controller’s.

For these reasons I’ve decided to use Thymeleaf’s string utilities to format the commit hash value. Getting the expression right is a bit tricky, so I’ll just show the solution and explain it step by step:

As you can see, the th:text is where the magic happens. The outer ${...} tells Thymeleaf to treat the whole thing as an expression. This is important because of what’s coming next: #strings.substring(...) calls Thymeleaf’s string utilities. Without the ${...} Thymeleaf would just render the call as text. Next we pass three parameters to the substring function – '__#{git.commit.hash}__', 0 and 7. The latter two parameters are simple: 0 and 7 are used as start and end index for the substring function. The first parameter is the string input for the substring function. Since we want to format a string from messages.properties, we have to tell Thymeleaf again to do special processing. This is what the underscores are for. With this code we finally get what we want:

(*) Note that the resource filtering might not work when starting from an IDE. This is because runtime classpath the IDE constructs might be different from the one constructed when the application is started using java -jar app.jar.

Build configuration for Heroku

Bringing the new GitHub commit link feature live is only a push away when deploying to Heroku. But what do we see after the deployment?

For some reason the link doesn’t work when the application is built on the Heroku build infrastructure. The first hint as to why this is can be found in the build log:

The buildnumber-maven-plugin cannot determine current revision from git. So what has been working locally fails in the target environment. After some research I figured out why. The Heroku slug compiler deletes the .git directory from the working copy before the build process starts. For this reason the git revision cannot be extracted. Luckily Heroku provides the SOURCE_VERSION environment variable at build time, which holds the hash of the commit being built. To make the commit hash extraction work on Heroku and in the local environment, the build process has to detect where it is running. This calls for profiles! Let’s define one that sets the buildNumber property to the value of the SOURCE_VERSION environment variable if it’s available:

This profile alone won’t do the trick, since the buildnumber-maven-plugin will still be running and it will override the property definition from the Heroku profile. For this reason we need to define a profile that runs the buildnumber-maven-plugin by default but doesn’t run when the Heroku profile is active. This is what the activeByDefault instruction is for. It tells maven to run a profile only if no other profile is active. Moving the configuration of the buildnumber-maven-plugin to a profile looks like this:

Conclusion

Adding information about the build revision to the UI of an application can ease up the process of analyzing problems. By combining handy tools like Apache maven, Spring Boot and Thymeleaf it is fairly easy to accomplish this. In this blog post I’ve shown how to extract a git commit hash using maven, both in a local environment and when deploying to Heroku. I’ve explained how to load the extracted value using Thymeleaf’s internationalization facilities and how to abbreviate the extracted value using the #strings utility object. With this information you should be able to adapt my example to your application and environment.

Tags

Benedikt Ritter works as a Software Crafter at codecentric AG in Solingen since September 2013. His joy for creating reliable software is not limited to coding at work: Benedikt is member of the Apache Software Foundation and Committer for the Apache Commons project.