web-dev-qa-db-fra.com

Versioning automatique de Android build en utilisant git describe avec Gradle

J'ai effectué des recherches approfondies, mais probablement en raison de la nouveauté de Android Studio et Gradle. Je n'ai trouvé aucune description de la façon de procéder. Je veux faire essentiellement exactement ce qui est décrit dans - ce post , mais avec Android Studio, Gradle et Windows plutôt qu'Eclipse et Linux.

39
Mike Yount

Une manière plus appropriée et plus simple d'obtenir le résultat qui a gagné du terrain ces derniers temps serait d'utiliser intégration gradle git via liaisons Groovy JGit . Comme il utilise JGit, il n'a même pas besoin d'être installé pour fonctionner.

Voici un exemple de base montrant une solution similaire (mais avec des informations supplémentaires dans la chaîne gitVersionName):

buildscript {
  dependencies {
    classpath 'org.ajoberstar:grgit:1.4.+'
  }
}
ext {
  git = org.ajoberstar.grgit.Grgit.open()
  gitVersionCode = git.tag.list().size()
  gitVersionName = "${git.describe()}"
}
Android {
  defaultConfig {
    versionCode gitVersionCode
    versionName gitVersionName
  }
}
[...]

Comme vous pouvez le voir dans documentation de l'API Grgit le décrire l'opération fournit des informations supplémentaires autres que la balise la plus récente accessible dans l'historique:

Recherchez la balise la plus récente accessible depuis HEAD. Si la balise pointe vers la validation, seule la balise est affichée. Sinon, il suffixe le nom de la balise avec le nombre de validations supplémentaires au-dessus de l'objet balisé et le nom d'objet abrégé de la validation la plus récente.

De toute façon, il ne dira pas si l'état est sale ou non. Ces informations peuvent être facilement ajoutées en consultant le état propre du dépôt et en ajoutant une chaîne si elle n'est pas propre.

25
Diego

Placez ce qui suit dans votre fichier build.gradle pour le projet. Il n'est pas nécessaire de modifier directement le manifeste: Google a fourni les crochets nécessaires dans leur configuration.

def getVersionCode = { ->
    try {
        def code = new ByteArrayOutputStream()
        exec {
            commandLine 'git', 'tag', '--list'
            standardOutput = code
        }
        return code.toString().split("\n").size()
    }
    catch (ignored) {
        return -1;
    }
}

def getVersionName = { ->
    try {
        def stdout = new ByteArrayOutputStream()
        exec {
            commandLine 'git', 'describe', '--tags', '--dirty'
            standardOutput = stdout
        }
        return stdout.toString().trim()
    }
    catch (ignored) {
        return null;
    }
}
Android {
    defaultConfig {
        versionCode getVersionCode()
        versionName getVersionName()
    }
}

Notez que si git n'est pas installé sur la machine, ou s'il y a une autre erreur lors de l'obtention du nom/code de version, il sera par défaut celui qui se trouve dans votre manifeste Android.

43
moveaway00

Après avoir vu la réponse de moveaway et le commentaire d'Avinash R sur cette réponse , j'ai fini par utiliser ceci:

apply plugin: 'Android'

def getVersionCode = { ->
    try {
        def stdout = new ByteArrayOutputStream()
        exec {
            commandLine 'git', 'rev-list', '--first-parent', '--count', 'master'
            standardOutput = stdout
        }
        return Integer.parseInt(stdout.toString().trim())
    }
    catch (ignored) {
        return -1;
    }
}

def getVersionName = { ->
    try {
        def stdout = new ByteArrayOutputStream()
        exec {
            commandLine 'git', 'describe', '--tags', '--dirty'
            standardOutput = stdout
        }
        return stdout.toString().trim()
    }
    catch (ignored) {
        return null;
    }
}

Android {
    defaultConfig {
        versionCode getVersionCode()
        versionName getVersionName()
    }
}

J'ai édité le code de moveaway00 pour inclure également le commentaire d'Avinash R: le code de version est maintenant le nombre de validations depuis master, car c'est ce que le code de version est censé être.

Notez que je n'avais pas besoin de spécifier le code de version et le nom de version dans le manifeste, Gradle s'en est occupé.

27
Léo Lam

Encore une autre façon:

https://github.com/gladed/gradle-Android-git-version est un nouveau plugin gradle qui calcule automatiquement les noms et codes de version compatibles avec Android.

