web-dev-qa-db-fra.com

Code de version à incrémentation automatique dans Android

existe-t-il un moyen d'incrémenter automatiquement le code de version chaque fois que vous créez une application Android dans Eclipse?

Selon http://developer.Android.com/guide/publishing/versioning.html , vous devez incrémenter manuellement votre code de version dans AndroidManifest.xml.

Je comprends, vous devez exécuter un script avant chaque build qui serait, par exemple analyser le fichier AndroidManifest.xml, trouver le numéro de version, l'incrémenter et enregistrer le fichier avant le démarrage de la construction. Cependant, je n'ai pas pu savoir comment et si Eclipse prend en charge les scripts d'exécution avant/après les builds.

J'ai trouvé cet article sur la configuration de Ant Builder, mais il ne s'agit pas exactement de Android et je crains que cela ne gâche trop les étapes de construction prédéfinies pour Android?

Cela devrait être un problème courant, comment l'avez-vous résolu?

Eh bien, on peut le faire manuellement, mais dès que vous oubliez de faire cette corvée, vous obtenez différentes versions avec le même numéro et le versioning entier n'a pas de sens.

62
iseeall

Donc, je le vois comme ceci:

Selon l'article que vous présentez, utilisez ant pour ces tâches (cibles?).

  1. analyser le manifeste (analyser XML)
  2. obtenir le manifeste du formulaire de l'ancienne version et l'augmenter/obtenir la version du référentiel
  3. stocker une nouvelle version dans le manifeste
  4. build Android app.

Mais dans mon cas, je remplis généralement ce champ par une valeur basée sur la révision de Tag lorsque je déploie ou distribue une application.

4
aeracode

J'ai accompli cela. Et voici comment je l'ai fait pour le prochain gars (en utilisant Eclipse):

1) Créez un exécutable de console externe qui va écrire un nouveau code de version dans AndroidManifest.xml: (le mien est en C #)

using System.IO;
using System.Text.RegularExpressions;

namespace AndroidAutoIncrementVersionCode
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                string FILE = @"AndroidManifest.xml";
                string text = File.ReadAllText(FILE);
                Regex regex = new Regex(@"(?<A>Android:versionCode="")(?<VER>\d+)(?<B>"")", RegexOptions.IgnoreCase);
                Match match = regex.Match(text);
                int verCode = int.Parse(match.Groups["VER"].Value) + 1;
                string newText = regex.Replace(text, "${A}" + verCode + "${B}", 1);

                File.WriteAllText(FILE, newText);
            }
            catch { }
        }
    }
}

à part: n'importe quel compilateur c-sharp peut construire cette application, vous n'avez pas besoin de Visual Studio ou même de Windows

  1. si vous ne l'avez pas déjà, installez le runtime .NET ( Mono fonctionnera, lien ) ( le lien vers le framework .NET 2.0 de MS, 2.0 est le plus petit téléchargement, n'importe quelle version> = 2.0 est très bien )
  2. copiez ce code dans un *.cs fichier (j'ai nommé le mien: AndroidAutoIncrementVersionCode.cs)
  3. ouvrez une invite de commande et accédez à l'endroit où vous avez créé votre *.cs fichier
  4. construire le fichier en utilisant cette commande (sous Windows, similaire pour Mono mais changez le chemin vers le compilateur): c:\Windows\Microsoft.NET\Framework\v2.0.50727\csc AndroidAutoIncrementVersionCode.cs (voir: . NET ou Mono pour plus d'informations)
  5. félicitations, vous venez de créer une application C # sans aucun outil, elle aurait dû générer AndroidAutoIncrementVersionCode.exe dans le même répertoire automatiquement

    ** le kilométrage peut varier, les chemins peuvent être différents, aucun achat requis, nul là où interdit, j'ai ajouté cela parce que C # est génial, et les gens pensent à tort qu'il a le verrouillage MS, vous pouvez tout aussi facilement le traduire dans une autre langue (mais je ne vais pas faire ça pour vous;). d'ailleurs n'importe quelle version de n'importe quel compilateur .NET fonctionnera, j'ai adapté le code pour le dénominateur le moins commun ... *

mettre de côté

2) Exécutez l'exécutable pendant le processus de construction: a) Accédez aux propriétés du projet

go to project properties

b) Dans les propriétés, allez dans "Builders" -> "New ..."

Eclipse properties screen

c) Choisissez "Programme"

choose program

