How to autoincrement versionCode in Android Gradle
AndroidBuildAndroid ManifestGradleAuto IncrementAndroid Problem Overview
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
- create versionCode file, read number from it, increase it and write it back to the file
- 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?
Android Solutions
Solution 1 - Android
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.
Solution 2 - Android
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'
}
}
Solution 3 - Android
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
...
}
Solution 4 - Android
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
}
Solution 5 - Android
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)
}
Solution 6 - Android
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
Solution 7 - Android
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)
}
}
Solution 8 - Android
##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):
1
period for Regex
Warning: Must contain 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" >
Solution 9 - Android
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'
}
}
Solution 10 - Android
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)
}
Solution 11 - Android
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"
}
Solution 12 - Android
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
}
}
Solution 13 - Android
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
]}
}