Continuous Deployment with Travis CI and HockeyApp, Part 1
Continuous integration has become an important topic and a significant part of a developer’s workflow. Finding bugs and ensuring a stable codebase are among the most important benefits of using continuous integration. While these benefits are huge on their own, we can extend our continuous integration setup to actually deploy new builds of our application to our testers. We call this extended setup continuous deployment.
For most app developers, setting up an Xcode server and all of the necessary requirements to run a CI server are more of a hassle and a distraction than anything else. There are a couple of alternatives available to this approach, one of them being Travis-CI, a cloud-based continuous integration solution that conveniently takes away the hassle to setup a server.
In this series of tutorials, I would like to show you how to set up an existing Xcode project with Travis CI for automated builds. Later on, I’ll show you how to automatically distribute your builds using HockeyApp.
Let’s dive right in, starting with Travis CI. The only thing you need in order to follow along is an Xcode project hosted on GitHub.
Introducing Travis CI
Travis CI is basically a virtual machine provider, which gives us a Mac OS X machine with a software stack on top that includes the latest Xcode, Ruby and homebrew. The complete list of available gems, software and commands can be found in the Travis CI docs.
To begin setting up Travis CI, you first need an account. Fortunately, Travis can use your GitHub account and you’re done with that part in one click. Go to travis-ci.org, click on “Sign in with GitHub” in the upper right corner and log in with your GitHub Account.
Once you’re logged in with your GitHub account, Travis shows you your latest CI build in the form of a dashboard. The navigation is divided into three parts. The left sidebar displays your active CI projects with some quick info, the header above gives you some navigation possibilities with your personal settings on the right, and the main area in the center shows your currently selected CI project with the latest build log under the ‘Current’ tab.
Settings for your selected CI project are available through the gear icon on the upper right side, next to the ‘build passing/failed/unknown’ button. All the information here is synchronized with GitHub, so new pushes lead to a new build under ‘Build History,’ and any errors or simple logging output can be found in the Terminal-style dark box when you select a build.
There’s also an option to build your private repositories, though that’s a feature included only in paid plans, as the free plan is meant to support open source projects. Private repositories are configured through travis-ci.com.
Get Your Repo Up to Speed
Once your repositories are fetched, you can enable your project for use with Travis by using the “ON/OFF” slider. Once you do that, Travis will start building the project even though Travis might not know how to properly do so just yet. I prefer to hold off Travis when no configuration file is available, by selecting “Build only if .travis.yml is present” in the project settings.
To do so, click on the ‘+’ sign in the left sidebar, make sure your desired identity is selected and then activate your repository on the right by using the ON/OFF slider. Next, click on the wrench symbol next to the switch and you’ll get redirected to your project’s settings page. Activate the ‘Build only if .travis.yml is present’ option.
This brings us to the core configuration of Travis, the .travis.yml file. All settings, instructions and customized routines are set through Travis’s .travis.yml file, which has to be placed in your repository’s root. Therefore, we go forward and create an empty file with the name ‘.travis.yml’ in our repository’s root, so Travis continues to build our project.
Note that you might not see this file through your file explorer, as most operating systems hide files with a dot prefix. Go ahead and commit that file to your repo, and Travis will continue to build it. Either use your favorite text editor or use ‘cd {yourRepositoriy/Path/} // touch .travis.yml’ to create an empty .travis.yml file in your repo. Commit and push to GitHub.
You can and should check the existence of your .travis.yml on your GitHub’s overview page, as shown in the screenshot below.
Note: Travis’s configuration file follows a customized YAML syntax, which can easily break. When writing the configuration file, be sure to use soft tabs (spaces) instead of real tabs and try to remind yourself that this will be parsed through bash when using whitespaces in commands.
Shared Xcode Schemes
Before we move along to the actual build process, we have to configure our project in Xcode to make our build targets available on the server. So far, the build targets have been created automatically, but that doesn’t happen on the server. To make our targets available on the server, we have to declare them as ‘Shared’ targets.
To do this, open your project in Xcode, click on the project’s name next to the Build & Run Button, where you normally select your deployment target, and select ‘Manage Schemes’. You should have at least two targets, one for your actual app and one for your Unit Test target as shown in the screenshot above. If not, go ahead and create them either through the ‘+’ Button or the ‘Autocreate Schemes Now’ Button.
Commit and push to GitHub. Make sure that your commit contains your shared scheme. It’s a common mistake for people to exclude the shared scheme through their .gitignore.
Under the Hood
At this point, you might ask yourself what Travis performs to build your project. As mentioned before, Travis is basically a VM provider and lets you modify its routine through bash scripts in order to build your projects. It’s no wonder that Travis uses Xcode’s command line tools to perform all actions on your project. The main command is ‘xcodebuild’, which is invoked with some basic parameters. The basic pattern goes like this:
xcodebuild -project {XCodeproject.xcodeproj} -scheme {SchemeNem} -sdk {SDKname}
In our specific case, that command would look like this:
xcodebuild -project ContinuousDistributionExample.xcodeproj -scheme ContinuousDistributionExample -sdk iphonesimulator
The required parameters are -project {Path to your .xcodeproj}, -scheme {Your projects scheme name} and -sdk {SDK name}. If you have a Xcode workspace instead of a project, exchange the -project parameter for a -workspace parameter.
As with most, if not all, of the commands shown here, you can test these commands locally before testing them on Travis CI. For this specific command, just open your terminal and naviagte with ‘cd’ into your repository and execute the same xcodebuild command. That way, you can see if your command works, and if there are any typos or other problems.
A lot could be said here about how to use xcodebuild, but as there are more advanced tools that work as a wrapper to xcodebuild, we’re going to move along and use ‘xctool’ to replace it. In any case, you should make sure that your Xcode command line tools are working and up to date. You can check that by performing ‘xcode-select –version’ in your terminal.
The Basic Setup
Now that you know how the xcodebuild command works, let’s start using it with Travis CI.
Modify the contents of your ‘.travis.yml’ to match this code snippet, while making the appropriate customizations for your parameters:
# file: .travis.yml
language: objective-c
script:
- xcodebuild -project ContinuousDistributionExample.xcodeproj -scheme ContinuousDistributionExample -sdk iphonesimulator
Travis CI defines a minimal configuration for an Objective-C project to have the keys language
, xcode_project
and xcode_scheme
defined. If you try to use these, Travis will use your project’s default settings, which have a high chance to fail on Travis CI without prior changes.
Commit the file, and push your changes to your GitHub repo. If you put in the correct names and paths and you committed your configuration back to GitHub, Travis CI should start building your project. Make sure that the build passes at this point, so we can customize our routine from here. You can tell that your build is passing from either the log by clicking on the ‘Build History’ tab, or after refreshing the website from the ‘build: status’ button at the upper right section.
XCTool
xctool is a wrapper for Apple’s xcodebuild, which we were using before. The main purpose for switching from xcodebuild to xctool is to make it easier to build and invoke unit tests, as well as to have a wrapper around xcodebuild to make general usage easier. Therefore we make the transition to ‘xctool’.
xctool is available through homebrew. I recommend installing it on your local machine. If you have homebrew installed, open Terminal and execute ‘brew update && brew install xctool’ to install it on your local machine.
xctool can be used with the following parameters:
xctool -workspace {Path.xcworkspace} -scheme {SchemeName} -sdk {SDK}
XCtool also automatically compiles your CocoaPods if they are part of your workspace.
For our example project, the build command looks like this:
xctool -workspace ContinuousDistributionExample.xcworkspace -scheme ContinuousDistributionExample -sdk iphonesimulator
Similar to the parameters xcodebuild uses, again, if you have a project instead of a workspace, replace ‘-workspace’ with ‘-project’.
To run our unit tests we use this command:
xctool test -workspace ContinuousDistributionExample.xcworkspace -scheme ContinuousDistributionExampleTests -sdk iphonesimulator
Now we need to put these commands in the right place within our .travis.yml file. Travis has a special build lifecycle that requires us to use the ‘script’ section for this purpose.
Change your .travis.yml file to look like this:
# file: .travis.yml
language: objective-c
script:
- xctool -workspace ContinuousDistributionExample.xcworkspace -scheme ContinuousDistributionExample -sdk iphonesimulator
- xctool test -workspace ContinuousDistributionExample.xcworkspace -scheme ContinuousDistributionExampleTests -sdk iphonesimulator
Note that if your Xcode project lives in a subfolder in your repo, be sure to give the full path from your repository’s root, such as ‘ContinuousDistributionExample/ContinuousDistributionExample.xcworkspace’. The ‘.’ notation for the current folder to use relative paths doesn’t work here and will result in errors. Make sure to apply this to all paths in a subfolder through the course of this series. If you need to use absolute paths, you can use the ‘$PWD’ env variable to get your current working directory, then expand the path further from there.
Updating Software && Dependencies
As mentioned before, Travis CI provides the CocoaPods gem within their OS X environment. If you put your Podfile in the same directory as your .travis.yml file in your project’s repo, Travis will automatically install all your pods without any further configuration.
We want to make sure we have the latest version of CocoaPods and other software we’re using, so let’s use this spot in the build lifecycle to update Cocoapods and XCtool. Travis CI recommends using their ‘before_install:’ hook to execute these commands. Add this code snippet to the end of your .travis.yml file:
# file: .travis.yml
before_install:
- sudo gem install cocoapods
- brew update
- if brew outdated | grep -qx xctool; then brew upgrade xctool; fi
Commit and push your changes. Travis should make a new build. Make sure everything works and your builds are passing by consulting Build History->Build.
Note: We want to upgrade xctool only if it’s outdated, because the chances that we’ll run into some brew link errors are quite high. While writing this post, I ran into a few problems with outdated software and iOS 8 due to recent releases of iOS and Xcode. If you get an error on Travis CI and not on your local machine, check the version numbers and consider updating to the same version.
If you want to check your results at this point or just get a preconfigured version, you can check out the minimal version.
Congratulations—you just integrated your first project using Travis CI! It should automatically build now and let you know once a commit breaks the building process.
Coming Up
In the next part of this series, we’re going to set up code signing on Travis CI so that we can deploy our releases later on.