d) Dans l'onglet "Principal", sélectionnez l'emplacement du programme (j'ai également défini le répertoire de travail pour qu'il soit sûr) et donnez-lui un nom si vous le souhaitez.

edit configuration - main

e) Dans l'onglet "Actualiser", sélectionnez l'option "Actualiser les ressources à la fin" et "La ressource sélectionnée" - cela actualisera le manifeste après l'avoir écrit.

edit configuration - refresh

f) Dans l'onglet "Options de construction", vous pouvez désactiver "Allouer la console" car vous n'avez aucune entrée et sortie, puis sélectionner uniquement "Pendant les constructions manuelles" et "Pendant les constructions automatiques" désélectionnez "Après un nettoyage" si elle est cochée. Sélectionnez ensuite "Spécifier un ensemble de travail des ressources pertinentes" et cliquez sur le bouton "Spécifier les ressources ...". Dans la boîte de dialogue "Modifier l'ensemble de travail", recherchez votre fichier "AndroidManifest.xml" dans la boîte de dialogue et vérifiez-le, puis cliquez sur "Terminer"

edit configuration - build optionsedit working set

f) Maintenant, cliquez sur "OK" dans la "boîte de dialogue Modifier la configuration" et dans les propriétés de votre application, sélectionnez le générateur nouvellement créé et continuez à cliquer sur "Haut" jusqu'à ce qu'il soit en haut de la liste, de cette façon l'incrémentation automatique s'exécute tout d'abord, et ne déclenche pas d'états de synchronisation ou de reconstructions accidentels. Une fois que le nouveau générateur que vous avez créé est en haut de la liste, cliquez sur "OK" et vous avez terminé.

edit configuration - hit okenter image description here

112
ckozl

Ce script Shell, adapté aux systèmes * nix, définit le versionCode et le dernier composant de versionName sur la révision Subversion actuelle. J'utilise Netbeans avec NBAndroid et j'appelle ce script à partir de la cible -pre-compile dans custom_rules.xml.

Enregistrez ce script dans un fichier appelé incVersion dans le même répertoire que AndroidManifest.xml, rendez-le exécutable: chmod +x incVersion

manf=AndroidManifest.xml
newverfull=`svnversion`
newvers=`echo $newverfull | sed 's/[^0-9].*$//'`
vers=`sed -n '/versionCode=/s/.*"\([0-9][0-9]*\)".*/\1/p' $manf`
vername=`sed -n '/versionName=/s/.*"\([^"]*\)".*/\1/p' $manf`
verbase=`echo $vername | sed 's/\(.*\.\)\([0-9][0-9]*\).*$/\1/'`
newvername=$verbase$newverfull
sed /versionCode=/s/'"'$vers'"'/'"'$newvers'"'/ $manf | sed /versionName=/s/'"'$vername'"'/'"'$newvername'"'/  >new$manf && cp new$manf $manf && rm -f new$manf
echo versionCode=$newvers versionName=$newvername

Créez ou modifiez custom_rules.xml et ajoutez ceci:

<?xml version="1.0" encoding="UTF-8"?>
<project name="custom_rules">
    <xmlproperty file="AndroidManifest.xml" prefix="mymanifest" collapseAttributes="true"/>
    <target name="-pre-compile">
        <exec executable="./incVersion" failonerror="true"/>
    </target>
</project>

Donc, si ma révision svn actuelle est 82, je me retrouve avec cela dans AndroidManifest.xml:

Android:versionCode="82"
Android:versionName="2.1.82">

Lorsque je veux publier une nouvelle version, je met généralement à jour les premières parties de versionName, mais même si j'oublie, la dernière partie de versionName (qui est exposée dans mon activité À propos) me dira toujours à partir de quelle révision svn elle a été construite. . De plus, si je n'ai pas archivé les modifications, le numéro de révision sera 82M et versionName sera quelque chose comme 2.1.82M.

L'avantage par rapport à une simple incrémentation du numéro de version chaque fois qu'une génération est effectuée est que le numéro reste sous contrôle et peut être directement lié à une révision svn spécifique. Très utile pour enquêter sur les bogues autres que la dernière version.

9
Clyde

FWIW, j'ai pu mettre à jour la valeur de la version de construction en six lignes de python:

#!/bin/env python
import os
from xml.dom.minidom import parse
dom1 = parse("AndroidManifest.xml")
dom1.documentElement.setAttribute("Android:versionName","%build.number%")
f = os.open("AndroidManifest.xml", os.O_RDWR)
os.write( f, dom1.toxml() )
7
user1048976

