Auto Version numbering your Android App using Git and Eclipse
Asked Answered
T

4

14

I believe that computers are the best things for doing repetitive tasks. I certainly am not, I either forget, or (mostly) don't do things in a consistent way - which isn't what I want.

One of the things I am likely to do is forget to up the version information in the manifest when I publish a new version of an Android app. In the past I've worked with configured build systems that have a auto version numbering feature and I got use to it (or maybe I got lazy).

I found this Stackoverflow post very useful in coming up with a solution, but it took me a while to fine tune it so it works just they way I want it. Earlier attempts would sometime cause continues building, which was a pain. So I thought I would publish my solution here in the hope that someone else will find it useful.

Tardiff answered 15/2, 2013 at 12:51 Comment(1)
@drobbo, ant/gradle is a cross-platform utility, and git in windows forces a posix environment (I haven't used it in WIN, so cannot vouch for it). Perhaps you can make your question less restrictive, as there is cross-platform solution to this.Rochkind
T
15

This solution was developed on Linux. I'm sure skilled Windows & Mac developers can adapt it to their platforms, but I am not such a developer. Linux is where my skill set lives.

Git has a nice feature in the git describe --dirty command. It scans back down the commit log and finds the tag and then builds a version string from there. If this is a "production build" where the last commit has been tagged and all files have been checked in then that is your version string. If this is a development build then the last tag is appended with the number of additional commits and an abbreviated hash code. The --dirty flag is just the cherry on the icing on the cake: it appends the word dirty if there are any modified files not committed yet. This is perfect for your android:versionName attribute in the manifest file.

For the android:versionCode a number is required. This needs to clock for releases but not for development build, and as every release will have a tag with a version string I simply count these. I always tag my versions in the form v<major>.<minor>[.<patch>] where <major>, <minor> and <patch> are just numbers. So counting tags that start with a lower case 'v' followed with a digit are counted is all thats really needed here.

After trailing with a template manifest file I discovered that the best way was to simply use the AndroidManifest.xml file in the project base, edited using the stream editor sed and deposit the result in bin/AndroidManifest.xml.

So I developed the script below, placed it in a scripts folder at the same level as my projects (so that they can all share the same script) and then configured a custom builder in Eclipse.

There is the script which I called version.sh:

#/bin/bash

echo "Auto Version: `pwd`"

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

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

echo "   Code: ${CODE}"
echo "   Ver:  ${VERSION}"

cat AndroidManifest.xml | \
    sed -e "s/android:versionCode=\"[0-9][0-9]*\"/android:versionCode=\"${CODE}\"/" \
        -e "s/android:versionName=\".*\"/android:versionName=\"${VERSION}\"/" \
    > bin/AndroidManifest.xml

exit 0

To configure the builder here are the steps:

1). Right click the project base and select "Properties" and then "Builders".

2). Hit the "New" button and select the "Program" option.

3). Name your version something like "<project> Auto Version". This string needs to be unique across all projects.

4). Configure the "Main" tab as follows:

4a). In the "Location" section use "Browse File System" and navigate and select the script file.

4b). In the "Working directory" section use "Browse Workspace" to select the project.

5). Leave the "Refresh resources upon completion" unchecked in the "Refresh" tab.

6). Don't set any variables up in the "Environment" tab.

7). In the "Build Options" tab:

7a). Make sure that "During manual builds" is ticked, and

7b). That "During auto builds" is also ticked.

7c). I now have everything else left unselected. I don't even allocate a console to it. The eagle eyed out there may have spotted that the script does output some information, but now I've got it working I just want the thing to run silently without bothering me.

8). Okay the build settings and then position your builder between "Android Pre-Compile" and "Java Builder".

Go back to developing your apps safe in the knowledge that they are being properly versioned, and check out your app's info. Isn't that version number cool. :-)

Steve

Tardiff answered 15/2, 2013 at 12:51 Comment(3)
You made my day! After only two small adjustments the above worked flawlessly. I had to 'chmod +x' the version.sh file and at least have one commit tagged in my git-history to get it working!Dotard
Wouldn't this mean that, if you only use this script, that you won't check in the generated manifest versionName=1.0.0 until one commit after git tag 1.0.0?Affiance
For further inspiration, see grunt-bump, extra usable with grunt-prompt.Affiance
R
12

