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
Press the "+" button at the top to add a "Run Script" build phase.
Name the new build phase as you see fit, for example "Set build number"
Type $SRCROOT/Scripts/set_build_number.sh
in the script's text box in order to execute the script.
Update the version and build fields leaving a note to the script for future developers.
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.