En s'appuyant sur réponse de Charles , ce qui suit incrémente la version de build existante:

#!/usr/bin/python
from xml.dom.minidom import parse

dom1 = parse("AndroidManifest.xml")
oldVersion = dom1.documentElement.getAttribute("Android:versionName")
versionNumbers = oldVersion.split('.')

versionNumbers[-1] = unicode(int(versionNumbers[-1]) + 1)
dom1.documentElement.setAttribute("Android:versionName", u'.'.join(versionNumbers))

with open("AndroidManifest.xml", 'wb') as f:
    for line in dom1.toxml("utf-8"):
        f.write(line)
6
Rocky

Recette:

Pour que l'attribut Android: versionCode de l'élément manifest dans AndroidManifest.xml soit automatiquement défini sur l'heure actuelle (à partir d'Epoch en secondes, obtenu à partir du shell Unix) chaque fois que vous exécutez une génération, ajoutez-le à votre cible -pre-build dans custom_rules.xml Android.

<target name="-pre-build">
  <exec executable="date" outputproperty="CURRENT_TIMESTAMP">
    <arg value="+%s"/>
  </exec>
  <replaceregex file="AndroidMainfest.xml" match="Android:versionCode=.*"
    replace='Android:versionCode="${CURRENT_TIMESTAMP}"' />
</target>

Test de confirmation:

Obtenez l'attribut versionCode du fichier apk généré, à l'aide de la commande Shell suivante depuis votre répertoire de projet Android:

$Android_SDK/build-tools/20.0.0/aapt dump badging bin/<YourProjectName>.apk | grep versionCode

et le comparer à la date actuelle renvoyée par la commande Shell: date +%s La différence doit être égale à la période en secondes entre les deux étapes de confirmation ci-dessus.

Avantages de cette approche:

  1. Que la construction soit lancée à partir de la ligne de commande ou d'Eclipse, elle mettra à jour le code de version.
  2. Le versionCode est garanti unique et croissant pour chaque build
  3. Le versionCode peut être reconstitué en un temps de construction approximatif si vous en avez besoin
  4. Le script ci-dessus remplace toute valeur actuelle de versionCode, même 0 et ne nécessite pas d'espace réservé à la macro (tel que -build_id-).
  5. Étant donné que la valeur est mise à jour dans le fichier AndroidManifest.xml, vous pouvez l'archiver dans le contrôle de version et elle conservera la valeur réelle, pas une macro (telle que -build_id-).
3
Brenton Camac

En s'appuyant sur réponse de Rocky j'ai amélioré ce script python un peu pour augmenter également versionCode, fonctionne pour moi sur Eclipse (intégré selon grand tutoriel ckozl =) & Mac OSX

#!/usr/bin/python
from xml.dom.minidom import parse

dom1 = parse("AndroidManifest.xml")
oldVersion = dom1.documentElement.getAttribute("Android:versionName")
oldVersionCode = dom1.documentElement.getAttribute("Android:versionCode")
versionNumbers = oldVersion.split('.')

versionNumbers[-1] = unicode(int(versionNumbers[-1]) + 1)
dom1.documentElement.setAttribute("Android:versionName", u'.'.join(versionNumbers))
dom1.documentElement.setAttribute("Android:versionCode", str(int(oldVersionCode)+1))
with open("AndroidManifest.xml", 'wb') as f:
    for line in dom1.toxml("utf-8"):
        f.write(line)

n'oubliez pas non plus de chmod +x autoincrement.py et assurez-vous d'avoir le bon chemin vers python sur la première ligne (en fonction de votre environnement) comme sulai indiqué

2
ursimon

J'ai pu trouver ma propre solution à partir des informations fournies. Au cas où cela serait utile pour quelqu'un, voici mon script bash pour mettre à jour les attributs versionCode et versionName lors de l'utilisation de GIT VCS sous Linux.

Mon script pour modifier le fichier AndroidManifest.xml ressemble à ceci:

#/bin/bash

CODE=`git tag | grep -c ^v`
NAME=`git describe --dirty`
COMMITS=`echo ${NAME} | sed -e 's/v[0-9\.]*//'`

if [ "x${COMMITS}x" = "xx" ] ; then
    VERSION="${NAME}"
else
    BRANCH=" (`git branch | grep "^\*" | sed -e 's/^..//'`)"
    VERSION="${NAME}${BRANCH}"
fi

