Version management is an important part of every software project, yet it’s often overlooked. Even more often, it can be significantly improved.

How many times did you face the following problems?

You can’t determine the version of a component that is currently deployed.

There is no direct link between the component’s version and the version control system.

The system behaves differently between a local computer and a cloud deployment. Under closer investigation, it turns out that somebody forgot to bump the version in application.properties and some new code runs with the old version number.

These problems can be even more painful with microservices, where many components are deployed with different versions and special care must be taken to assure compatibility.

But, when configured properly and automated, version management can no longer be a nuisance.

With the Git-Version Gradle plugin, you can generate the version automatically based on commits and tags in your Git repository. It can be easily integrated with other plugins to provide the same versioning schema for Docker containers and expose the version information via REST API.

Sample Application

The sample Java web application is available on GitHub: https://github.com/98elements/git-version-demo. It is written using Java and Spring Boot.

At first, let’s see how Git-based automatic version management works and then we’ll dive into the configuration details.

Let’s build the application and inspect the generated artifact:

$ ./gradlew clean bootJar BUILD SUCCESSFUL in 1s 3 actionable tasks: 3 executed $ ls build/libs git-version-demo-dc3019b.jar

The application’s version is dc3019b . It looks like a Git commit hash, which is a good guess:

$ git log -n 1 commit dc3019b09418b62b9367ab7812c5811e89af6ea2 (HEAD -> master, origin/master) ...

We can build a Docker image with the version tag, too:

$ ./gradlew clean docker --info ... Successfully built b95e551e8e87 Successfully tagged com.98elements/git-version-demo:dc3019b :docker (Thread[Execution worker for ':',5,main]) completed. Took 0.976 secs. BUILD SUCCESSFUL in 1s 6 actionable tasks: 5 executed, 1 up-to-date

We can also run the application and obtain version information via REST API:

$ docker run --rm -p 8080:8080 com.98elements/git-version-demo:dc3019b $ curl http://localhost:8080/actuator/info {"app":{"version":"dc3019b"}}

When we create a Git tag representing a release, the plugin will detect it and change the version accordingly:

$ git tag -a 1.0.0 -m "Release 1.0.0" $ ./gradlew clean bootJar $ ls build/libs git-version-demo-1.0.0.jar

After creating a new commit, the plugin will generate a pre-release version:

$ git commit -m "First commit after release" --allow-empty $ ./gradlew clean bootJar $ ls build/libs git-version-demo-1.0.0-1-g6f4175f.jar

The plugin appended -1-g6f4175f to the version string. The 1 represents the number of commits after the last tag, while g6f4175f is the hash of the new commit.

If we have some uncommitted changes, the plugin will append .dirty to the version string:

$ echo "My own README content" > README.md $ ./gradlew clean bootJar $ ls build/libs git-version-demo-1.0.0-1-g6f4175f.dirty.jar

NOTE: Version strings generated by Git-Version Gradle Plugin are compatible with Semantic Versioning if we follow the convention with our release tags. A related quote from the Semantic Versioning 2.0.0 standard:

9. A pre-release version MAY be denoted by appending a hyphen and a series of dot separated identifiers immediately following the patch version. Identifiers MUST comprise only ASCII alphanumerics and hyphen [0-9A-Za-z-]. Identifiers MUST NOT be empty. Numeric identifiers MUST NOT include leading zeroes. Pre-release versions have a lower precedence than the associated normal version. A pre-release version indicates that the version is unstable and might not satisfy the intended compatibility requirements as denoted by its associated normal version. Examples: 1.0.0-alpha, 1.0.0-alpha.1, 1.0.0-0.3.7, 1.0.0-x.7.z.92

Configuration

We’ve got through the features of Git-Version Gradle Plugin. Now, let’s see how the sample project is configured!

Let’s start with the configuration in build.gradle :

plugins { ... id 'com.palantir.git-version' version '0.12.2' id 'com.palantir.docker' version '0.22.1' ... }

We use two plugins from com.palantir : Git-Version Plugin and Docker Plugin. The first one is responsible for version generation, while the latter adds support for building Docker containers.

Then, we have to declare that the Gradle project version comes from Git-Version Plugin:

version gitVersion()

and configure the Docker image:

docker { name "com.98elements/${project.name}:${project.version}" files tasks.bootJar.outputs.files } docker.dependsOn bootJar

The GET /actuator/info endpoint comes from Spring Boot Actuator:

dependencies { ... compile 'org.springframework.boot:spring-boot-starter-actuator:2.1.9.RELEASE' }

The response returned by the endpoint is configured in application.properties :

info.app.version=${project.version}

To enable substitution of Gradle variables in application.properties , we need to add a simple configuration in build.gradle :

processResources { expand(project.properties) }

That’s it! We managed to set up automatic versioning with Git-Version Gradle Plugin with just a few lines of code. That will help us avoid the common pitfalls of version management.

Read More

To read more about the used plugins and libraries, visit: