You'd think something as simple as an automatically incrementing build number would be a solved problem by now. Instead it seems to be one of those areas where everyone has their own workaround. At least for those of us not blessed, or cursed, with a monolithic shared build system. For Cocoa apps the build number is also known as a
CFBundleVersion and it's a very useful thing for lining up bug reports with exact points in your code history.
I still use a mostly manual approach to build numbers. Whenever I'm ready to release either a new ad hoc beta or submit a build to iTunes Connect, I run a small shell script that uses agvtool to set the CFBundleVersion of all the targets in my project. Until recently I was just letting agvtool bump the current project version number, whatever it was. This works great until you're building from multiple branches concurrently at which point you will end up with completely different builds sharing the same build number.
My next idea was to keep a text file with the current version number at the root of the project. This text file has to be excluded from version control however, so that it would always reflect the highest build number of any branch. For a single developer on a single machine this is alright but I have two computers...
So over the weekend I decided to build The World's Stupidest Web App™. It's basically one of those bright red "take a number" things, just hosted on Heroku. You make a GET request to a unique path (a bundle ID works great) and it returns the next build number in a sequence. You can specify a starting number, useful for existing projects, but that's about it.
Here's my current build script:
#!/usr/bin/env sh if git diff-index --quiet HEAD --; then BUILD_NUMBER=$(curl "http://buildnum.herokuapp.com/com.example.myproject?start_at=42") agvtool new-version -all "$BUILD_NUMBER" git commit -am "New build: $BUILD_NUMBER" else echo Commit your changes before building. fi
First I check to make sure there are no uncommitted changes. If there aren't I query the web app using the bundle ID for the project and use the response to set the build number. Last, I add a new commit to mark the spot in the repo. It's not perfect, I'm constantly changing my build script, but at least I know that each build number lines up with a single commit in the code base. The build numbers are also suitable for submission to iTunes Connect so I don't have to have a separate process for ad hoc builds and releases.
Feel free to use the web app if you want. Just replace 'com.example.myproject' with your app's bundle ID and, optionally, '42' with the first number you want to receive. You can leave the start_at param, it will be ignored for any active bundle IDs. Note: I don't recommend using this as part of your normal development cycle if only because it can take the app a few seconds to warm up if it's been put to sleep by Heroku.