Continuous Integration with Jenkins
I don’t think I should go all over the “Why is continuous integration important?” topic all over again!, there are many great articles about it ( I recommend you to read Build Server: The Hearbeat of The Project by Noel Llopis ). So, we know that continuous integration is great, it avoids several things that can slow us down, or can even degrade the quality of our products.
Some of this things are:
- Making sure our project builds on all platforms.
- Making sure the Unit Test and Integration Tests pass on all platforms.
- Creating different build setups for Beta Testers / Production / Debugging
- Letting the person that broke the build know where did it happen immediately.
This things are great when working alone as a “Single Man Team”, but I think continuous integration is a must when working on a 2+ persons team. Seriously, I can’t count the number of hours saved by using C.I, it’s just so great to be able to send the beta testers a URL address so that they can get the “goods”
.
For this week iDevBlogADay post, I will talk about how I use Jenkins to build Xcode projects. Remember that you can always follow me on Twitter if you want to contact me directly
Jenkins
So after that short speech, let’s get into Jenkins… who is this Jenkins dude ?
Jenkins was previously known as Hudson, is an open source C.I tool written in Java. The project had to change it’s name after having a dispute with Oracle, they claimed that owned the right to trademark the previous project name and actually applied for the trademark on December 2010. It’s even crazier that Oracle, decided to continue the development of hudson, so right now 2 forks of the project exists.
Installing
Jenkins is really easy to install under several platforms ( Including OS X, you’ll need Java on Lion though ), our main C.I server runs under Ubuntu 10.04.2 LTS ( with xen kernel… ). And installing it couldn’t be easier, basically this 4 lines made it happen:
wget -q -O - http://pkg.jenkins-ci.org/debian/jenkins-ci.org.key | sudo apt-key add - sudo sh -c 'echo deb http://pkg.jenkins-ci.org/debian binary/ > /etc/apt/sources.list.d/jenkins.list' sudo aptitude update sudo aptitude install jenkins
If you’re under a different platform, you can actually get a binary here. You can also find more details about the installation, like how to change the default port to serve Jenkins here.
Xcode project + Jenkins
It’s quite easy to build an Xcode project with Jenkins, there are several ways to do it. The first thing to note, is that even though our Jenkins server runs Ubuntu 10, Jenkins allows the developer to create several “build nodes”. So for example I could have different nodes to build an iOS Project, an Android Project and a Windows Phone 7 Project ( not coming soon though
).
Creating an OS X node on Jenkins is extremely easy, maybe too easy
. The only previous required knowledge is to know how to setup “passwordless SSH login”, this is because Jenkins has a built-in SSH client implementation that it uses to talk with remote sshd and start slave agents. When Jenkins is installed, you can go to “Manage Jenkins”, then Manage Nodes, and then click the “New Node” button. Here the important thing is that you’ll supply the connection information, like slave host name, user, and ssh key. For this to work, you will need to copy the master’s public ssh key into the ~/.ssh/authorized_keys file. The great thing, is that after that Jenkins will do the rest of the work, it will copy the “node binary” to the remote machine via ssh and do the rest of the configuration.
So, to build Xcode projects with Jenkins there are a couple of alternatives, the first one is the Xcode Plug-In for Jenkins. I haven’t personally tried the plug-in, but I think it could be worth a shot.
The way I’m using Jenkins to build Xcode Projects by creating a new “Free Style Software Project” (in the New Job Menu ), be sure to specify your repository URL or SSH adress (ssh://user@host/path/to/project.git), then check the box “this build is parameterized” and set it up like this (click the image to enlarge):
So basically I created a new Parameter Called “CONFIGURATION” with two Choices, Debug and Release, for a basic setup we need to specify that we are running a script to build our project, I found this script about a year ago in Stack Overflow, but I haven’t been able to find it again ( I think the question may have been deleted ). I may have changed a line here or there but it’s mostly the same (direct download):
# !/bin/sh # By Anonymous user on Stack Overflow # You need to fill your password function fail { echo "$*" >&2 exit 1 } function section_print { echo "\n=== $* ===" } section_print "Building $CONFIGURATION"; if [ -z $CONFIGURATION ]; then fail "No configuration specified"; exit 1; fi #strange way to force backslash if [ $PROJDIR ]; then PROJDIR=${PROJDIR%/} PROJDIR="$PROJDIR/" section_print "Project Dir is specified and is $PROJDIR" cd "$PROJDIR" || fail "no directory $PROJDIR" pwd fi section_print "Unlocking keychain" security unlock-keychain -p "password" ~/Library/Keychains/login.keychain section_print "Cleaning build" xcodebuild -configuration "$CONFIGURATION" clean || fail "Clean failed" section_print "Building $CONFIGURATION" xcodebuild -configuration "$CONFIGURATION" -sdk iphoneos4.2 || fail "Build failed" section_print "Packaging ipa" BUILD_PATH=build/"$CONFIGURATION"-iphoneos #we are already in the $PROJDIR cd "$BUILD_PATH" || fail "no directory 'build/$CONFIGURATION-iphoneos'" for file in "*.app" do APP_NAME=`echo $file` done APP_NAME=${APP_NAME%.*} section_print "Removing old artefacts" cd "$WORKSPACE" rm -f *.ipa rm -f *.dSYM.zip rm -f *.mobileprovision #take $PROJDIR into account section_print "Application name $APP_NAME" cd "$PROJDIR$BUILD_PATH" rm -rf Payload rm -f "$APP_NAME".*.ipa mkdir Payload cp -Rp "$APP_NAME.app" Payload/ if [ -f "$WORKSPACE"/iTunesArtwork ]; then cp -f "$WORKSPACE"/iTunesArtwork Payload/iTunesArtwork fi IPA_FILE="$APP_NAME.$BUILD_ID.$BUILD_NUMBER.ipa" zip -r "$IPA_FILE" Payload section_print "Compressing dSYM" rm -f "*.dSYM.zip" DSYM_FILE="$APP_NAME.$BUILD_ID.$BUILD_NUMBER.dSYM.zip" zip -r "$DSYM_FILE" "$APP_NAME.app.dSYM" section_print "Exporting profile" cp -f "$APP_NAME.app/embedded.mobileprovision" profile.mobileprovision section_print "Get the profile name" PROFILE_NAME=`strings ./profile.mobileprovision | grep -A1 'Name' | tail -n1 | awk -F'' '{print $2}' | awk -F'' '{print $1}'` PROFILE_NAME=${PROFILE_NAME//'*'/} PROFILE_NAME=${PROFILE_NAME//'?'/} PROFILE_NAME="$PROFILE_NAME.mobileprovision" section_print "Profile name is $PROFILE_NAME" mv -f "profile.mobileprovision" "$PROFILE_NAME" rm -f "$WORKSPACE/*.mobileprobision" || fail "Failed to remove $WORKSPACE/*.mobileprovision" rm -f "$WORKSPACE/$IPA_FILE" || fail "Failed to remove $WORKSPACE/$IPA_FILE" section_print "Copying $IPA_FILE to artifacts" cp -v "$IPA_FILE" "$WORKSPACE/." || fail "Failed to copy ipa" cp -v "$PROFILE_NAME" "$WORKSPACE/." || fail "Failed to copy profile" cp -v "$DSYM_FILE" "$WORKSPACE/." || fail "Failed to copy dSYM" section_print "Build succeeded"
This script will create artifacts like an IPA, a Profile and a DSYM file, which could be useful for distribution, you need to check the box “Archive the artifacts”, which is a post-build action of Jenkins, and basically set it up like this:
If you followed the instructions correctly, you should have Jenkins up and running and capable of building an Xcode project. If you want to setup more Xcode projects, Jenkins has a very nice feature to duplicate and edit an existing “Job”, so you won’t have to repeat all the previous steps over and over again.
This is it for this week!, I hope you have enjoyed scratching the surface of Jenkins… I encourage you to look more things that Jenkins is capable of doing, it’s a very powerful tool!






Dmytro September 4th
Thanks for the article. I’m using Hudson for CI for iOS projects and I agree it’s a very useful tool.
I’ve got a problem with Mac OS X Server and keychain after server rebooting http://bit.ly/mSbPY5 which I still can’t solve.
BTW Is there any particular reason to create IPA files manually? There is the utility called xcrun which does it for you.
ASIM April 30th
Where we have to place this build.sh script?
ARUN KUMAR PADMA July 19th
Hello,
I need to integrate jenkins with teamforge. please could some one help the process need to follow.
Thanks,
Arun
sejo April 4th
i miss the images
Add Yours
YOU