cat AndroidManifest.template.xml \\
    | sed -e "s/__CODE__/${CODE}/" \\
          -e   "s/__VERSION__/${VERSION}/" > AndroidManifest.xml

exit 0

Il analyse le fichier modèle (AndroidManifest.template.xml) et remplace les chaînes "__VERSION__" et "__CODE__" par des valeurs plus appropriées:

  • "__CODE__" est remplacé par un décompte du nombre de balises dans le référentiel Git qui commence par un seul V minuscule et est suivi d'une séquence de chiffres et de points. Cela ressemble à la plupart des chaînes de version comme: "v0.5", "v1.1.4" et ainsi de suite.
  • "__VERSION__" est remplacé par une combinaison de la sortie de la commande "git describe" et, si ce n'est pas une construction "propre", la branche sur laquelle il a été construit.

Par une construction "propre", je veux dire une où tous les composants sont sous contrôle de version et leur dernière validation est étiquetée. "git describe --dirty" rapportera un numéro de version basé sur la dernière balise annotée accessible dans votre dernier commit sur la branche actuelle. S'il y a des validations depuis cette balise, le nombre de ces validations est signalé, tout comme le nom d'objet abrégé de votre dernière validation. L'option "--dirty" ajoutera "-dirty" aux informations ci-dessus si des fichiers modifiés sous contrôle de version ont été modifiés.

Par conséquent, AndroidManifest.xml ne doit plus être sous contrôle de version et vous devez uniquement modifier le fichier AndroidManifest.template.xml. Le début de votre fichier AndroidManifest.template.xml ressemble à ceci:

<manifest xmlns:Android="http://schemas.Android.com/apk/res/Android"
    package="com.example.packagename"
    Android:versionCode="__CODE__"
    Android:versionName="__VERSION__" >

J'espère que cela est utile à quelqu'un

1
Dobbo

Tout le mérite revient à ckoz, mais j'ai écrit ma propre implémentation en c #. Je pense que c'est un peu plus rapide et ne mange pas d'erreurs parce que si quelque chose va mal, quelque chose est mal configuré et je devrais le savoir.

namespace AndroidVersionCodeAutoIncrement
{
    using System.IO;
    using System.Text.RegularExpressions;

    public class Program
    {
        private static readonly Regex VersionCodeRegex = new Regex("Android:versionCode=\"(?<version>.*)\"", RegexOptions.Compiled);

        public static void Main()
        {
            using (var manifestFileStream = File.Open("AndroidManifest.xml", FileMode.Open, FileAccess.ReadWrite))
            using (var streamReader = new StreamReader(manifestFileStream))
            {
                var manifestFileText = streamReader.ReadToEnd();

                var firstMatch = VersionCodeRegex.Match(manifestFileText);
                if (firstMatch.Success)
                {
                    int versionCode;
                    var versionCodeValue = firstMatch.Groups["version"].Value;
                    if (int.TryParse(versionCodeValue, out versionCode))
                    {
                        manifestFileText = VersionCodeRegex.Replace(manifestFileText, "Android:versionCode=\"" + (versionCode + 1) + "\"", 1);

                        using (var streamWriter = new StreamWriter(manifestFileStream))
                        {
                            manifestFileStream.Seek(0, SeekOrigin.Begin);
                            streamWriter.Write(manifestFileText);
                            manifestFileStream.SetLength(manifestFileText.Length);
                        }
                    }
                }
            }
        }
    }
}
1
Bartłomiej Mucha

Pour ceux qui sont sur OSX et veulent utiliser Python, mais qui ne perdent pas le formatage XML qui, lorsque l'analyse est effectuée par l'analyseur XML python se produit, voici un script python qui fera l'affaire l'incrémental basé sur l'expression régulière, qui conserve la mise en forme:

#!/usr/bin/python
import re

f = open('AndroidManifest.xml', 'r+')
text = f.read()

result = re.search(r'(?P<groupA>Android:versionName=")(?P<version>.*)(?P<groupB>")',text)
version = str(float(result.group("version")) + 0.01)
newVersionString = result.group("groupA") + version + result.group("groupB")
newText = re.sub(r'Android:versionName=".*"', newVersionString, text);
f.seek(0)
f.write(newText)
f.truncate()
f.close()

Le code était basé sur la réponse @ckozl, il a juste été fait dans python donc vous n'avez pas besoin de créer d'exécutable pour cela. Nommez simplement le script autoincrement.py, placez-le dans le même dossier que le fichier manifest.xml et suivez les étapes décrites par ckozl ci-dessus!

