Continuous Integration of iOS projects using Xcode Server
Continuous Integration (CI) for iOS projects has long been a difficult scenario. Tools like Jenkins and Teamcity have been used with varying degrees of success to manage the build lifecycle, but often these solutions involved custom scripts or workarounds. For test device installs, TestFlight was often the tool of choice, but many development houses chose to create custom internal deployment methods. With the recent release Mac OSX Mavericks and Xcode server, Apple has incorporated a CI tool in their standard development environment. Once configured, “Bots” can be created and monitored from within each developers’ Xcode IDE or through a web browser with the build being handled periodically on the server.
Build history is all available, along with details of any errors, warnings, analysis issues and test results. Best of all, the signed app can be installed directly on a test iPhone or iPad by navigating to the build web page in mobile safari, and clicking the Product link.
Setting up the system for straightforward projects is relatively easy – install the server software, answer a handful of questions, connect to your repository and start creating bots to manage your integrations. It’s once you get into more involved projects that you might find yourself hitting a few stumbling blocks. Here’s a few tips we’ve discovered while setting up Xcode server in our environment.
Git Integration and SSH Keys
Every repository needed to perform your builds needs to be known to the server – including any submodule repositories. You can actually have the OSX Server host the repositories directly, but most people will already have a source control solution in place and will need to connect Xcode server to those repositories. There have been reports of unreliable SSH keys being generated by Xcode, so it’s best to go back to the terminal on your server and generate your SSH keys manually using ssh-keygen. Once keys are created, you need to register the public key with your repository – generally by pasting the key into a user’s profile. To copy the key, type the following in terminal:
pbcopy < ~/.ssh/id_rsa.pub
Use the server to create the repositories. They can be configured as required when creating a bot from within Xcode, but it’s generally easier to go through the process on the server. When adding a new repository, select the type of repository (local Git, remote Git or Subversion), give the repository a name and provide the URL to the actual repository on the server. You can select password or SSH as the authentication method. Using SSH means that there is no username and password configured here, plus it means that any submodules that use this repository (that themselves are configured using SSH) will still work. The username needs to match any username provided in the URL. Instead of accepting the provided SSH key, edit the key and copy the server’s existing key into the spaces provided:
- Paste the output of 'pbcopy < ~/.ssh/id_rsa.pub' into the public key section
- Paste the output of 'pbcopy < ~/.ssh/id_rsa' into the private key section
A bot for a project that uses submodules should be created in Xcode – there is no facility to specify the submodules and confirm their configuration on the server through the web interface. In Xcode, open the project and use the Source Control menu to make sure that no submodule is in a detached head state. Any submodule that is in the detached head state will cause the build process to look for the branch literally named “Detached head…”. There is a Refresh Status option in the Source Control menu, but it doesn’t actually refresh the state if you’ve changed branches outside of Xcode, so any branch changes need to be done from within the Source Control menu of Xcode. Restarting Xcode can help to force a refresh of the Source Control menu state, but it’s worth checking it to be sure.
Certificates and Provisioning Profiles
Xcode has progressed in leaps and bounds recently in terms of managing certificates and provisioning profiles. There may still be some issues though with managing keys on the server. The certificates and keys need to be in the system keychain, not just the login keychain since the Xcode build service will run in its own account. Dragging the keys from the login keychain to the system keychain should be all that’s required. For those keys that are not already downloaded, you may need to export the keys from a development machine and import them into the system keychain on the server manually.
A quick tip: if the development machine you’re exporting from does not have the arrow next to the name of the certificate for the key in the keychain, then that machine does not have the private key that’s required and you’ll need to track down the machine that does. Also, provisioning profiles are not automatically downloaded, so you need to manually copy all of them to /Library/Server/Xcode/Data/ProvisioningProfiles on the server.
If the code signing fails with “user interaction is not allowed”, then the certificates and keys are all present, but the keychain is trying to verify that the codesign application is allowed to access the keychain, and is attempting to do so interactively through a popup dialog on the server. To overcome this issue, find the key in question in the system keychain on the server, and select “Get Info” from the context menu (right click), switch to the Access Control tab and add /usr/bin/codesign to the list of applications that have access to the key. By default, /usr is not visible in the file browser, but typing “/” will bring up a Go To folder prompt allowing you to directly enter /usr/bin.
Accessing the server
Xcode server sets up a web interface that provides all sorts of information about the health of the projects. There’s even an information radiator view that will provide a full-screen display of the current status of all your bots. Using the web interface, a user can go through and see the history of the builds, kick off a new build, view commit logs and download a specific build directly to a device. In order to get the full potential to a team of developers though, it’s ideal to have each developer add the server to their accounts through the preferences panel. Xcode then allows the developer to view all the detail from within the Log Navigator within the IDE itself. More detailed information about failure reasons is available through Xcode (as opposed to the web interface).
Xcode 5, and OSX Xcode server has brought about a clean and reliable way to implement Continuous Integration into a professional development environment. Many of the features of third party build environments have been included, but have been customised to deal with the intricacies of building and signing a distributable iOS app. There is still a fair few teething issues as this post has discussed, but having everything integrated into a single system has reduced a lot of complexity in setting up a build server / Continuous Integration server.
- Apple Documentation
- Xcode Bots, hosted git repositories, and automated TestFlight builds!
- Xcode Bots: Common Problems and Workarounds
- Stop Sucking at Build Environments
- WWDC2013 Session 412: Continuous Integration with Xcode 5