Fork me on Github

Adding Commit Info to your Grails Application

Knowing the exact version of code that your application is running is important

Authors: Colin Harrington

Grails Version: 3.3.1

1 Grails Training

Grails Training - Developed and delivered by the folks who created and actively maintain the Grails framework!.

2 Getting Started

In this guide we are going to add git commit info to your grails build artifacts and running application. There are many benefits of keeping your commit info handy:

  • Commit info is encapsulated within the built artifacts

  • Fast authoritative means of identifying what specific code is running in an environment

  • This solution doesn’t rely on external tracking mechanisms

  • Transparency and reproducibility when investigating issues

If you prefer the summary checkout tl;dr

2.1 What you will need

To complete this guide, you will need the following:

  • Some time on your hands

  • A decent text editor or IDE

  • JDK 1.7 or greater installed with JAVA_HOME configured appropriately

2.2 How to complete the guide

To get started do the following:

or

The Grails guides repositories contain two folders:

  • initial Initial project. Often a simple Grails app with additional some code to give you a head-start.

  • complete A completed example. It is the result of working through the steps presented by the guide and applying those changes to the initial folder.

To complete the guide, go to the initial folder

  • cd into grails-guides/adding-commit-info/initial

and follow the instructions in the next sections.

You can go right to the completed example if you cd into grails-guides/adding-commit-info/complete

3 History

There have been several different approaches to accomplish this task in the past that are worth mentioning.

3.1 Early approaches

In the earlier days of Grails there were approaches that tapped into the build events such as eventCompileStart in scripts/_Events.groovy to capture the git-sha ("git rev-parse HEAD".execute().text) and include it within the application as described by this post.

3.2 application.properties

Grails 2.x and below has an application.properties that can be utilized to store commit properties. This is the same place that Grails used to store its application name and version.

In this example some mechanism needs to be in place to append/update the commit info to the application.properties It could be the build system that appends git commit info as a build step before producing a war.

After the commit info is added to application.properties We can access that metadata in several ways:

  • GrailsApplication via grailsApplication.metadata['git.sha']

  • Utilizing the taglib <g:meta name='git.sha'/> in a view.

3.3 Build Info Plugin

Another popular approach that was popular with Grails 2.x was to utilize the Build Info Plugin The build-info plugin works by utilizing the environment variables available via a build system like Jenkins.

  • BUILD_NUMBER

  • BUILD_ID

  • BUILD_TAG

  • SVN_REVISION

  • GIT_COMMIT

  • GIT_BRANCH

These values would then be available by accessing the /buildInfo endpoint.

4 Writing the Application

There are two basic steps

Let’s get started!

4.1 Gradle Plugin

One of the features of Grails 3.x is that the applications come packaged with Gradle as a build tool.

Rather than implementing our own mechanism to capture git commit information we can leverage the gradle-git-properties plugin. The goal of this plugin is to capture the commit information and store it in a file called git.properties

Adding a Gradle plugin is fairly straight forward. This involves a few steps.

  1. Adding the dependency to the buildscript { …​ } block in build.gradle. This means that you are adding the dependency to the build and not your deliverable application.

  2. Ensuring the build script dependency will be resolved via the appropriate maven repositories

  3. Applying the plugin

/complete/build.gradle
buildscript {
    repositories {
        maven { url "https://plugins.gradle.org/m2/" }
    }
    dependencies {
        classpath "gradle.plugin.com.gorylenko.gradle-git-properties:gradle-git-properties:1.4.17"
    }
}
apply plugin: "com.gorylenko.gradle-git-properties"

Now every time the application is compiled or run (specifically the JavaPlugin’s classes task), you’ll find that git.properties has been generated. When running the app locally, this file may be found at /build/resources/main/git.properties. When packaged as a war or standalone jar you’ll find git.properties included in WEB-INF/classes:

$ unzip -l ./build/libs/complete-0.1.war | grep git.properties
      276  2017-02-02 11:53   WEB-INF/classes/git.properties

Inspecting git.properties reveals the commit information that was captured

git.branch=master
git.commit.id=777d70650bb055862436fc59071072aae8cd0ddd
git.commit.id.abbrev=777d706
git.commit.user.name=Colin Harrington
[email protected]
git.commit.message.short=Adding gradle-git-properties plugin
git.commit.message.full=Adding gradle-git-properties plugin

git.commit.time=1487262171

It is possible to configure/influence how git.properties is generated by configuring the gradle task (in build.gradle).

gitProperties {
    dateFormat = "yyyy-MM-dd'T'HH:mmZ"
    dateFormatTimeZone = "CST"
}

4.2 Spring Boot Actuator endpoints

Now that we have the git commit info captured and included in our application via a git.properties file, it is time to utilize it.

Grails 3.x is based on Spring Boot and we can leverage some of the underlying tools already included in the application.

Spring Boot includes a number of additional features to help you monitor and manage your application when it’s pushed to production. You can choose to manage and monitor your application using HTTP endpoints, with JMX or even by remote shell (SSH or Telnet). Auditing, health and metrics gathering can be automatically applied to your application. that has some production ready features already built into it.

Built into Spring boot is a module called spring-boot-actuator that provides these endpoints. The endpoints are disabled by default so the first step is to enabled them.


