Xcode Server 8 tips and tricks

Xcode Server — Apple’s continuous integration system for automating static analysis of software, unit testing, and build archiving has been around for a while, but it never got as much as attention as it deserves. Historically, setting up Xcode Server has been a rather cumbersome and time consuming process. This made some to give up all the great power of seamless integration with Xcode IDE and reach for alternative CI servers out there. This year at WWDC during Advanced Testing and Continuous Integration session, Apple has announced exciting features and improvements to Xcode Server 8. These new enhancements make Xcode Server 8 a valuable tool you should consider setting up in your team.

In this blog post, I want to share a few tips and tricks I have learnt while setting up Xcode Server in our team. I hope, that these tips and tricks will help you make Xcode Server a robust CI server your development team can rely on and benefit from.

Xcode Server bots

Disclaimer

This blog post is not a step-by-step tutorial, but rather a comprehensive summary of best practices I have learnt while setting up Xcode Server. If you are looking for a detailed step-by-step tutorial, check out great series of Xcode Server Tutorials by Honza Dvorsky.

Also, if you are still unsure about self-hosting your own build server and why to use Xcode Server rather than other CI servers (cloud or self-hosted) out there, check out my previous post Self-hosted CI for iOS/Mac development.

TL;DR

  1. Dedicate separated machine for Xcode Server
  2. Create dedicated user account for Integration User
  3. Create dedicated GitHub/Bitbucket account for Integration User
  4. Use Bundler in your project to install the exact Ruby gems and versions
  5. Use Ruby version manager on your Xcode Server to manage Ruby gems
  6. Always install and update Ruby gems and CocoaPods in pre-integration trigger
  7. Use Buildasaur macOS app for pull request testing
  8. Use Fastlane for deploying an app, executed in pre-integration trigger

Getting started

macOS Server app

Xcode Server is a service running — among other useful services like FTP, Wiki, Caching — within the macOS Server app. It can be downloaded from the Mac App Store for $19.99. However, if you’re registered in the Apple Developer Program, you can get a redeem code and download it for free.

TIP #1: I highly recommend you to dedicate Mac Mini or Mac Pro with a fresh installation of macOS Sierra for your CI server. This piece of hardware should be stationary placed on your company premises to ensure privacy, full control and availability to your development team 24/7.

Integration User

Once you have downloaded macOS Server app, make sure to follow the Set Up Xcode Server installation guide. In step 4. (Select a user account Xcode Server can use for performing integrations) you will be asked to select a Testing User. Testing User (Integration User, CI User whatever you choose to call it) is a regular user account under which all Xcode Server integrations will be executed.

TIP #2: Create a new non-administrator user account. Do not use your existing (administrator) account as you want to isolate the environment your integrations will be executed in, in order to eliminate potential security breaches.

TIP #3: Create a separate GitHub/Bitbucket profile and generate new SSH keys for your Integration User. Again, you want to isolate your CI environment as much as possible.

Ruby installation

Chances are that your project has a dependency on one or more Ruby gem — be it CocoaPods, Carthage or any other. Once you start to have several projects on your Xcode Server (each using a different version of e.g.: CocoaPods) it will become extremely cumbersome to manage their different gem versions.

TIP #4: Install and use Bundler in your project to install the exact Ruby gems and versions that it needs. It only takes a few minutes to setup a Gemfile for your project.

TIP #5: Install and use any Ruby version manager on your Xcode Server to manage several versions of Ruby gems. I personally use and would recommend rbenv, which is a lightweight, easy-to-use Ruby version manager that serves well to the needs of our development team.

Bots configuration

The way you choose to configure your bots depends on your project needs and will likely vary from one project to another. Out of the box, Xcode Server bots can perform static analysis, run tests and archive your app. This is nice, but quite often not enough. You will probably also want to execute e.g.: pod install every time your bot integrates in order to install your 3rd party libraries. Luckily, bots can be configured to perform certain trigger such as running a custom script before and after your integration.

TIP #6: Always install your Ruby gems (TIP #4), update and install CocoaPods (TIP #5) dependencies in pre-integration trigger to ensure your project is in a clean and up-to-date state.

Bot pre-integration trigger

Wondering whysource ~/.bash_profile is part of the pre-integration trigger? Our ~/.bash_profile includes eval “$(rbenv init -)” command, which starts rbenv (TIP #5). Bots do not load ~/.bash_profile by default and therefore it needs to be explicitly loaded through pre-integration trigger.

TIP #7: Is your team using feature branch workflow? Check out the Buildasaur macOS app, which runs on your server and observes your GitHub/Bitbucket repo. It then creates a new bot and starts integration every time new PR is published.

Fastlane

As mentioned in the previous section, Xcode Server can perform static analysis, run tests and archive your app. It is a very basic set of actions you can use in your integration pipeline. This is where the rich collection of Fastlane actions and plugins comes to rescue. Use Fastlane lanes to compose a sequence of actions, executed as part of your integration.

TIP #8: For deploying an app to iTunesConnect, I found it useful not to use any of the build-in Xcode Server actions and let Fastlane do the heavy lifting instead. Executing fastlane beta (custom lane for beta deployment) will compile the app, increment the version number, upload to TestFlight and notify team. In this case, our bot is quite dull, because its job is to only execute Fastlane.

Bot build configuration
Bot pre-integration trigger with Fastlane execution

Conclusion

Xcode Server has become the invisible member of our iOS team which runs our unit tests on each and every pull request, deploys new beta builds to testers and submits new App Store versions to iTunes Connect — all that automatically or with just 1 click on a button in Xcode. It may take some time to get Xcode Server 8 in place, but it’s totally worth the time and effort.