Getting to Know Gradle: Auto-Increment Version and Signing Releases

After attending AnDevCon 2013 in San Francisco a few weeks ago and creating my own application, I decided it was time to become more familiar with gradle and the build process. I set out to accomplish two things:

  1. Auto-increment the version when a new build is created
  2. Automatically sign the release version of the code with my keystore information

Auto-Increment

I did some searching around the web for the best way to auto-increment version numbers using the gradle build process. The best article I found was on Stack Overflow, but, not being very familiar with gradle or Ruby, I wasn’t exactly sure where to make the changes.

There are actual two version numbers that need to be incremented:

  1. versionCode is the integer that is used by the app as a relative comparison to other versions. This value is not seen by the user.
  2. versionName is the String that is used to identify the version to users and appears in the app info screen.

I eventually figured it out (with some help from my fellow BlueFletch developers) and came up with the following:

build.gradle

import java.util.regex.Pattern
 
buildscript {
    repositories {
        ...
 
    }
    dependencies {
        ...
    }
}
 
repositories {
    ...
}
 
android {
    compileSdkVersion 18
    buildToolsVersion "17.0.0"
 
    defaultConfig {
        minSdkVersion 11
        targetSdkVersion 18
        ...
    }
 
    signingConfigs {
        release
    }
 
    buildTypes {
        release {
            ...
        }
        debug {
        }
    }
}
 
dependencies {
    ...
}
 
task incrementVersionCode << {
    println(":incrementVersionCode - Incrementing Version Code...")
    def manifestFile = file("src/main/AndroidManifest.xml")
    def patternVersionCode = Pattern.compile("versionCode=\"(\\d+)\"")
    def manifestText = manifestFile.getText()
    def matcherVersionCode = patternVersionCode.matcher(manifestText)
    matcherVersionCode.find()
    def mVersionCode = Integer.parseInt(matcherVersionCode.group(1))
    def mNextVersionCode = mVersionCode + 1
    def manifestContent = matcherVersionCode.replaceAll("versionCode=\"" + mNextVersionCode + "\"")
    println(":incrementVersionCode - current versionCode=" + mVersionCode);
    println(":incrementVersionCode - next versionCode=" + mNextVersionCode);
    manifestFile.write(manifestContent)
}
 
task incrementVersionName << {
    println(":incrementVersionName - Incrementing Version Name...")
    def manifestFile = file("src/main/AndroidManifest.xml")
    def patternVersionNumber = Pattern.compile("versionName=\"(\\d+)\\.(\\d+)\\.(\\d+)\"")
    def manifestText = manifestFile.getText()
    def matcherVersionNumber = patternVersionNumber.matcher(manifestText)
    matcherVersionNumber.find()
    def majorVersion = Integer.parseInt(matcherVersionNumber.group(1))
    def minorVersion = Integer.parseInt(matcherVersionNumber.group(2))
    def pointVersion = Integer.parseInt(matcherVersionNumber.group(3))
    def mVersionName = majorVersion + "." + minorVersion + "." + pointVersion
    def mNextVersionName = majorVersion + "." + minorVersion + "." + (pointVersion + 1)
    def manifestContent = matcherVersionNumber.replaceAll("versionName=\"" + mNextVersionName + "\"")
    println(":incrementVersionName - current versionName=" + mVersionName);
    println(":incrementVersionName - new versionName=" + mNextVersionName);
    manifestFile.write(manifestContent)
}
 
task release << {
    println(":release - Build and Version Increment")
}
 
task debug << {
    println(":debug - Build")
}
 
incrementVersionName.mustRunAfter build
incrementVersionCode.mustRunAfter build
 
debug.dependsOn assembleDebug
// Uncomment if you want to increment the versionCode and/or versionName when using the debug build
//debug.dependsOn incrementVersionCode
//debug.dependsOn incrementVersionName
 
release.dependsOn assembleRelease
release.dependsOn incrementVersionCode
release.dependsOn incrementVersionName

