Tommi's Scribbles

Automatic Version Numbers With Gradle

Automatic Version Numbers With Gradle
  • Published on 2022-02-05

When working on any software project, version numbers are a useful tool. Version numbers let you track builds and bugs. However, managing version numbers by hand is a pain. Some systems, such as AWS CodePipeline, make automatic build versioning easy. Some, such as Gradle, make the task less obvious.

In this article, I show you one way to handle automatic build version numbers with Gradle and Kotlin.

Getting started

I assume you already have a Gradle build set up, so I will not go through that. Gradle has good documentation on how to set up a gradle build project in case you are just starting out. I am using Gradle 7.2. If you are on a different version, you might need some tweaks. Same goes if you are using Groovy instead of Kotlin to write your build scripts.

To start, create a new file in the root project directory called version.properties. The contents look very simple:

VERSION_BUILD:1

Gradle Build Script

With the helper file ready, we can go on and edit the build.gradle.kts file. The method is pretty simple. When we want to use the version number, we read it from the file. When we publish the project, or really run whatever task you want to grow the version number with, we read the current value, increase the value by one, and write it back.

We start by including some java libraries we are going to use.

import java.io.FileInputStream
import java.io.FileNotFoundException
import java.io.FileOutputStream
import java.time.LocalDateTime
import java.time.temporal.ChronoField
import java.util.Properties

Next, make sure you declare the version number. I like to use something I can recognize when it failed assigning.

version = "beta"

Now we are ready to write a few helper functions. The first one will read the current version number. As you can see from the bottom right corner of this website, I like doing chronological version numbering. You can format the string with the build number value as you see fit or make a more elaborate versioning scheme.

fun parseCurrentVersion(): String {
val versionPropsFile = File("version.properties")
if (versionPropsFile.canRead()) {
val versionProps = Properties()
versionProps.load(FileInputStream(versionPropsFile))
val versionBuild = versionProps.getValue("VERSION_BUILD") as String
val current = LocalDateTime.now()
return current.get(ChronoField.YEAR)
.toString() + "." + current.get(ChronoField.MONTH_OF_YEAR)
.toString() + "." + versionBuild
}
else {
throw FileNotFoundException("Could not read version.properties!")
}
}

The second helper function will increase the stored version number by one.

fun increaseVersion() {
val versionPropsFile = File("version.properties")
if (versionPropsFile.canRead()) {
val versionProps = Properties()
versionProps.load(FileInputStream(versionPropsFile))
val versionBuild = (versionProps.getValue("VERSION_BUILD") as String).toInt() + 1
versionProps.setProperty("VERSION_BUILD", versionBuild.toString())
versionProps.store(FileOutputStream(versionPropsFile), null)
}
else {
throw FileNotFoundException("Could not read version.properties!")
}
}

And that is it. Now, you can use the functions as a part of your scripts. For example, to read the current file number in jar task, you can do:

tasks.named("jar") {
version = parseCurrentVersion()
}

Or for a publish:

tasks.named("publish") {
version = parseCurrentVersion()
doLast {
increaseVersion()
}
finalizedBy("gitCommit")
}

Improvements

Obviously this is a very rudimentary example that leaves a lot of room for improvement. The hardcoded variable names are ugly and the method only supports simple schemes as is. You might have also guessed from the last line that the version properties file needs to live in the version control system, and the build needs to commit and push the file after a CD/CI build runs.

However, it is a working solution that hopefully gets you started and gives you some idea on how to automate version numbers with Gradle.