Automatic Versioning of Android Build Using Git Describe with Gradle

Automatic versioning of Android build using git describe with Gradle

A more proper and lean way to achieve the result which gained traction lately would be to use grgit integration, which uses JGit Java libray. As it uses JGit it doesn't even require git to be installed to work (which simplifies things in build pipelines).

Here's a basic example showing a similar (but with some additional information in gitVersionName string) solution:

plugins {
id 'org.ajoberstar.grgit' version '4.1.1'
}
ext {
gitVersionCode = grgit.tag.list().size()
gitVersionName = grgit.describe(tags: true, always: true)
}
android {
defaultConfig {
versionCode gitVersionCode
versionName gitVersionName
}
}
[...]

As you can see in Grgit API documentation the describe operation provides additional information other than most recent tag reachable in history:

Find the most recent tag that is reachable from HEAD. If the tag points to the commit, then only the tag is shown. Otherwise, it suffixes the tag name with the number of additional commits on top of the tagged object and the abbreviated object name of the most recent commit.

Anyhow, it won't tell if the state is dirty or not. This information can be easily added by looking at the clean status of the repo, and appending a string if it's not clean.

Gradle Script to Automate Android Versioning by git tags

found a solution

apply plugin: 'com.android.application'

ext.versionMajor = null
ext.versionMinor = 0
ext.versionPatch = 1
ext.versionClassifier = null
ext.isSnapshot = true
ext.minimumSdkVersion = 21

android {
//fetch version tag
setVersionNumberByTag()
compileSdkVersion 25
buildToolsVersion "25.0.3"
defaultConfig {
applicationId "com.webdesign.crf.eins"
minSdkVersion minimumSdkVersion
targetSdkVersion 25
versionCode generateVersionCode()
versionName generateVersionName()
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
compile 'com.android.support:design:25.3.1'
compile 'com.android.support:cardview-v7:25.3.1'
compile 'com.android.support:support-v4:25.3.1'
compile 'com.android.volley:volley:1.0.0'
testCompile 'junit:junit:4.12'
}

private Integer generateVersionCode() {
return ext.minimumSdkVersion * 10000000 + ext.versionMajor * 10000 + ext.versionMinor * 100 + ext.versionPatch
}

private String generateVersionName() {
String versionName = "${versionMajor}.${versionMinor}.${versionPatch}"
if (ext.versionClassifier == null) {
if (isSnapshot) {
versionClassifier = "SNAPSHOT"
}
}

if (ext.versionClassifier != null) {
versionName += "-" + versionClassifier
}
return versionName;
}

private String setVersionNumberByTag() {
/*
* Gets the version name from the latest Git tag
*/
def stdout = new ByteArrayOutputStream()
exec {
commandLine 'git', 'describe', '--tags'
standardOutput = stdout
}
def String verByGit = stdout.toString().trim()
def (major, minor, patch) = verByGit.tokenize(".");
ext.versionMajor = Integer.parseInt(major);
ext.versionMinor = Integer.parseInt(minor);
ext.versionPatch = Integer.parseInt(patch);
}

In gradle files groovy is used. That means its not possible to use someString.split("."); like normal in java. I found out, that def (major, minor, patch) = verByGit.tokenize("."); did the trick.

Gradle script to autoversion and include the commit hash in Android

I encountered a similar problem, but did not want to modify the versionName to include the git hash. We wanted to keep that as something like 1.2.2, but still have the possibility of displaying the git hash in the UI.

I modified the code from the other answer here to use the buildConfigField task to generate a BuildConfig.GitHash value that can be referenced in the Java code.

Add this above the android section of your module's build.gradle file:

def getGitHash = { ->
def stdout = new ByteArrayOutputStream()
exec {
commandLine 'git', 'rev-parse', '--short', 'HEAD'
standardOutput = stdout
}
return stdout.toString().trim()
}

Then add the following line to the defaultConfig section of the android section of the build.gradle, i.e. below versionName:

buildConfigField "String", "GitHash", "\"${getGitHash()}\""

This generates the following line in the auto-generated BuildConfig.java file:

// Fields from default config.
public static final String GitHash = "e61af97";

Now you can get the git hash in your Java code with BuildConfig.GitHash.

Auto Version numbering your Android App using Git and Eclipse

This solution was developed on Linux. I'm sure skilled Windows & Mac developers can adapt it to their platforms, but I am not such a developer. Linux is where my skill set lives.

Git has a nice feature in the git describe --dirty command. It scans back down the commit log and finds the tag and then builds a version string from there. If this is a "production build" where the last commit has been tagged and all files have been checked in then that is your version string. If this is a development build then the last tag is appended with the number of additional commits and an abbreviated hash code. The --dirty flag is just the cherry on the icing on the cake: it appends the word dirty if there are any modified files not committed yet. This is perfect for your android:versionName attribute in the manifest file.

For the android:versionCode a number is required. This needs to clock for releases but not for development build, and as every release will have a tag with a version string I simply count these. I always tag my versions in the form v<major>.<minor>[.<patch>] where <major>, <minor> and <patch> are just numbers. So counting tags that start with a lower case 'v' followed with a digit are counted is all thats really needed here.

After trailing with a template manifest file I discovered that the best way was to simply use the AndroidManifest.xml file in the project base, edited using the stream editor sed and deposit the result in bin/AndroidManifest.xml.

So I developed the script below, placed it in a scripts folder at the same level as my projects (so that they can all share the same script) and then configured a custom builder in Eclipse.

There is the script which I called version.sh:

#/bin/bash

echo "Auto Version: `pwd`"

CODE=`git tag | grep -c ^v[0-9]`
NAME=`git describe --dirty | sed -e 's/^v//'`
COMMITS=`echo ${NAME} | sed -e 's/[0-9\.]*//'`

if [ "x${COMMITS}x" = "xx" ] ; then
VERSION="${NAME}"
else
BRANCH=" (`git branch | grep "^\*" | sed -e 's/^..//'`)"
VERSION="${NAME}${BRANCH}"
fi

echo " Code: ${CODE}"
echo " Ver: ${VERSION}"

cat AndroidManifest.xml | \
sed -e "s/android:versionCode=\"[0-9][0-9]*\"/android:versionCode=\"${CODE}\"/" \
-e "s/android:versionName=\".*\"/android:versionName=\"${VERSION}\"/" \
> bin/AndroidManifest.xml

exit 0

To configure the builder here are the steps:

1). Right click the project base and select "Properties" and then "Builders".

