Automating your build numbers with Xcode and git

A quick post for users of Xcode and git - how to automatically set your version and build numbers in Xcode from the output of git describe.

I've always used tagging in git as a way to help me generate version and build numbers automatically pretty much since I started using git. Indeed, before git describe existed I wrote a tool to do pretty much the same thing. The basic idea is you tag when you start working on a version with the version number (say, 2.6) with that number, and then you use git describe (and a little awk) to get a build number based on the last tagged version number, and how many commits have been done since (so, if there were 17 commits since we tagged the start of work on a version, the build number is 2.6.17).

The advantage of this is that it means for any given release, I can go and find the exact code that was used to build it. I've used little scripts based on this to build debian packages and the like, but until recently I didn't have a way to make this happen automatically in Xcode. But I've now fixed that, and thought I'd quickly put it here incase others find it useful.

Open your project up in Xcode (I assume you're using the most recent version from the Mac App Store), and head over to the Build Phases tab. Click on the "Add Build Phase" button at the bottom, and select "Add Run Script". This will add a Run Script entry to the end of the build phases list, but we don't want this to happen last, so drag it up to somewhere before the "Compile Sources" phase.

If you expand the tab, you'll see you can enter a script directly into Xcode, which in itself is just a very handy thing to be able to do. You can also select the "Run Script" label on this phase and click it again to edit it. It's a good option to rename it so that it describes what the script does, such as "Get version from git".

Now enter the following script, which I based on various other scripts I'd spotted on the web for auto-incrementing build numbers, and then subverted to my particular needs:

# Assumes that you tag versions with the version number (e.g., "1.1") and then the build number is
# that plus the number of commits since the tag (e.g., "1.1.17")

echo "Updating version/build number from git..."
plist=${PROJECT_DIR}/${INFOPLIST_FILE}

# increment the build number (ie 115 to 116)
versionnum=`git describe | awk '{split($0,a,"-"); print a[1]}'`
buildnum=`git describe | awk '{split($0,a,"-"); print a[1] "." a[2]}'`
if [[ "${versionnum}" == "" ]]; then
    echo "No version number from git"
    exit 2
fi

if [[ "${buildnum}" == "" ]]; then
    echo "No build number from git"
    exit 2
fi

/usr/libexec/Plistbuddy -c "Set CFBundleShortVersionString $versionnum" "${plist}"
echo "Updated version number to $versionnum"
/usr/libexec/Plistbuddy -c "Set CFBundleVersion $buildnum" "${plist}"
echo "Updated build number to $buildnum"

This script uses git describe to get both the version and build numbers (e.g., "2.6" and "2.6.17") and then copies them into the Info.plist for your project, using a little hidden utility called PListBuddy, that's hidden away on most Macs as far as I can tell.

Once done, Xcode should look a little like this:

Xcode version script.png

Assuming you've tagged your git repository at some point, you should find that when you hit build next that the version and build numbers will be automatically updated to reflect the latest state of your git repository.