Cómo autoincrementar versionCode en Android Gradle

78

Estoy experimentando con un nuevo sistema de compilación de Android basado en Gradle y estoy pensando, ¿cuál es la mejor manera de aumentar automáticamente versionCode con él? Estoy pensando en dos opciones

  1. crear el archivo versionCode, leer el número de él, aumentarlo y escribirlo de nuevo en el archivo
  2. analizar AndroidManifest.xml, leer versionCode de él, aumentarlo y escribirlo de nuevo en AndroidManifest.xml

¿Existe alguna solución más sencilla o adecuada?

¿Alguien ha usado una de las opciones de mentiod y podría compartirla conmigo?

sealskej
fuente
Leyendo de un version.propertiesarchivo stackoverflow.com/a/21405744
Dori

Respuestas:

58

Me he decidido por la segunda opción: analizar AndroidManifest.xml. Aquí está el fragmento de trabajo.

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'
    }
}

versionCodese lanza para versiones de lanzamiento en este caso. Para aumentarlo para las compilaciones de depuración, cambie la ecuación task.name en la task.whenTaskAddeddevolución de llamada.

sealskej
fuente
5
una pregunta, ¿cómo integrar ese código en el sistema de compilación Cradle?
LiangWang
Además, una respuesta algo más limpia aquí no modifica XML en su lugar, pero mantiene el número de versión en un archivo de accesorios: stackoverflow.com/questions/21405457/…
Paul Cantrell
1
Simplemente cambie el archivo ("AndroidManifest.xml") por el archivo ("src / main / AndroidManifest.xml") si está utilizando la nueva estructura del proyecto.
Elhanan Mishraky
3
¿Qué pasa si no configuro versionCode en mi AndroidManifest.xml?
IgorGanapolsky
@Jacky Si todavía no tienen una solución alternativa, vea mi respuesta: stackoverflow.com/a/39619297/1150251
iStar
39

Estoy usando este código para actualizar tanto versionCode como versionName, usando un esquema "major.minor.patch.build".

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'
    }
}
Andres
fuente
1
Olvidó importar el patrón en la parte superior: import java.util.regex.Pattern
Mic92
1
Sé que este es un poco viejo, pero oye, no está de más preguntar, pero intenté usar el fragmento anterior, recibo este error. ¿ Error:(48) Execution failed for task ':app:incrementVersionName'. > No match foundAlguna idea?
acrichm
1
@acrichm: asegúrese de establecer android:versionName="0.0.0.1"en el manifiesto, de lo contrario, no se esperará un número de coincidencias
Jay Wick
Funciona perfectamente, pero en BuildConfig.java versionName está vacío, público estático final String VERSION_NAME = "";
Al-Mothafar
Tenga en cuenta que "Instant Run" en Android Studio sería más lento porqueFull recompilation is required because 'BuildConfig.java' was changed.
Ivan Chau
36

no parece ser la configuración exacta que estás usando, pero en mi caso las compilaciones están siendo ejecutadas por jenkins y quería usar su $ BUILD_NUMBER como el código de versión de la aplicación. lo siguiente hizo el truco para mí allí.

defaultConfig {
    ...
    versionCode System.getenv("BUILD_NUMBER") as Integer ?: 9999
    ...
}
usuario2399268
fuente
¿Qué es System.genenv?
IgorGanapolsky
En realidad, esto me ayudó mucho al usar Jenkins. Puede usar este complemento para crear una variable de entorno con el número de compilación y leerla System.getenv()en el gradle.
Lamorak
19

Estoy usando la marca de tiempo para el código de versión:

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

defaultConfig {
    minSdkVersion 10
    targetSdkVersion 21
    versionCode code
}
thanhbinh84
fuente
1
esto es simple y genial. pulgar hacia arriba
Євген Гарастович
7

Si tiene el código de la versión en el archivo build.gradle, use el siguiente fragmento:

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)
}
Elhanan Mishraky
fuente
Ese patrón no coincide con el versionCode.
lagos
¿Puedes publicar tu build.gradle?
Elhanan Mishraky
Mi build.gradle es un archivo de construcción normal. La línea VersionCode se parece a versionCode 18 . Incluso si ejecuté su expresión regular en un tester ( "versionCode \d+"), no coincidirá.
lagos
El error fue ese espacio en blanco más. Android Studio usa dos espacios en blanco en versionCodey el dígito, ya que se alineará con el texto en versionName.
lagos
5

Gradle Advanced Build Version es un complemento para Android que permite generar versionCode y versionName automáticamente. hay mucha personalización. aquí puede encontrar más información al respecto https://github.com/moallemi/gradle-advanced-build-version

moallemi
fuente
nota: esto todavía no admite el incremento automático del nombre de la versión
hooby3dfx
4

Para tener en cuenta los sabores de los productos y los tipos de compilación y usar la lógica de @ sealskej para analizar el manifiesto:

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)
    }
}
Max Ch
fuente
1
¿Dónde necesitamos escribir esto ... quiero decir en qué clase / archivo se debe agregar este código?
ChArAnJiT
esto es parte de build.gradle ya que es parte del proceso de construcción
Max Ch
3

Incrementar la tarea de código de versión (entero):

Esto funciona incrementando el código de versión 1, por ejemplo:

  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)
}

Incrementar la tarea de VersionName (cadena):

Advertencia: debe contener un 1período para Regex

Esto funciona incrementando el Nombre de la versión 0.01, por ejemplo: Puede modificar y cambiar fácilmente su incremento o agregar más dígitos.

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)
}

Antes de:

<?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" >

Después:

<?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" >
Jared Burrows
fuente
3

Si escribe su archivo versionCodein gradle.build(la mayoría de los casos actualmente), aquí hay una solución. Un poco estúpido (actualizar "self"), ¡pero funciona!

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'
    }
}
yo empiezo
fuente
2

Para agregar a la publicación de @ sealskej, así es como puede actualizar tanto el código de su versión como el nombre de la versión (aquí supongo que su versión principal y secundaria son ambas 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)
}
Karim Varela
fuente
1

que hay de esto agregar a build.gradle (módulo de la aplicación)

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"
}
Karoly
fuente
2
Gran pensamiento, sino(20160129 + 000000000) < (20160128 + 125959999)
C0D3LIC1OU5
0

Entonces, mientras buscaba la mayor parte de la solución, eran agradables pero no lo suficiente, así que escribí esto, un incremento por implementación múltiple:

Esto incrementará la compilación al compilar versiones de depuración e incrementará el punto y el código de versión al implementar.

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
    }
}
TacB0sS
fuente
-1

Mi enfoque es leer el archivo de manifiesto de la carpeta de compilación y sacar buildVersion de allí, luego borrar una carpeta. Cuando la tarea crea un nuevo manifiesto, mi variable buildVersion incrementada ya está allí.

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
    ]}
}
Katoteshi Fuku
fuente
Esto no proporciona una respuesta a la pregunta. Una vez que tenga suficiente reputación , podrá comentar en cualquier publicación ; en su lugar, proporcione respuestas que no requieran aclaración por parte del autor de la pregunta . - De la crítica
MLavoie
@MLavoie compruébalo ahora mi fragmento de WIP
Katoteshi Fuku