Idea (using ant and git executable)

ok, here's a novel way of doing it:

  • for calculating version.code:

    git rev-list master --first-parent --count

    this follows the versioning guideline. As it effectively finds the number of commits from the initial commit (inclusive) which is always incrementing from the previous version.

  • for calculating the version.name:

    git describe --tags --dirty --abbrev=7

Implementation:

build.xml usually imports a custom ant script called custom_rules.xml

so making the content of the script as:

<?xml version="1.0" encoding="UTF-8"?>
<project name="application-custom">

<macrodef name="git" taskname="@{taskname}">
    <attribute name="command" />
    <attribute name="dir" default="" />
    <attribute name="property" default="" />
    <attribute name="taskname" default="" />
    <attribute name="failonerror" default="on" />
    <element name="args" optional="true" />
    <sequential>
        <exec executable="git" dir="@{dir}" outputproperty="@{property}" 
            failifexecutionfails="@{failonerror}" failonerror="@{failonerror}">
            <arg value="@{command}" />
            <args/>
        </exec>
    </sequential>
</macrodef>

<target name="-pre-build">
    <git command="rev-list" property="versioning.code" taskname="versioning">
        <args>
            <arg value="master" />
            <arg value="--first-parent" />
            <arg value="--count" />
        </args>
    </git>
    <git command="describe" property="versioning.name" taskname="versioning">
        <args>
            <arg value="--tags" />
            <arg value="--dirty" />
            <arg value="--abbrev=7" />
        </args>
    </git>
    <echo level="info" taskname="versioning">${versioning.code}, ${versioning.name}</echo>
    <replaceregexp file="AndroidManifest.xml" match='android:versionCode=".*"' replace='android:versionCode="${versioning.code}"' />
    <replaceregexp file="AndroidManifest.xml" match='android:versionName=".*"' replace='android:versionName="${versioning.name}"' />
</target>

<target name="-post-build" >
    <replaceregexp file="AndroidManifest.xml" match='android:versionCode=".*"' replace='android:versionCode="0"' />
    <replaceregexp file="AndroidManifest.xml" match='android:versionName=".*"' replace='android:versionName="0"' />
</target>

would just do.

In a nut shell, it just replaces the android.versionCode and android.versionName with the current version code and name, stored in git.

Caveats

  • initial version code and name is set to 0 upon the completion of build. If you require it, replace the zero in the -post-build target, or (though I highly doubt you would require it) you could make it configurable and place it in some property (file or embedded; your choice)
  • if the build is failed, or aborted, the version will remain as it is. (though i highly doubt it is of any concern, just revert the file!)

Enjoy.

Refs

Important Edit

  1. prevent using HEAD to calculate the build number; causes version downgrade issue when doing a build during development, and later when installing the stable version (or when doing a beta to main release transition). using the master (or the branch which is used for production builds) ref instead.

PS: relevant for AS users: Automatically versioning Android project from git describe with Android Studio/Gradle

Rochkind answered 14/12, 2013 at 13:56 Comment(1)
note that this should essentially be a cross-platform solution too.Rochkind
T
8

Using Android Studio (Gradle):

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")
        ...
    }
}
Turdine answered 7/9, 2014 at 6:10 Comment(0)
P
0
'  for windows
'   preBuildMy.cmd  include  repVer.vbs 

' repVer.vbs :

Dim objFileSystem, objOutputFile
Dim objInputFile
Dim sText,gitFile

FileName = "./bin/AndroidManifest.xml"
'  gitFile = ".\..\.git\refs\heads\master"
gitFile = ".\..\.git\refs\remotes\origin\master"

Set objFileSystem = CreateObject("Scripting.fileSystemObject")

set objInputFile= objFileSystem.OpenTextFile(FileName)
sText= objInputFile.ReadAll

set objOutputFile = objFileSystem.CreateTextFile(FileName , TRUE)

set objInputFile= objFileSystem.OpenTextFile(gitFile)
refText= objInputFile.ReadAll

sText = Replace(sText,"v1.0","v 1.0 " & Now & " ref=" & mid(refText,1,7))

objOutputFile.WriteLine(sText)

objOutputFile.Close

Set objFileSystem = Nothing
WScript.Quit(0)
Perez answered 3/3, 2013 at 14:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.