Il gère de nombreux cas particuliers qui ne sont pas possibles avec la solution acceptée:

  • balises de version pour plusieurs projets dans le même référentiel
  • codes de version étendus comme 1002003 pour 1.2.3
  • tâches gradle pour extraire facilement les informations de version pour les outils CI
  • etc.

Avertissement: je l'ai écrit.

11
gladed

Voici une autre solution qui nécessite des instructions au lieu de fonctions pour accéder à la ligne de commande. Attention: * solution nix uniquement

def gitSha = 'git rev-parse --short HEAD'.execute([], project.rootDir).text.trim()

// Auto-incrementing commit count based on counting commits to master (Build #543)
def commitCount = Integer.parseInt('git rev-list master --count'.execute([], project.rootDir).text.trim())

// I want to use git tags as my version names (1.2.2)
def gitCurrentTag = 'git describe --tags --abbrev=0'.execute([], project.rootDir).text.trim()

Android {
    compileSdkVersion 22
    buildToolsVersion "22.0.1"

    defaultConfig {
        applicationId "com.some.app"
        minSdkVersion 16
        targetSdkVersion 22
        versionCode commitCount
        versionName gitCurrentTag

        buildConfigField "String", "GIT_SHA", "\"${gitSha}\""

    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-Android.txt'), 'proguard-rules.pro'
        }

    }
}
5
jpotts18

Une autre façon, en utilisant Android Studio (Gradle): Consultez cet article de blog: http://blog.Android-develop.com/2014/09/automatic-versioning-and-increment.html

Voici l'implémentation du blog:

Android {
defaultConfig {
...
    // Fetch the version according to git latest tag and "how far are we from last tag"
    def longVersionName = "git -C ${rootDir} describe --tags --long".execute().text.trim()
    def (fullVersionTag, versionBuild, gitSha) = longVersionName.tokenize('-')
    def(versionMajor, versionMinor, versionPatch) = fullVersionTag.tokenize('.')

    // Set the version name
    versionName "$versionMajor.$versionMinor.$versionPatch($versionBuild)"

    // Turn the version name into a version code
    versionCode versionMajor.toInteger() * 100000 +
            versionMinor.toInteger() * 10000 +
            versionPatch.toInteger() * 1000 +
            versionBuild.toInteger()

    // Friendly print the version output to the Gradle console
    printf("\n--------" + "VERSION DATA--------" + "\n" + "- CODE: " + versionCode + "\n" + 
           "- NAME: " + versionName + "\n----------------------------\n")
...
}

}

3
Sean

Si cela peut vous être utile, j'ai mis en place un exemple de script Gradle qui utilise les balises Git et Git describe pour y parvenir. Voici le code (vous pouvez également le trouver ici ).

1) Créez d'abord un versioning.gradle fichier contenant:

import Java.text.SimpleDateFormat

/**
 * This Gradle script relies on Git tags to generate versions for your Android app
 *
 * - The Android version NAME is specified in the tag name and it's 3 digits long (example of a valid tag name: "v1.23.45")
 *   If the tag name is not in a valid format, then the version name will be 0.0.0 and you should fix the tag.
 *
 * - The Android version CODE is calculated based on the version name (like this: (major * 1000000) + (minor * 10000) + (patch * 100))
 *
 * - The 4 digits version name is not "public" and the forth number represents the number of commits from the last tag (example: "1.23.45.178")
 *
 */

