I’ve just finished setting up a Jenkins instance at home to automate aspects of my developent workflow. I haven’t lived with this system for long but my hope is at least to accomplish the following;
Automated builds … so I can
- provide an rss feed with regular a alpha or beta build for testers
- know when a change I make to my AppStore version of DropSync breaks the direct download version or vice versa
Automated tests … so I can
- run tests that I can’t realistically run every time I hit build in xcode
- run tests that launch my app and modify app data in a clean or specified environment
- actually get some use out of my tests (you have to actually run them to do that)
Solve all my problems and achieve a state of blissful enlightenment
Here’s how I set things up. Although I’ve got all this working I haven’t really road tested it yet so I might decide to change some of this down the road.
1. Create shell scripts
Generally speaking everything Jenkins does should be boiled down to commands that can be run in a shell script. I have two main jobs I want to run, building my app, and testing my app so I created shell scripts to perform these tasks that I could ask Jenkins to invoke. I added these to my source code repository.
My xcode build script is very simple it looks like this;
#!/bin/bash #Scheme should be given as a commandline argument. eg build.sh DropSync\ App or build.sh DropSync scheme=$1 schemebuildroot=$HOME/.xcbuildroot/$scheme mkdir -p $schemebuildroot/dst mkdir -p $schemebuildroot/sym mkdir -p $schemebuildroot/obj xcodebuild clean build archive -scheme $scheme DSTROOT=$schemebuildroot/dst SYMROOT=$schemebuildroot/sym OBJROOT=$schemebuildroot/obj
The script takes one argument which is the scheme I want to build, so I can use the same script to build my app two different ways for example;
build.sh DropSync build.sh DropSync\ App # My Appstore version which has a different scheme
Another choice I made with this script was to use ~/.xcbuildroot/ as the build root. The reason for this choice is so that my built products will allways be in a predictable place on both my normal user account and for the Jenkins user. You may find that this isn’t necessary for your setup, but in my case I wanted to run Applescript and JSTalk based tests using my built app, and needed to be able to provide the path to the built app to those scripts. By default xcode places build products under a directory like
~/Library/Developer/Xcode/DerivedData/<ProjectName>-<hashstring>/ which means that finding paths to built executables requires some breakable logic (eg look for a directory that starts with ProjectName, but what if two exist and what if Apple changes this location). To avoid all this I just use a fixed directory relative to the user and make sure that I don’t use conflicting scheme names.
2. Setup Jenkins
I’m recording my setup steps here for posterity, as documentation for myself and in the hope that others find it useful. Bear in mind though that I’m pretty much a total novice Jenkins user so this might be a long way from the ideal setup. It seems to work for me so far though.
- Create a separate user for Jenkins and login as that user
Download Jenkins and start it up
mkdir ~/JenkinsApp cd ~/JenkinsApp curl -L http://mirrors.jenkins-ci.org/war/latest/jenkins.war -o jenkins.war java -jar jenkins.war
Install the git, github and github OAuth plugins using Jenkins’ admin interface. I did this because I host my projects on Github and wanted Jenkins to kick off a build each time I push some changes.
Login to github and
- register Jenkins as an application so that the OAuth plugin can work.
- setup a service hook for the Jenkins Github plugin on relevant github projects. The Hook Url should be something like http://yourdomain:8080/github-webhook/
Enter the Jenkins configuration Menu (Jenkins -> Manage Jenkins -> Configure) and;
- Enable Security
- Use github Authentication and settings for the github Authentication plugin
- Github committer authorization
- Enable github webhook and allow Jenkins to manage the URLs
3. Create a Jenkins Job
I created a Jenkins job to simply build my app every time a change is pushed to github. Initially I used the Jenkins xcode plugin for this, but eventually migrated to simply running my own build script. I miss out on some goodness with this, especially the fact that Jenkins will create reports from test results. Ultimately though, I figure that if a test fails I simply need to know about it, and I can then investigate the issue directly in xcode without requiring Jenkins. The advantage of using my own build script is that I can use the same script on my development account as Jenkins uses, and this should make troubleshooting easier. Later I’ll talk about my whole-app testing strategy which will make use of this script as a precursor to running tests.
So that’s my Jenkins setup so far. At this point I haven’t really gained very much with my simple build job, but I hope to write another post soon about the next steps in my automation and testing strategy where I try to implement some Applescript whole-app tests.