Xcode Bots presents a beautifully simple interface to a barely-but-technically-functional CI solution. It lacks the knobs, dials and switches of Jenkins but this is the way that the Apple gods have told us that CI works and my bet is that they’ll be leaning heavily into OS X Server, ‘XCode service’ and Bots in the future so it’s probably better to get on board. There are definitely upsides to the simplicity too, once you navigate your way past the bugs and shortcomings, the ‘convention over configuration’ approach means that for many simple projects, there’s basically no work involved in getting basic CI up and running.
One notable problem however, is that in their infinite wisdom, Apple has completely ignored the way that most professional developers were using the SDK & tools in the wild when designing their CI solution. In particular, the popular dependency management tool Cocoapods is pretty much totally unsupported. We took Xcode Bots for a run at Venmo the past couple of weeks and have gotten it to a mostly functional place and are considering replacing our Jenkins setup with it moving forward, so I thought I’d share the issues we encountered, how we overcame them and how we approached the setup we have now.
We ordered a 21″ iMac with a 256GB SSD to be our CI box; this way we can easily set it up and can also mount it in the office to show the status of our iOS tests.
For the purposes of this guide, I’m going to assume that you have an up-to-date install of OS X Mavericks with Xcode installed.
- Download OS X Server from the App Store & run the setup. The setup (usually) completes fine.
- Open the ‘Server’ app and select the ‘Xcode’ service. Switch it on & tell it where Xcode is.
So it turns out that OS X Server setup does almost nothing sensible with permissions. The server & all server services (e.g. Xcode) run (mostly) as a hidden user _teamsserver. You can’t log in as this user and so configuring anything on the file system is hard, also, occasionally some processes get started as your main user and so if you don’t have access to files it can cause problems. For reference, the user account on our machine is mobileteam, so anywhere you see that, replace it with your user account.
- By now the directory /var/teamsserver/ should exist.
sudo mkdir /var/teamsserver
- Now let’s add ourselves to this folder.
- Open the folder in Finder
cd /var/ & open .
- Right click on teamsserver (if you don’t already make hidden files visible, you should)
- Unlock the dialog and add your user as ‘Read & Write’ on the folder.
- Apply this to all subfolders recursively by using ‘Apply to enclosed items’
Set up our SSH Keys
OS X Server & Xcode will generate some SSH keys, but don’t trust it.. it’s a trap! So let’s generate our own..
Generate an SSH key and copy it to the clipboard. In terminal:
Add this to your GitHub account here. (just paste in to the field – it’s on your clipboard)
Add it to GitHub Enterprise if you use it ☺
Move the SSH key to the teamsserver’s user folder:
Make sure that the properties for both of the files include Read access for the user _teamsserver
Test it out by adding an ssh-based GitHub repository to the repositories list in the Xcode tab in the Server app.
Build a setup script
You may be able to skip this step if you have tooling in place already, but we need a script which can do everything required to go from git clone to ‘Build & Run‘. This will likely be a good tool for your team moving forward anyway.
Ours is called setup and it looks a little bit like this:
(You will need to change this to match your setup steps, but hopefully it can help. I’ve also removed some more sensitive steps from this).
Setup our Xcode Project
Now we’re ready to start preparing the project to build on a bot.
You’ll most likely want to make these changes in a branch until we get it working, then you can rebase & squash the changes to one nice pretty ‘Add Xcode Bots Compatibility’ commit ☺.
There are probably many ways of doing this, but the solution that I decided on was to create a target which becomes a dependency of our target and contains a single run-script build phase. This only runs if it’s on the Xcode bots server, which I determine by checking to see if the username it’s running as is t_eamsserver.
Our configuration looks a little bit like this…
This then becomes the first dependency of your main build target.
And the code..
Add your Signing Certificate to Bots Server
If you’re comfortable, you can go and generate a new signing identity for the Bot Server etc.. but for the sake of this guide, I’m just going to export mine from my Keychain.
Open Keychain Assistant
Find the ‘Certificate’ named something like ‘iPhone Developer Your Name’
Right click -> Export and save it somewhere
Move this to the Bots server and add it to the System keychain. (This is important, it won’t work in Login)
Make sure that the Bots Server has the right Provision Profile
Export the provision profile for your debug configuration from your machine and move it to the Xcode Bots server.
Double click the file to open it in Xcode.
Then move the .mobileprovision file to:
- Make sure that the _teamsserver user has access to the provision profile. You can likely do this by opening the ‘Get Info’ panel on the ProvisioningProfiles folder and re-applying the permissions to all subfiles and folders.
Let’s try running a bot!
On your Xcode bots server, open the web interface and create a new bot. Enter your repository SSH url & set the branch to whatever you named your Xcode Bots branch. Set the target to your main target name (it will use the ‘Test’ scheme) and the run mode to manually run –> then run it!
It will most likely fail for one of a few reasons.. Where does it fail?
While cloning the source files
This likely means that either your Xcode bots machine can’t reach GitHub (Enterprise) — check it’s online and on your VPN if required, or that your SSH key was not found or authenticated for the repository.
Check that your SSH keys are in /var/teamsserver/.ssh/ and that _teamsserver has access to them, also check that this SSH key is registered on GitHub. You can check which SSH key is being used by opening the Bot run logs.
Let’s try building in Xcode and fix errors as they come.
Get the URL of the checked-out source from the log from the Bot run. It will be in the ‘/Library/
Start Xcode as the _teamsserver user
sudo -u _teamsserver /Application/Xcode.app/Contents/MacOS/Xcode
Open the project from the checked out source and hit ‘Build & Run’
The problem will likely become obvious.. either a provision profile issue, code signing issue (it may require an interactive confirmation dialog the first time it accesses the certificate) or that some of the sources aren’t present.
I personally really like the Xcode bots interface and the simplicity should hopefully mean that once you’ve gone through the process, you can leave it and not think too much about CI except moving it forward, rather than trying to keep it running. Unfortunately, we’re still having some issues between runs & between restarts.. especially around permissions. I’m still working on some of these.
Also, any this is user error, people often mistakenly check in changes to the project file which use some locally generated provisioning profile (Xcode’s ‘Fix this’ tool in Xcode 5.0 is often the opposite of helpful!).
We’re going to continue using Xcode Bots for a while and I know that I’m excited to see where this tool goes and how quickly Apple can build this in to the full CI & CD tool that we so badly need.
Let's talk about it.
Head on over to Twitter and let's talk more.
Find me at @chrismaddern.