If you open up grails-app/conf/application.yml you will find a block of configuration that looks like this:

grails-app/conf/application.yml
# Spring Actuator Endpoints are Disabled by Default
endpoints:
    enabled: false

You simply have to enable the actuator endpoints by setting enabled: true

/grails-app/conf/application.yml
endpoints:
    enabled: true

5 Running the Application

To run the application use the ./gradlew bootRun command which will start the application on port 8080.

After enabling the actuator endpoints, when you run the application you can go to the /info endpoint at http://localhost:8080/info and you should see some JSON formatted info like:

{
    app: {
        name: "complete",
        grailsVersion: "3.2.6",
        version: "0.1"
    },
    git: {
        commit: {
            time: "2017-02-16T10:31-0600",
            id: "2f27f2c"
        },
        branch: "master"
    }
}

6 Configuration

You may not have wanted to enable all of the default actuator endpoints. If you wanted to just enable the info endpoint you’ll want to set endpoints.info.enabled=true via config and leave endpoints.enabled=false:

# Spring Actuator Endpoints are Disabled by Default
endpoints:
    enabled: false
    info:
        enabled: true
You should be aware that enabling the all off the actuator endpoints might open up several endpoints that you may not want to exposed. Some of the endpoints expose sensitive information and you may have to secure or disable them. Consult the documentation for more info http://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-endpoints.html

6.1 Configuring the commit info shown

You may have noticed that the /info endpoint is only a subset of the properties in git.properties such as the abbreviated sha, commit time and branch. You can display the full git properties via configuration by setting management.info.git.mode=full

You’ll set these values in grails-app/conf/application.yml

/complete/grails-app/conf/application.yml
management:
    info:
        git:
            mode: full

Then when you restart the application and access the /info endpoint at http://localhost:8080/info you should see something like:

{
    app: {
        name: "complete",
        grailsVersion: "3.2.6",
        version: "0.1"
    },
    git: {
        commit: {
            message: {
                full: "Enabling Spring Actuator Endpoints",
                short: "Enabling Spring Actuator Endpoints"
            },
            time: "2017-02-16T11:46-0600",
            id: "1695eb2c925e486acb962acf771f813a36568719",
            id.abbrev: "1695eb2",
            user: {
                email: "[email protected]",
                name: "Colin Harrington"
            }
        },
        branch: "master"
    }
}
Under the hood there this behavior is handled by the GitInfoContributor. The InfoEndpoint is extensible and will aggregate all enabled beans implementing org.springframework.boot.actuate.info.InfoContributor

Moreover, we can add a functional test to verify that Git information appears in the rest response.

Add grails-datastore-rest-client plugin to your dependencies block

/complete/build.gradle
    testCompile "org.grails:grails-datastore-rest-client"
/complete/src/integration-test/groovy/demo/InfoSpec.groovy
package demo

import grails.plugins.rest.client.RestBuilder
import grails.testing.mixin.integration.Integration
import grails.transaction.Rollback
import spock.lang.Specification

@Integration
class InfoSpec extends Specification {

    def "test git commit info appears in JSON"() {
        given:
        RestBuilder rest = new RestBuilder()

        when:
        def resp = rest.get("http://localhost:${serverPort}/info") {
            header("Accept", "application/json")
        }

        then:
        resp.statusCode.value() == 200
        resp.json
        resp.json.git
        resp.json.git.commit
        resp.json.git.commit.message
        resp.json.git.commit.time
        resp.json.git.commit.id
        resp.json.git.commit.user
        resp.json.git.branch
    }
}

6.2 Environment Configuration

Adding git commit information to your application is handy for debugging and transparency. The default simple mode only exposes the sha, time and branch name. There are some organizations who would would prefer to restrict commit info even further in their more sensitive environments.

One approach is to leverage per-environment configuration to enable/disable and configure the endpoints differently according to the environment. For example if you only wanted simple mode enabled in production you might use the following config:

/complete/grails-app/conf/application.yml
environments:
    production:
        management:
            info:
                git:
                    mode: simple

6.3 Secure your endpoints if neccesary

In addition to the environment specific configuration, some organizations may want to restrict usage to only authenticated users or a specific set of roles. This can be accomplished by plugging into your security plugin. We’ll use Spring Security as a canonical example. The /info endpoint isn’t a Grails controller so if you use the default annotation based approach you’ll have to configure grails.plugin.springsecurity.controllerAnnotations.staticRules.

grails.plugin.springsecurity.controllerAnnotations.staticRules = [
   [pattern: '/',               access: ['permitAll']],
   [pattern: '/error',          access: ['permitAll']],
   [pattern: '/index',          access: ['permitAll']],
   // ...
   [pattern: '/info',           access: ['ROLE_ADMIN', 'isFullyAuthenticated()']]
]

The RequestMaps or simple map security configuration approaches use the request’s url so they won’t need anything special handling. Consult the Spring Security Core’s plugin documentation for more.

7 tl;dr (Too long; didn't read)

Summary:

  • Add the gradle-git-properties plugin

  • Enable actuator endpoint via endpoints.info.enabled=true or endpoints.enabled=true

  • Configure and Secure the endpoint as desired

  • …​

  • Enjoy knowing exactly which version of code is running (profit!)