How to autoincrement versionCode in Android Gradle

I'm experimenting with new Android build system based on Gradle and I'm thinking, what is the best way to autoincrease versionCode with it. I am thinking about two options

  1. create versionCode file, read number from it, increase it and write it back to the file
  2. parse AndroidManifest.xml, read versionCode from it, increase it and write it back to the AndroidManifest.xml

Is there any more simple or suitable solution?

Has anyone used one of mentiod options and could share it with me?

47220 次浏览

I have decided for second option - to parse AndroidManifest.xml. Here is working snippet.

task('increaseVersionCode') << {
def manifestFile = file("AndroidManifest.xml")
def pattern = Pattern.compile("versionCode=\"(\\d+)\"")
def manifestText = manifestFile.getText()
def matcher = pattern.matcher(manifestText)
matcher.find()
def versionCode = Integer.parseInt(matcher.group(1))
def manifestContent = matcher.replaceAll("versionCode=\"" + ++versionCode + "\"")
manifestFile.write(manifestContent)
}


tasks.whenTaskAdded { task ->
if (task.name == 'generateReleaseBuildConfig') {
task.dependsOn 'increaseVersionCode'
}
}

versionCode is released for release builds in this case. To increase it for debug builds change task.name equation in task.whenTaskAdded callback.

To add on to @sealskej's post, this is how you can update both your version code and version name (Here I'm assuming your major and minor version are both 0):

task('increaseVersion') << {
def manifestFile = file("AndroidManifest.xml")
def patternVersionCode = Pattern.compile("versionCode=\"(\\d+)\"")
def manifestText = manifestFile.getText()
def matcherVersionCode = patternVersionCode.matcher(manifestText)
matcherVersionCode.find()
def versionCode = Integer.parseInt(matcherVersionCode.group(1))
def manifestContent = matcherVersionCode.replaceAll("versionCode=\"" + ++versionCode + "\"")


manifestFile.write(manifestContent)


def patternVersionNumber = Pattern.compile("versionName=\"0.0.(\\d+)\"")
manifestText = manifestFile.getText()
def matcherVersionNumber = patternVersionNumber.matcher(manifestText)
matcherVersionNumber.find()
def versionNumber = Integer.parseInt(matcherVersionNumber.group(1))
manifestContent = matcherVersionNumber.replaceAll("versionName=\"0.0." + ++versionNumber + "\"")
manifestFile.write(manifestContent)
}

To take both product flavors and build types into account and using @sealskej's logic for parsing manifest:

android.applicationVariants.all { variant ->
/* Generate task to increment version code for release */
if (variant.name.contains("Release")) {
def incrementVersionCodeTaskName = "increment${variant.name}VersionCode"
task(incrementVersionCodeTaskName) << {
if (android.defaultConfig.versionCode == -1) {
def manifestFile = file(android.sourceSets.main.manifest.srcFile)
def pattern = Pattern.compile("versionCode=\"(\\d+)\"")
def manifestText = manifestFile.getText()
def matcher = pattern.matcher(manifestText)
matcher.find()
def versionCode = Integer.parseInt(matcher.group(1))
android.defaultConfig.versionCode = versionCode + 1
def manifestContent = matcher.replaceAll("versionCode=\"" + android.defaultConfig.versionCode + "\"")
manifestFile.write(manifestContent)
}
}
def hookTask = variant.generateBuildConfig
hookTask.dependsOn(incrementVersionCodeTaskName)
}
}

I'm using this code to update both versionCode and versionName, using a "major.minor.patch.build" scheme.

import java.util.regex.Pattern


task('increaseVersionCode') << {
def manifestFile = file("src/main/AndroidManifest.xml")
def pattern = Pattern.compile("versionCode=\"(\\d+)\"")
def manifestText = manifestFile.getText()
def matcher = pattern.matcher(manifestText)
matcher.find()
def versionCode = Integer.parseInt(matcher.group(1))
def manifestContent = matcher.replaceAll("versionCode=\"" + ++versionCode + "\"")
manifestFile.write(manifestContent)
}