ext {

    getGitSha = {
        return 'git rev-parse --short HEAD'.execute().text.trim()
    }

    getBuildTime = {
        def df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'")
        df.setTimeZone(TimeZone.getTimeZone("UTC"))
        return df.format(new Date())
    }

    /**
     * Git describe returns the following: [GIT_TAG_NAME]-[BUILD_NUMBER]-[GIT_SHA]
     */
    getAndroidGitDescribe = {
        return "git -C ${rootDir} describe --tags --long".execute().text.trim()
    }

    /**
     * Returns the current Git branch name
     */
    getGitBranch = {
        return "git rev-parse --abbrev-ref HEAD".execute().text.trim()
    }

    /**
     * Returns the full version name in the format: MM.mm.pp.ccc
     *
     * The version name is retrieved from the tag name which must be in the format: vMM.mm.pp, example: "v1.23.45"
     */
    getFullVersionName = {
        def versionName = "0.0.0.0"
        def (tag, buildNumber, gitSha) = getAndroidGitDescribe().tokenize('-')
        if (tag && tag.startsWith("v")) {
            def version = tag.substring(1)
            if (version.tokenize('.').size() == 3) {
                versionName = version + '.' + buildNumber
            }
        }
        return versionName
    }

    /**
     * Returns the Android version name
     *
     * Format "X.Y.Z", without commit number
     */
    getAndroidVersionName = {
        def fullVersionName = getFullVersionName()
        return fullVersionName.substring(0, fullVersionName.lastIndexOf('.'))
    }

    /**
     * Returns the Android version code, deducted from the version name
     *
     * Integer value calculated from the version name
     */
    getAndroidVersionCode = {
        def (major, minor, patch) = getAndroidVersionName().tokenize('.')
        (major, minor, patch) = [major, minor, patch].collect{it.toInteger()}
        return (major * 1000000) + (minor * 10000) + (patch * 100)
    }

    /**
     * Return a pretty-printable string containing a summary of the version info
     */
    getVersionInfo = {
        return "\nVERSION INFO:\n\tFull version name: " + getFullVersionName() +
                "\n\tAndroid version name: " + getAndroidVersionName() +
                "\n\tAndroid version code: " + getAndroidVersionCode() +
                "\n\tAndroid Git branch: " + getGitBranch() +
                "\n\tAndroid Git describe: " + getAndroidGitDescribe() +
                "\n\tGit SHA: " + getGitSha() +
                "\n\tBuild Time: " + getBuildTime() + "\n"
    }

    // Print version info at build time
    println(getVersionInfo());
}

2) Modifiez ensuite votre app/build.gradle pour l'utiliser comme ceci:

import groovy.json.StringEscapeUtils;

apply plugin: 'com.Android.application' // << Apply the plugin

Android {

    configurations {
        // ...
    }

    compileSdkVersion 22
    buildToolsVersion "22.0.1"

    defaultConfig {

        minSdkVersion 17
        targetSdkVersion 22

        applicationId "app.example.com"

        versionCode getAndroidVersionCode() // << Use the plugin!
        versionName getAndroidVersionName() // << Use the plugin!

        // Build config constants
        buildConfigField "String", "GIT_SHA", "\"${getGitSha()}\""
        buildConfigField "String", "BUILD_TIME", "\"${getBuildTime()}\""
        buildConfigField "String", "FULL_VERSION_NAME", "\"${getVersionName()}\""
        buildConfigField "String", "VERSION_DESCRIPTION", "\"${StringEscapeUtils.escapeJava(getVersionInfo())}\""
    }

    signingConfigs {
        config {
            keyAlias 'MyKeyAlias'
            keyPassword 'MyKeyPassword'
            storeFile file('my_key_store.keystore')
            storePassword 'MyKeyStorePassword'
        }
    }

    buildTypes {

        debug {
            minifyEnabled false
            debuggable true
        }

        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-Android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.config
            debuggable false
        }

    }

    productFlavors {
       // ...
    }

    dependencies {
        // ...
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_7
        targetCompatibility JavaVersion.VERSION_1_7
    }
}

/**
 * Save a build.info file
 */
task saveBuildInfo {
    def buildInfo = getVersionInfo()
    def assetsDir = Android.sourceSets.main.assets.srcDirs.toArray()[0]
    assetsDir.mkdirs()
    def buildInfoFile = new File(assetsDir, 'build.info')
    buildInfoFile.write(buildInfo)
}

gradle.projectsEvaluated {
    assemble.dependsOn(saveBuildInfo)
}

La partie la plus importante est d'appliquer le plugin

apply plugin: 'com.Android.application'

Et puis utilisez-le pour le Android

versionCode getAndroidVersionCode()
versionName getAndroidVersionName()
3
Soloist

Sur la base de réponse de Léo Lam et de mes précédentes explorations sur la même solution pour fourmi, j'ai conçu un purement multiplateforme solution utilisant jgit:

(original source )

Fichier: git-version.gradle

buildscript {
    dependencies {
        //noinspection GradleDynamicVersion
        classpath "org.Eclipse.jgit:org.Eclipse.jgit:4.1.1.+"
    }
    repositories {
        jcenter()
    }
}
import org.Eclipse.jgit.api.Git
import org.Eclipse.jgit.revwalk.RevWalk
import org.Eclipse.jgit.storage.file.FileRepositoryBuilder