The above code does a few things:

  1. Creates a new task for incrementing the versionCode
  2. Creates a new task for incrementing the versionName
  3. Creates a new task for a debug build
  4. Creates a new task for a release build
  5. Sets the dependency saying that each of those two new tasks must run after the build process
  6. Sets the dependencies for the debug task to depend on assembleDebug (the pre-existing task for creating an debug build) and optionally a dependency for each of the incrementVersion* tasks – you can uncomment these to increment the versions for the debug builds as well
  7. Sets the dependencies for the debug task to depend on assembleRelease (the pre-existing task for creating a release build) and for each of the incrementVersion* tasks

The incrementVersionCode task takes the existing versionCode in AndroidManifest.xml and increments it for the next release.

For example: If before the build the AndroidManfist.xml has versionCode=7, after the build versionCode=8.

The incrementVersionName task takes the existing versionName in AndroidManifest.xml and increments the point number for the next release. The AndroidManifest.xml files versionName needs to be in the format [major].[minor].[point].

For example: If before the build the AndroidManfist.xml has versionName=1.3.6, after the build versionName=1.3.7

Signing Release APK

The next thing I wanted to figure out was how to put the .apk signing process into the gradle build process. Once again, Stack Overflow provided some guidance, but I was having trouble getting things working.

The preferred path I wanted to take was to have the keystore and passwords referenced in a properties file. Here’s what I came up with:

build.gradle

android {
    compileSdkVersion 18
    buildToolsVersion "17.0.0"
 
    defaultConfig {
        minSdkVersion 11
        targetSdkVersion 18
 
        testPackageName "com.bryankrosenbaum.later.test"
        testInstrumentationRunner "android.test.InstrumentationTestRunner"
    }
 
    signingConfigs {
        release
    }
 
    buildTypes {
        release {
            signingConfig signingConfigs.release
        }
        debug {
        }
    }
}
 
def Properties props = new Properties()
def propFile = new File("signing.properties")
if (propFile.canRead()) {
    props.load(new FileInputStream(propFile))
 
    if (props!= null
            && props.containsKey("KEYSTORE_FILE")
            && props.containsKey("KEYSTORE_PASSWORD")
            && props.containsKey("KEY_ALIAS")
            && props.containsKey("KEY_PASSWORD")) {
 
        def keystoreFile = new File(props["KEYSTORE_FILE"])
        if (keystoreFile.canRead()) {
            android.signingConfigs.release.storeFile = keystoreFile
            android.signingConfigs.release.storePassword = props["KEYSTORE_PASSWORD"]
            android.signingConfigs.release.keyAlias = props["KEY_ALIAS"]
            android.signingConfigs.release.keyPassword = props["KEY_PASSWORD"]
        }
        else {
            println("keystore file not found: " + props["KEYSTORE_FILE"])
            android.buildTypes.release.signingConfig = null
        }
    }
    else {
        println("signing.properties found but some entries missing")
        android.buildTypes.release.signingConfig = null
    }
} else {
    println("signing.properties not found")
    android.buildTypes.release.signingConfig = null
}

signing.properties

KEYSTORE_FILE=path\\to\\mykeystore.keystore
KEYSTORE_PASSWORD=******
KEY_ALIAS=******
KEY_PASSWORD=******
WARNING ABOUT signing.properties

You’ll probably want to make sure that you don’t check in your signing.properties file into version control with your real passwords inside, otherwise anyone can build a release of your app, submit it to the google play store, etc.

What I did is create a signings.properties file with placeholders like the above code shows. Then on the machine that is doing the builds I run the following for git:

git update-index --assume-unchanged path/to/signing.properties

This will tell git to ignore any changes to that file. After that you can put the real values in for those passwords in the signing.properties file on the build machine.

How to Use

To use these features, run gradle release, which will increment the versionCode and versionName, and will sign the .apk.

To see this in action, check out my build.gradle file in my “Later” Android project: https://github.com/bryanro/later-android/blob/master/Later/build.gradle
The signing.properties file is here.

