Use different build numbers for every build — automatically using a gradle script

Another JIRA ticket with a crash report from a client: “The app always crashes when filtering the products”. No more information given. Is it a new crash? How can we reproduce it? Which app version was he using? After calling the client, I found out that the crash happens only on his Samsung tablet with the app version 1.1.2. When testing it on our devices I couldn’t reproduce it. The crash tracking captured the crash but it didn’t made any sense. A NullPointerException inside a comment?

Long story short; the client was using a debug build I created and deployed at the last meeting which had local changes. The version 1.1.2 on this test device was different from the tagged 1.1.2 version in our git repository. Normally, the build for the client has a build number 1.1.2–65. The missing build number identified the app version as a locally built debug version.

Increase the build number with Jenkins

Increasing the build number for every build on Jenkins works — it’s common practice. The only thing that bugs me is that a larger Jenkins build number doesn’t mean a newer app state. The number increases whether I build version 2.3.6 or 1.0.1.

It’s hard to understand why feature X is part of build 202 but not included in build 203. I received multiple emails because of a “missing feature”, simply because we were kind enough to create a build from a work in progress feature branch to show the progress (the version before).

We solved this problem with separate Jenkins jobs for preview builds. We never send links to the “latest” build version, a link always points to a specific build when we send out previews which haven’t been merged, yet.

Another job builds the “develop” branch to provide the “latest” version. Whilst this strategy works, it doesn’t solve the problem with local builds. Wouldn’t it be nice to have the same build version whether you are building on your local machine or through Jenkins? A solution that also works for your private fun project without Jenkins?

Good build numbers

In my opinion, a good build number shows the current state of the software version. It’s not a timestamp or a random increasing number, so it shouldn’t change when I build a version twice! A bigger number indicates a more advanced version of the software because we all know; A higher number is always better, everybody understands that.

Microsoft does a pretty good job with their build numbers for Windows. It’s clear that 11082 is a lot older than version 14342, and there’s no big difference to the latest version 14352.

SVN also use good versioning by increasing the revision for every commit — it’s the same revision for both local and remote builds. However is not possible in git because branching is so common (and that’s a good thing). Counting the commits of the current branch isn’t accurate. “342. commit” doesn’t point to a specific commit and multiple commits on different branches could have 341 ancestors.

Static build numbers for git

I thought a lot about this problem and came up with the following solution:

For every build I count the commits into the main branch (“master” or “develop” for most people). This commit number is a good start but not enough as I think it gives too many insights into the project. Once a client knows that the commit count is equal to the build number they start asking why the commit count is so high/low for the latest release. That’s why I add the project age (initial commit to latest commit) as the seconds part to the revision and add them together.

I decided to give one year the value 1000 — this means that the revision count increases every 8.67 hours. When you started your project half a year ago and you have 325 commits the revision is probably 825. This number is easy to understand, stable for every commit (locally and on Jenkins) and always increasing.

Multiple branches

The number alone doesn’t solve the multi branch problem. To solve this I add a second part to the build number, the commit count on the feature branch since last merging the default branch and a two character identifier (hash) of the branch name (example: 825-ud4). When you see another build with the same build number (825-za2) it’s easy to recognize that both builds are build on top of the same commit (build number 825). Apparently those builds are from different branches because of different branch identifiers.

When you told a client that feature X is available from build 450 on he will never ask why the feature isn’t in build 436-as16. And while developing feature Y you can provide “wt”-test-builds and your client knows that feature Y is available in build 421-wt6 as well as 532-wt6 (where you merged the latest 532 build from the default branch into your feature branch of feature Y).

Local Changes

Sending debug builds to the client is not a common practice. But it happens that a debug app version stays on a device of the client because a bug on that specific device required debugging on it. I also share the same test devices with QA. Identifying debug builds with local changes is very important.

I add the number of local changes and a big -SNAPSHOT to such a build.

1083-dm4(6)-SNAPSHOT 

This build has 6 local changes on a feature branch (dm) with 4 commits

Add it to your project

It’s so easy. I wrote this logic as a gradle scriptscript which you can apply with a single line and access the version name with the ext.gitVersionName variable. The configuration is optional.

The gitVersion variable gives you all information separately. This let’s you generate your own pattern.

See the GitHub project for more information and the code https://github.com/passsy/gradle-GitVersioner

PS:

Some of you may use Jake Whartons’ lazy strategy to generate a build number. This is a big help, but manually increasing a build version is something I always forget.

The same works with my script. The version increases for every commit on the default branch. #automateeverything

Easy, isn’t it?

Please let me know what you think about my git versioner — testing it in your current project will take you only a few seconds. I’m excited to get your feedback!