import static org.Eclipse.jgit.lib.Constants.MASTER

def git = Git.wrap(new FileRepositoryBuilder()
        .readEnvironment()
        .findGitDir()
        .build())

ext.readVersionCode = {
    def repo = git.getRepository()
    def walk = new RevWalk(repo)
    walk.withCloseable {
        def head = walk.parseCommit(repo.getRef(MASTER).getObjectId())
        def count = 0
        while (head != null) {
            count++
            def parents = head.getParents()
            if (parents != null && parents.length > 0) {
                head = walk.parseCommit(parents[0])
            } else {
                head = null
            }
        }
        walk.dispose()
        println("using version name: $count")
        return count
    }
}

ext.readVersionName = {
    def tag = git.describe().setLong(false).call()
    def clean = git.status().call().isClean()
    def version = tag + (clean ? '' : '-dirty')
    println("using version code: $version")
    return version
}

L'utilisation sera:

apply from: 'git-version.gradle'

Android {
  ...
  defaultConfig {
    ...
    versionCode readVersionCode()
    versionName readVersionName()
    ...
  }
  ...
}
1
Avinash R

Ceci est une version légèrement modifiée de réponse de Diego , qui répond à mon désir d'avoir le nom de la version dans le style suivant:

{dernière balise} - {hachage court de la validation actuelle} - {heure de la validation actuelle}

    import Java.text.SimpleDateFormat

    buildscript {
        repositories {
            jcenter()
        }
        dependencies {
            classpath 'org.ajoberstar.grgit:grgit-core:3.1.1'
        }
    }

    /**
     * Version name will be in following format:
     *
     * "{latest release tag}-{short commit hash of current commit}-{time of current commit}"
     *
     * Example: 1.6.0-5ae9b86-2019-07-04-13:20
     */
    ext {
        git = org.ajoberstar.grgit.Grgit.open(currentDir: projectDir)

        listOfTags = git.tag.list()
        noTags = listOfTags.isEmpty()
        head = git.head()

        if (noTags) {
            gitVersionCode = 0
            gitVersionName = "no-tag-${head.abbreviatedId}-${head.time}"
        } else {
            tagNames = listOfTags.collect { git.describe(commit: it.commit, tags: true) }
            mostRecentVersion = mostRecentVersion(tagNames)

            def date = new SimpleDateFormat('yyyy-MM-dd-HH:mm').format(new Date(head.time * 1000))
            gitVersionCode = listOfTags.size()
            gitVersionName = "$mostRecentVersion-${head.abbreviatedId}-${date}"
        }
    }

    /**
     * Shamelessly stolen from <a href="https://stackoverflow.com/a/7723766/">StackOverflow</a>.
     */
    static String mostRecentVersion(List versions) {
        def sorted = versions.sort(false) { a, b ->
            List verA = a.tokenize('.')
            List verB = b.tokenize('.')

            def commonIndices = Math.min(verA.size(), verB.size())

            for (int i = 0; i < commonIndices; ++i) {
                def numA = verA[i].toInteger()
                def numB = verB[i].toInteger()

                if (numA != numB) {
                    return numA <=> numB
                }
            }
            // If we got this far then all the common indices are identical, so whichever version is longer must be more recent
            verA.size() <=> verB.size()
        }

        // println "Sorted versions: $sorted"
        sorted[-1]
    }

    task printVersion() {
        println("Version Code: $gitVersionCode")
        println("Version Name: $gitVersionName")
    }

En supposant que vous avez également spécifié versionNameSuffix dans app du module build.gradle manière suivante:

    Android {
        ...
        productFlavors {
            debug {
                versionCode gitVersionCode
                versionName gitVersionName
                versionNameSuffix '-DEBUG'
                ...
            }
            // ... other flavors here
        }
    }

Ensuite, ce sera le nom de la version:

Version name as can be seen by user in settings

0
azizbekian

Définissez une fonction simple dans le fichier gradle:

def getVersion(){
    def out = new ByteArrayOutputStream();
    exec {
        executable = 'git'
        args = ['describe', '--tags']
        standardOutput = out
    }
    return out.toString().replace('\n','')
}

Utilise le:

project.version = getVersion()
0
S.D.