1
Lefteris

J'ai fait quelque chose de similaire, mais je l'ai écrit en tant qu'application Desktop AIR au lieu d'un C # externe (je n'ai pas ressenti l'installation d'un autre système de build). Créez cette application Flex/ActionScript et modifiez le chemin d'accès à votre fichier, puis créez-le en tant qu'application de bureau autonome. Il réécrit la partie 1.2.3 de votre fichier.

    <?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.Adobe.com/mxml/2009"
                       xmlns:s="library://ns.Adobe.com/flex/spark"
                       xmlns:mx="library://ns.Adobe.com/flex/mx"
                       width="371" height="255" applicationComplete="Init();">
    <fx:Declarations>
        <!-- Place non-visual elements (e.g., services, value objects) here -->
    </fx:Declarations>

    <fx:Script>
        <![CDATA[

            public function Init():void
            {
                import flash.filesystem.File;
                import flash.filesystem.FileMode;
                import flash.filesystem.FileStream;

                var myFile:File = new File("D:\\Dropbox\\Projects\\My App\\src\\Main-app.xml");

                var fileStream:FileStream = new FileStream();
                fileStream.open(myFile, FileMode.READ);

                var fileContents:String = fileStream.readUTFBytes(fileStream.bytesAvailable);

                var startIndex:Number = fileContents.indexOf("<versionNumber>");
                var numberIndex:Number = startIndex + 15;
                var endIndex:Number = fileContents.indexOf("</versionNumber>");

                if (startIndex == -1 || endIndex == -1)
                    return;

                var versionNumber:String = fileContents.substr(numberIndex, endIndex - numberIndex);
                var versionArr:Array = versionNumber.split(".");
                var newSub:Number = Number(versionArr[2]);
                newSub++;
                versionArr[2] = newSub.toString();
                versionNumber = versionArr.join(".");

                var newContents:String = fileContents.substr(0, startIndex) + "<versionNumber>" + versionNumber + "</versionNumber>" +
                                fileContents.substr(endIndex + 16);
                fileStream.close(); 


                fileStream = new FileStream();
                fileStream.open(myFile, FileMode.WRITE);
                fileStream.writeUTFBytes(newContents);
                fileStream.close(); 

                close();
            }
        ]]>
    </fx:Script>
    <s:Label x="10" y="116" width="351" height="20" fontSize="17"
             text="Updating My App Version Number" textAlign="center"/>

</s:WindowedApplication>
1
Per

Voici la version Java pour ce que ça vaut. Aussi gérer plusieurs manifestes.

String directory = "d:\\Android\\workspace\\";

String[] manifests = new String[] 
{
        "app-free\\AndroidManifest.xml",
        "app-donate\\AndroidManifest.xml",
};

public static void main(String[] args)
{
    new version_code().run();
}

public void run()
{
    int I = manifests.length;
    for(int i = 0; i < I; i++)
    {
        String path = directory + manifests[i];

        String content = readFile(path);
        Pattern         versionPattern = Pattern.compile( "(.*Android:versionCode=\")([0-9]+)(\".*)", Pattern.DOTALL );
        Matcher m = versionPattern.matcher(content);

        if (m.matches())
        {
            int code = Integer.parseInt( m.group(2) ) + 1;

            System.out.println("Updating manifest " + path + " with versionCode=" + code);

            String newContent = m.replaceFirst("$1" + code + "$3");

            writeFile(path + ".original.txt", content);
            writeFile(path, newContent);
        }
        else
        {
            System.out.println("No match to update manifest " + path);
        }
    }
}
0
3c71

Si vous souhaitez mettre à jour AndroidManifest.xml pour utiliser un numéro de version spécifique, peut-être à partir d'un système de construction, vous pouvez utiliser le projet que je viens de pousser vers GitHub: https://github.com/bluebirdtech/AndroidManifestVersioner

C'est une application de ligne de commande .NET de base, utilisation:

AndroidManifestVersioner <path> <versionCode> <versionName>.

Merci à d'autres affiches pour leur code.

0
Justin Caldicott

Si vous utilisez gradle, vous pouvez spécifier versionName et versionCode très facilement dans build.gradle. Vous pouvez utiliser git commit count comme un nombre croissant pour identifier la génération.

Vous pouvez également utiliser cette bibliothèque: https://github.com/rockerhieu/Versionberg .

0
Hieu Rocker