Posted in Code Snippets Tagged with: , ,
2 comments on “Getting to Know Gradle: Auto-Increment Version and Signing Releases
  1. um says:

    Hi Bryan,
    I have tried to increase the codeVersion even in debug.
    But not thing happen. Could you please give me a suggestion?
    Here are my modified code

    import java.util.regex.Pattern

    buildscript {

    repositories {
    mavenLocal()
    mavenCentral()
    }

    dependencies {
    classpath ‘com.android.tools.build:gradle:0.12.+’
    }
    }

    apply plugin: ‘com.android.application’

    android {
    compileSdkVersion 19
    buildToolsVersion ‘20.0.0’

    defaultConfig {
    minSdkVersion 17
    targetSdkVersion 19
    versionCode 1
    versionName “1.0”
    }

    buildTypes {
    release {
    runProguard false
    proguardFiles getDefaultProguardFile(‘proguard-android.txt’), ‘proguard-rules.txt’
    }
    debug{

    }
    }
    }

    dependencies {
    //compile fileTree(dir: ‘libs’, include: [‘*.jar’])
    compile fileTree(dir: ‘libs’, include: [‘android-support-v4.jar’])
    compile fileTree(dir: ‘libs’, include: [‘achartengine-1.1.0.jar’])
    }

    task incrementVersionCode << {
    println(":incrementVersionCode – Incrementing Version Code…")
    def manifestFile = file("src/main/AndroidManifest.xml")
    def patternVersionCode = Pattern.compile("versionCode=\"(\\d+)\"")
    def manifestText = manifestFile.getText()
    def matcherVersionCode = patternVersionCode.matcher(manifestText)
    matcherVersionCode.find()
    def mVersionCode = Integer.parseInt(matcherVersionCode.group(1))
    def mNextVersionCode = mVersionCode + 1
    def manifestContent = matcherVersionCode.replaceAll("versionCode=\"" + mNextVersionCode + "\"")
    println(":incrementVersionCode – current versionCode=" + mVersionCode);
    println(":incrementVersionCode – next versionCode=" + mNextVersionCode);
    manifestFile.write(manifestContent)
    }

    task incrementVersionName << {
    println(":incrementVersionName – Incrementing Version Name…")
    def manifestFile = file("src/main/AndroidManifest.xml")
    def patternVersionNumber = Pattern.compile("versionName=\"(\\d+)\\.(\\d+)\\.(\\d+)\"")
    def manifestText = manifestFile.getText()
    def matcherVersionNumber = patternVersionNumber.matcher(manifestText)
    matcherVersionNumber.find()
    def majorVersion = Integer.parseInt(matcherVersionNumber.group(1))
    def minorVersion = Integer.parseInt(matcherVersionNumber.group(2))
    def pointVersion = Integer.parseInt(matcherVersionNumber.group(3))
    def mVersionName = majorVersion + "." + minorVersion + "." + pointVersion
    def mNextVersionName = majorVersion + "." + minorVersion + "." + (pointVersion + 1)
    def manifestContent = matcherVersionNumber.replaceAll("versionName=\"" + mNextVersionName + "\"")
    println(":incrementVersionName – current versionName=" + mVersionName);
    println(":incrementVersionName – new versionName=" + mNextVersionName);
    manifestFile.write(manifestContent)
    }

    task release << {
    println(":release – Build and Version Increment")
    }

    task debug << {
    println(":debug – Build")
    }

    //incrementVersionName.mustRunAfter build
    incrementVersionCode.mustRunAfter build

    debug.dependsOn assembleDebug
    // Uncomment if you want to increment the versionCode and/or versionName when using the debug build
    debug.dependsOn incrementVersionCode
    debug.dependsOn incrementVersionName

    release.dependsOn assembleRelease
    release.dependsOn incrementVersionCode
    release.dependsOn incrementVersionName

    • How are you kicking off the build? Are you running gradle debug or something else?
      I also noticed you commented out incrementVersionName.mustRunAfter build – that shouldn’t impact anything, but you may want to try uncommenting it.
      Finally, were you able to get the version to increment with gradle release?

Leave a Reply to um Cancel reply

Your email address will not be published. Required fields are marked *

*