2). Hit the "New" button and select the "Program" option.

3). Name your version something like "<project> Auto Version". This string needs to be unique across all projects.

4). Configure the "Main" tab as follows:

4a). In the "Location" section use "Browse File System" and navigate and select the script file.

4b). In the "Working directory" section use "Browse Workspace" to select the project.

5). Leave the "Refresh resources upon completion" unchecked in the "Refresh" tab.

6). Don't set any variables up in the "Environment" tab.

7). In the "Build Options" tab:

7a). Make sure that "During manual builds" is ticked, and

7b). That "During auto builds" is also ticked.

7c). I now have everything else left unselected. I don't even allocate a console to it. The eagle eyed out there may have spotted that the script does output some information, but now I've got it working I just want the thing to run silently without bothering me.

8). Okay the build settings and then position your builder between "Android Pre-Compile" and "Java Builder".

Go back to developing your apps safe in the knowledge that they are being properly versioned, and check out your app's info. Isn't that version number cool. :-)

Steve

How do I include a Gradle auto-incremented version number in my code's repo?

I have previously solved your proposed issue in 2 separate ways. Firstly, by using a Gradle plugin, similar to the nebula-release plugin @sghill linked above.

However, that plugin worked by counting all the commits for a patch version, configured major and minor via a Gradle extension and appended metadata information, e.g. branch name and whether it was dirty or not. That seemed too complex a workflow for my needs and was not useful for projects that didn't use Gradle. For your case, however, it's a quick off the shelf solution.

In my case, all I needed were unique version numbers automatically tagged upon a PR being merged into develop or master, and unique version numbers for each commit on a branch. To do so, I did use Git tags and wrote a script for it.

The 3 cases for versioning were:

  • a new, uninitialised repo => generate a new version.json file with a default branch (master, but can be changed per repo, a major and a minor version to configure those bumps)
  • any commit merged into the default branch generates a new version and tags it. If the major or minor versions in version.json have been changed, a major or minor bump occurs and the patch version is reset to 0.
  • unique versions on branches: the output of git describe and the branch name, e.g. 0.1.0-x-branch-name where x is the number of commits ahead of the default branch.

You can find it here and the docker container here

As for configuring Jenkins to have write access to the repo, have you followed the instructions here? This is what I've been successfully doing in all of my repos:
Git push using jenkins credentials from declarative pipeline

Autoincrement VersionCode with gradle extra properties

I would like to read the versionCode from an external file

I am sure that there are any number of possible solutions; here is one:

android {
compileSdkVersion 18
buildToolsVersion "18.1.0"

def versionPropsFile = file('version.properties')

if (versionPropsFile.canRead()) {
def Properties versionProps = new Properties()

versionProps.load(new FileInputStream(versionPropsFile))

def code = versionProps['VERSION_CODE'].toInteger() + 1

versionProps['VERSION_CODE']=code.toString()
versionProps.store(versionPropsFile.newWriter(), null)

defaultConfig {
versionCode code
versionName "1.1"
minSdkVersion 14
targetSdkVersion 18
}
}
else {
throw new GradleException("Could not read version.properties!")
}

// rest of android block goes here
}

This code expects an existing version.properties file, which you would create by hand before the first build to have VERSION_CODE=8.

This code simply bumps the version code on each build -- you would need to extend the technique to handle your per-flavor version code.

You can see the Versioning sample project that demonstrates this code.

Gradle versioning with Git

If gradle is having trouble finding the location of git, I would try using the full path for git executable, or alternatively setting the workingDirvalue for Exec.

exec {
workingDir '../path/to/git/bin'
commandLine 'cmd', '/c', 'git',...
}

Increment Android build number in Continuous Integration

I use none of the above — like you, I don't want to alter the repo for each build, nor any files.

Jenkins has an always-increasing value for each build, exposed via the BUILD_NUMBER environment variable.

In Gradle, I generate the versionCode value programmatically at build time, using the BUILD_NUMBER value to ensure that the versionCode is always higher than the previous build.

A snippet of my build.gradle:

// Used to set the package version name and version code
ext.versionMajor = 1
ext.versionMinor = 2

android {
defaultConfig {
versionName computeVersionName()
versionCode computeVersionCode()
}
}

// Will return "1.2" in this example
def computeVersionName() {
// Basic <major>.<minor> version name
return String.format('%d.%d', versionMajor, versionMinor)
}

// Will return 120042 for Jenkins build #42
def computeVersionCode() {
// Major + minor + Jenkins build number (where available)
return (versionMajor * 100000)
+ (versionMinor * 10000)
+ Integer.valueOf(System.env.BUILD_NUMBER ?: 0)
}

So I only need to update the two values at the top when making a release build. For all other build types, I can let Gradle/Jenkins automatically set the versionCode and then upload to Google Play.

This also means, for any alpha version listed on the Play Store, or by inspecting an APK, I can see straight away which Jenkins build it came from, and from there the git commit.



Related Topics



Leave a reply



Submit