task('incrementVersionName') << {
def manifestFile = file("src/main/AndroidManifest.xml")
def patternVersionNumber = Pattern.compile("versionName=\"(\\d+)\\.(\\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 buildVersion = Integer.parseInt(matcherVersionNumber.group(4))
def mNextVersionName = majorVersion + "." + minorVersion + "." + pointVersion + "." + (buildVersion + 1)
def manifestContent = matcherVersionNumber.replaceAll("versionName=\"" + mNextVersionName + "\"")
manifestFile.write(manifestContent)
}


tasks.whenTaskAdded { task ->
if (task.name == 'generateReleaseBuildConfig' || task.name == 'generateDebugBuildConfig') {
task.dependsOn 'increaseVersionCode'
task.dependsOn 'incrementVersionName'
}
}

it doesn't seem to be the exact setup you're using, but in my case the builds are being run by jenkins and i wanted to use its $BUILD_NUMBER as the app's versionCode. the following did the trick for me there.

defaultConfig {
...
versionCode System.getenv("BUILD_NUMBER") as Integer ?: 9999
...
}

Increment VersionCode Task(Integer):

This works by incrementing the Version Code by 1, for example:

  android:versionCode="1"
1 + 1 = 2
import java.util.regex.Pattern


task incrementVersionCode << {
def manifestFile = file('AndroidManifest.xml')
def matcher = Pattern.compile('versionCode=\"(\\d+)\"')
.matcher(manifestFile.getText())
matcher.find()
def manifestContent = matcher.replaceAll('versionCode=\"' +
++Integer.parseInt(matcher.group(1)) + '\"')
manifestFile.write(manifestContent)
}

Increment VersionName Task(String):

Warning: Must contain 1 period for Regex

This works by incrementing the Version Name by 0.01, for example: You can easily modify and change your increment or add more digits.

android:versionName="1.0"
1.00 + 0.01 -> 1.01
1.01 + 0.01 -> 1.02
1.10 + 0.01 -> 1.11
1.99 + 0.01 -> 2.0
1.90 + 0.01 -> 1.91
import java.util.regex.Pattern


task incrementVersionName << {
def manifestFile = file('AndroidManifest.xml')
def matcher = Pattern.compile('versionName=\"(\\d+)\\.(\\d+)\"')
.matcher(manifestFile.getText())
matcher.find()
def versionName = String.format("%.2f", Integer
.parseInt(matcher.group(1)) + Double.parseDouble("." + matcher
.group(2)) + 0.01)
def manifestContent = matcher.replaceAll('versionName=\"' +
versionName + '\"')
manifestFile.write(manifestContent)
}

Before:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.exmaple.test"
android:installLocation="auto"
android:versionCode="1"
android:versionName="1.0" >

After:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.exmaple.test"
android:installLocation="auto"
android:versionCode="2"
android:versionName="1.01" >

Gradle Advanced Build Version is a plugin for Android that makes generating versionCode and versionName automatically. there are lots of customization. here you can find more info about it https://github.com/moallemi/gradle-advanced-build-version

If you are holding the version code in the build.gradle file use the next snippet:

import java.util.regex.Pattern
task('increaseVersionCode') << {
def buildFile = file("build.gradle")
def pattern = Pattern.compile("versionCode\\s+(\\d+)")
def manifestText = buildFile.getText()
def matcher = pattern.matcher(manifestText)
matcher.find()
def versionCode = Integer.parseInt(matcher.group(1))
def manifestContent = matcher.replaceAll("versionCode " + ++versionCode)
buildFile.write(manifestContent)
}

what about this ? add to build.gradle (app module)

def getBuildVersionCode() {
def date = new Date()
def formattedDate = date.format('yyyyMMdd')
def formattedSeconds = date.format('HHmmssSSS')
def formatInt = formattedDate as int;
def SecondsInt = formattedSeconds as int;
return (formatInt + SecondsInt) as int
}


defaultConfig {
applicationId "com.app"
minSdkVersion 17
targetSdkVersion 22
versionCode getBuildVersionCode()
versionName "1.0"
}

So as I was looking into most of the solution, they were nice but not enough so I wrote this, one increment per multi-deploy:

This will increment the build when compiling debug versions, and increment the point and version code when deploying.

import java.util.regex.Pattern


def incrementVersionName(int length, int index) {
def gradleFile = file("build.gradle")
def versionNamePattern = Pattern.compile("versionName\\s*\"(.*?)\"")
def gradleText = gradleFile.getText()
def matcher = versionNamePattern.matcher(gradleText)
matcher.find()


def originalVersion = matcher.group(1)
def originalVersionArray = originalVersion.split("\\.")
def versionKeys = [0, 0, 0, 0]
for (int i = 0; i < originalVersionArray.length; i++) {
versionKeys[i] = Integer.parseInt(originalVersionArray[i])
}
def finalVersion = ""
versionKeys[index]++;
for (int i = 0; i < length; i++) {
finalVersion += "" + versionKeys[i]
if (i < length - 1)
finalVersion += "."
}


System.out.println("Incrementing Version Name: " + originalVersion + " ==> " + finalVersion)


def newGradleContent = gradleText.replaceAll("versionName\\s*\"(.*?)\"", "versionName \"" + finalVersion + "\"")
gradleFile.write(newGradleContent)
}


def incrementVersionCode() {
def gradleFile = file("build.gradle")
def versionCodePattern = Pattern.compile("versionCode\\s*(\\d+)")
def gradleText = gradleFile.getText()
def matcher = versionCodePattern.matcher(gradleText)
matcher.find()


def originalVersionCode = Integer.parseInt(matcher.group(1) + "")
def finalVersionCode = originalVersionCode + 1;
System.out.println("Incrementing Version Code: " + originalVersionCode + " ==> " + finalVersionCode)


def newGradleContent = gradleText.replaceAll("versionCode\\s*(\\d+)", "versionCode " + finalVersionCode)
gradleFile.write(newGradleContent)
}


task('incrementVersionNameBuild') << {
incrementVersionName(4, 3)
}


task('incrementVersionNamePoint') << {
incrementVersionName(3, 2)
}


task('incrementVersionCode') << {
incrementVersionCode()
}




def incrementedBuild = false
def incrementedRelease = false


tasks.whenTaskAdded { task ->
System.out.println("incrementedRelease: " + incrementedRelease)
System.out.println("incrementedBuild: " + incrementedBuild)
System.out.println("task.name: " + task.name)


if (!incrementedBuild && task.name.matches('generate.*?DebugBuildConfig')) {
task.dependsOn 'incrementVersionNameBuild'
incrementedBuild = true
return
}


if (!incrementedRelease && task.name.matches('generate.*?ReleaseBuildConfig')) {
task.dependsOn 'incrementVersionCode'
task.dependsOn 'incrementVersionNamePoint'
incrementedRelease = true
return
}
}

UPDATE

As google play warning:

The greatest value Google Play allows for versionCode is 2100000000.

We might change the format as below to reduce the risk of reaching limit:

def formattedDate = date.format('yyMMddHH')

ORIGINAL

I am using time stamp for the version code:

def date = new Date()
def formattedDate = date.format('yyMMddHHmm')
def code = formattedDate.toInteger()


defaultConfig {
minSdkVersion 10
targetSdkVersion 21
versionCode code
}

My approach is to read manifest file from build folder and get buildVersion out of there, than I delete a folder. When task creates new manifest, my incremented buildVersion variable is already there.

def versionPattern = "Implementation-Version=(\\d+.\\d+.\\d+.\\d+\\w+)"


task generateVersion (dependsOn : 'start') {
// read build version from previous manifest
def file = file("build/libs/MANIFEST.MF")
if (file.exists()) {
def pattern = Pattern.compile(versionPattern)
def text = file.getText()
def matcher = pattern.matcher(text)
matcher.find()
buildNumber = Integer.parseInt(matcher.group(1))
// increment build version
version = "${majorVer}.${minorVer}.${patchVer}.${++buildNumber}${classifier}_${access}"
}
else
version = "${majorVer}.${minorVer}.${patchVer}.1${classifier}_${access}"
}


task specifyOutputDir (dependsOn : 'generateVersion', type : JavaCompile) {
// create a folder for new build
destinationDir = file("build/${version}/")
}


task clean (dependsOn : 'generateVersion', type : Delete) {
doLast {
delete "build/${version}"
println 'Build directory is deleted'
}
}


task configureJar (dependsOn : 'generateVersion', type : Jar) {
baseName = applicationName
version = project.version
archiveName = "${applicationName}_ver${version}.${extension}"
manifest {[
"Main-Class" : mainClassName,
"Implementation-Title" : name,
"Implementation-Version" : version,
"Access" : access,
"Developer" : developer
]}
}

If you write your versionCode in gradle.build file(most case currently), here is a workaround. A little bit stupid(update "self"), but it works!

import java.util.regex.Pattern


task('increaseVersionCode') << {
def buildFile = file("build.gradle")
def pattern = Pattern.compile("versionCode(\\s+\\d+)")
def buildText = buildFile.getText()
def matcher = pattern.matcher(buildText)
matcher.find()
def versionCode = android.defaultConfig.versionCode
def buildContent = matcher.replaceAll("versionCode " + ++versionCode)
buildFile.write(buildContent)


System.out.println("Incrementing Version Code ===> " + versionCode)
}


tasks.whenTaskAdded { task ->
if (task.name == 'generateReleaseBuildConfig') {
task.dependsOn 'increaseVersionCode'
}
}