mokacoding

unit and acceptance testing, automation, productivity

Automated Xcode version and build numbering via Git

When building a software there are a number of challenges that are outside of the scope of the code itself. Which provider to use for beta distribution? In-house or hosted CI? Where to store the code? How to market it? How to spread technical knowledge across the team? And so on...

One of these challenges is versioning. How to makes assure a clear and reliable distinction between different version of the product? How to clearly identify the latest one? How to identify compatible versions? How to differentiate an update that only has a handful of bugfixes from one that adds a big new feature?

In this post we will see how to automate the build and version bump process in such a way that not only it happens automatically, but it also doesn't pollute the git log.

The approach I prefer and recommend is to use Git to generate the build, and optionally version, numbers, in a way that doesn't require updating the Info.plist at every iteration. This method is based on this article by Jared Sinclair, this other article by Kyle Fuller, and the template used in the liftoff tool by thoughbot.

The gist of it is:

  • Use the count of the commits on master as the build number, as the number of commits is always going to increase. Nobody would rebase master squashing commits right?!
  • Use the value of the latest tag as the project version. This assumes that your project is using semantic versioning tags, as everybody should.
  • Inject those values in the archives generated by the build process, so that none of the file under version control result changed.

The script

#!/bin/bash

git=$(sh /etc/profile; which git)
number_of_commits=$("$git" rev-list HEAD --count)
git_release_version=$("$git" describe --tags --always --abbrev=0)

target_plist="$TARGET_BUILD_DIR/$INFOPLIST_PATH"
dsym_plist="$DWARF_DSYM_FOLDER_PATH/$DWARF_DSYM_FILE_NAME/Contents/Info.plist"

for plist in "$target_plist" "$dsym_plist"; do
  if [ -f "$plist" ]; then
    /usr/libexec/PlistBuddy -c "Set :CFBundleVersion $number_of_commits" "$plist"
    /usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString ${git_release_version#*v}" "$plist"
  fi
done

You should save this script as set_build_number.sh, in a folder named Scripts in the root of your project.

How to set this up

The best way to assure the script is run automatically is to execute it as a Run Script Build Phase. To do so in Xcode 7:

Go to the app's main target Build Phases configuration tab

Go to the app's main target Build Phases screenshot

Press the "+" button at the top to add a "Run Script" build phase. Presse the "+" button at the top screenshot

Add a run script phase screenshot

Name the new build phase as you see fit, for example "Set build number"

Name the new run script phase "Set build number"

Type $SRCROOT/Scripts/set_build_number.sh in the script's text box in order to execute the script.

Make the run script phase execute the script screenshot

Update the version and build fields leaving a note to the script for future developers.

Update the Info.plist file screenshot

Build only option

I personally prefer to set only the build number using this script, and to leave the process of bumping the version as an intentional manual operation. The version number in the Info.plist is bumped when the development for a new version starts, so that TestFlight won't complain when receiving new builds, but the x.y.z tag is added only when the version development is completed.


I hope you found this post useful, and I'd recommend you try this approach in your next project. Having automation perform mundane tasks like bumping the build number is incredibly powerful, it makes the workflow more reliable and frees developers' head space so that they can focus on more important tasks, like, you know... writing better code.

If you would like to share you experience with automatic version numbering via git, need help setting it up, or have some improvements to suggest, hit me up on Twitter @mokagio or leave a comment below.

Want more of these posts?

Subscribe to receive new posts in your inbox.