Archive for the ‘iOS Continous Integration Series’ Category

Continous Deployment for Apps via testflightapp

January 26, 2012 Leave a comment

The benefits of continous integration are widely known. By extending the ideas of continous integration to the full software lifecycle, continous delivery becomes an inevitable practice. Especially in the context of managing a beta program for mobile devices, to most of which I as a developer have no physical access, the ability to have fully automated deployments is crucial. provides a great service to iOS developers by managing app provisioning and deployments. They do also provide easy to use instrumentation facilities.

Continous deployment with is a breaze, all you need your build-server to do is interact with a straightforward web-api to upload your ipa packages.
Here’s the script I’m using for RowMotion:


# tokens


SIGNING_IDENTITY="iPhone Distribution"

# calculated vars

# kill artefacts directory

# compile
echo "##teamcity[compilationStarted compiler='xcodebuild']"
xcodebuild -workspace RowMotion.xcworkspace -scheme RowMotion -sdk iphoneos5.0 -configuration Release build archive

if [[ $buildSucess != 0 ]] ; then
echo "##teamcity[message text='compiler error' status='ERROR']"
echo "##teamcity[compilationFinished compiler='xcodebuild']"
exit $buildSucess

echo "##teamcity[compilationFinished compiler='xcodebuild']"

echo "##teamcity[progressMessage 'Creating .ipa for ${PRODUCT_NAME}']"

DATE=$( /bin/date +"%Y-%m-%d" )
ARCHIVE=$( /bin/ls -t "${HOME}/Library/Developer/Xcode/Archives/${DATE}" | /usr/bin/grep xcarchive | /usr/bin/sed -n 1p )

/usr/bin/xcrun -sdk iphoneos PackageApplication -v "${APP}" -o "${OUT_IPA}" --sign "${SIGNING_IDENTITY}" --embed "${PROVISIONING_PROFILE}"

echo "##teamcity[progressMessage 'Zipping .dSYM for ${PRODUCT_NAME}']"
/usr/bin/zip -r "${OUT_DSYM}" "${DSYM}"

# prepare build notes
NOTES=`hg tip`

echo "##teamcity[progressMessage 'Uploading ${PRODUCT_NAME} to TestFlight']"

/usr/bin/curl "" \
-F file=@"${OUT_IPA}" \
-F dsym=@"${OUT_DSYM}" \
-F api_token="${API_TOKEN}" \
-F team_token="${TEAM_TOKEN}" \
-F notes="${NOTES}" \
-F notify="True" \
-F distribution_lists="Private"

Make sure to adapt the script to your requirements. One trick I’m fond of is automatically providing SCM information in the build notes (the step with executing hg tip does just that).
For deployments I’m using two lists, a private one to which all builds will be published, and a public one to which I can selectively deploy. What’s so great about is that it will automatically send emails to notify my testers about the new build and will then allow me to monitor installs.

Retrieving Coverage Information – LLVM, CoverStory and Teamcity

August 10, 2011 1 comment


This time, we will set up automated code coverage metrics retrieval from our unit and integration test suites. When I started writing my iOS Continous Integration Series, we needed to use GCC and GCov to generate coverage information. Fortunately, this has changed and starting with iOS 5 Beta 4 XCode ships a version of LLVM that is capable of generating coverage information.

We will use CoverStory to create a (not-so-pretty but useful) HTML report of our coverage information and teamcity will pick this up and display it in the Build Result tab.

Collecting Metrics

Setting up LLVM to generate coverage information is easy. The following is an augmented version of the instructions found at the CoverStory Wiki:

  1. Edit all test targets, add -fprofile-arcs and -ftest-coverage to Other C Flags
  2. Edit all test targets, select the “Build Phases” tab and add /Developer/usr/lib/libprofile_rt.dylib to the stage “Link Binary WIth Libraries”
  3. Build and run, you should see *.gcda and *.gcno files generated in your bin directory

HTML Reports with CoverStory

Once the Build Script compiled the product and executed all the tests, we need to pick up the coverage information generated. From this data, we need to generate a report that shows the number of lines covered. I’m using CoverStory to do just this. Unfortunately, it doesn’t come with a command line interface, so I had to resort to some hacked AppleScript to drive the process (note that I checked in CoverStory in my source tree under tool/ The script takes two command line parameters, the first being the directory to search for coverage information, the second being the output directory for the generated html report.

on run argv
	tell application "tool/"
		set x to open (item 1 of argv)
		tell x to export to HTML in (item 2 of argv)
	end tell
	return item 1 of argv & "|" & item 2 of argv
end run

Ok, so that’s all nice and sweet. However, there is another issue I hit with CoverStory (which has already been filed as a bug with the maintainers). Basically, CoverStory would startup and freeze upon opening your coverage information. The Link also has a patch fixing it, that did not make it’s way into an official release yet. I do have a private build of CoverStory with the fix applied, If you are feel unable to build CoverStory yourself email me and I’ll be happy to provide you with my build. I hope this becomes obsolete very soon.

Alright, where did we left off? We have an apple script for collecting coverage information. This needs to be included in our build script. So our Shell based build script looks like this now:

# build here

# test here

# collect coverage results
echo "##teamcity[progressStart 'Collecting Coverage results']"
osascript coverStory.applescript $PWD $PWD/Artefacts/Coverage
echo "##teamcity[progressFinish 'Collecting Coverage results']"

Note that $PWD denotes the current working directory, i.e. the directory the build script is executed from. CoverStory will generate  a HTML based report and put it in our Artefacts directory in a subdirectory called Coverage. Next, we’re going to make Teamcity pick up this report.

Integrating Coverage Reports with Teamcity

Teamcity does an admirable job at managing artefacts. As a first step, we need to edit our build configuration and add an Artifact Path: “ =>”. This will cause TeamCity to take the Coverage Report and put it into a archive that it sucks in and stores along the build results.

Next, we edit the Teamcity Server Configuration, chose “Report Tabs” and hit “Create new report tab” with the following settings:

  • Tab Title: Code Coverage
  • Base path:
  • Start page: index.html

Teamcity will display an iframe with the contents of index.html of the artifact we created in the previous step. If everything went well, your build results should look like this now:

Mercurial Server using hgweb.cgi on Ubuntu

June 30, 2011 Leave a comment

In a previous post, we set up a virtual machine template for an ubuntu server. Now that we have set up a clone of this machine, it is time to set up a Mercurial repository server


Mercurial provides an easy to use repository server via a python cgi script. Mercurials protocol facilitates fast transfers over http, making it superior over an ssh solution based (such as git) when considering minimal protocol overhead vs. ease of use. As my webserver of choice, we will use lighttpd. This guide will follow the instructions published here

Installing Python

This one is simple:

ubuntu@localhost: sudo apt-get install python

Installing Lighttpd

To install lighttpd, run:

ubuntu@localhost: sudo apt-get install lighttpd

Next, we need to create a specific configuration for our mercurial cgi script. We need to redirect all incoming requests to the cgi script, and then we apply some url rewrite magic to remove the ugly hgweb.cgi from our URLs. The hgweb.cgi script will be served from /var/www/hgweb.cgi. If you use a different location, make sure to chown it to www-data and chmod+x it (all described in the mercurial wiki). I created my config like this:

ubuntu@localhost:~$ sudo vi /etc/lighttpd/hg.conf
ubuntu@localhost:~$ cat /etc/lighttpd/hg.conf 
url.rewrite-once = (
  "^([/?].*)?$" => "/hgweb.cgi$1",
   "^/([/?].*)?$" => "/hgweb.cgi$1"

$HTTP["url"] =~ "^/hgweb.cgi([/?].*)?$" {
             server.document-root = "/var/www/"
             cgi.assign = ( ".cgi" => "/usr/bin/python" )

Next is the lighttpd config, that will need to reference our hg.conf and enable mod_cgi:

ubuntu@localhost:~$ cat /etc/lighttpd/lighttpd.conf 
include "hg.conf"

server.modules = (

Further configuration Tricks

You should force hgweb.cgi to serve UTF-8 content. Fortunately enough, this is as simple as adding (or uncommenting) the following lines to hgweb.cgi:

import os
os.environ["HGENCODING"] = "UTF-8"

You will also need a hgweb.conf right next to hgweb.cgi and reference it from there (again, described in the mercurial wiki). For reference, my configuration includes all repos sourced under /var/hg/repos (and subdirectories) and allows anonymous push (I’m authenticated via VPN policy):

ubuntu@localhost:~$ cat /var/www/hgweb.config
/ = /var/hg/repos/**

baseurl = /
allow_push = *
push_ssl = false

Final Words

That’s all there is to it. To make the server available via DNS, you need to make sure the servers hostname is registered with your local DNS. In my case, I simply added a static record to it.

TeamCity Server on Ubuntu

June 18, 2011 7 comments

Last time, we set up a virtual machine template for an ubuntu server. Now that we have set up a clone of this machine, it is time to set up Teamcity on it.


Teamcity on linux is meant to be run from its integrated Tomcat server. We will use the default Teamcity installation procedure in combination with the lightweight lighttpd to act as a front end server listening on port 80 and forwarding requests to Teamcity’s Tomcat installation. This setup is both, easier than configuring Tomcat on Port 80 (remember it requires root permissions to allocate) and we could add authentication or https access more easily later (though I will not do that for now).

Installing Teamcity

To install Teamcity, follow the instructions from JetBrains, which can be found here (takes less than 10 minutes). I chose to install mine at /var/TeamCity: 

Follow the instructions to set up your external database (recommended approach). I am using a SQL Server 2008 Installation that is already present and regularly backed up in my “private cloud”.  Edit your server.conf to configure a port for the Tomcat Server :

ubuntu@localhost: sudo vi /var/TeamCity/conf/server.xml

Permissions are a chore, but we don’t want the Teamcity Server directory to be owned by our admin user, so we change the owner of our Teamcity install directory to the default www-data user.

ubuntu@localhost: sudo chown -R www-data /var/TeamCity

Next, we want Teamcity to start automatically when the server is booted, so we add a small init script. Be sure to adjust the TEAMCITY_DATA_PATH environment variable to a static directory of your choice, otherwise TCs default will make end up in www-data’s home directory, which is, frankly, a very inconvenient location.

ubuntu@localhost:/var/TeamCity$ cat /etc/init.d/teamcity 
# /etc/init.d/teamcity -  startup script for teamcity
export TEAMCITY_DATA_PATH="/var/TeamCity/.BuildServer"

case $1 in
 start-stop-daemon --start  -c www-data --exec /var/TeamCity/bin/ start

 start-stop-daemon --start -c www-data  --exec  /var/TeamCity/bin/ stop


exit 0

Now we need to register the startup script to run automatically:

ubuntu@localhost: sudo update-rc.d teamcity defaults

Next, we start the server manually (you can reboot too):

ubuntu@localhost: sudo /etc/init.d/teamcity start

Installing Lighttpd

Now we need to install lighttpd:

ubuntu@localhost: sudo apt-get install lighttpd

And configure it to forward requests from port 80 to the port we configured for Tomcat (8080 in my case).

ubuntu@localhost: sudo vi /etc/lighttpd/lighttpd.conf
server.modules = (
#       "mod_rewrite",

$HTTP["host"] =~ "" {
        proxy.server = (
                "" => (
                        "tomcat" => (
                                "host" => "",
                                "port" => 8080,
                                "fix-redirects" => 1

Final Words

That’s it. By now you should have a running teamcity server. If something goes wrong, be sure to check the logs which can be found at /var/TeamCity/logs. To make the server available via DNS, you need to make sure the servers hostname is registered with your local DNS. In my case, I simply added a static record to it.

Ubuntu Server on HyperV

June 17, 2011 Leave a comment

As my Linux distro of choice for a set of lightweight virtualized Servers, Ubuntu Sever provides several advantages that made me go for it:

* Driver support for HyperV
* Active Community, number of HowTo’s available considered superior to Debian
* Packages available are cutting edge
* Well documented
* Good experience with Ubuntu Desktop

At the time of writing this, there are two choices of Ubuntu Server: Ubuntu 10 with LTS or cutting edge Ubuntu 11. LTS stands for LongTermSupport and Cannonical guarantees there will be updates for at least 3 years. It’s a matter of preference, but I chose Ubuntu 11.

To install it in HyperV, I recommend you follow this guide:

Of course, you should adapt your network configuration to your requirements. Before templating this machine, I installed openssh because I consider it a core part of my server administration toolkit.

By this point, we should have a core installation of Ubuntu Server template that is ready to be cloned. Depending on your virtualization solution of choice, different steps apply here. (In HyperV it is as simple as exporting and re-importing the machine.) Make sure you create a unique copy of the machine, so that its network adapter gets assigned a new MAC Adress (everything else is calling for trouble).

After instantiating your template, we now need to customize that template:

1. Change hostname
 sudo hostname "NewHostName"

2. Configure Networking (it is likely your adapter will now show up as eth1 instead of eth0, rember that instantiating a VM tempate involves changing the MAC adress of the server)

sudo vi /etc/network/interfaces

3. Change user name/password

sudo passwd

4. Reboot

sudo shutdown -r now

iOS Development Continous Integration Setup

June 7, 2011 Leave a comment

After almost a year of absence, I’m in the middle of getting back to iOS Development. Since my departure from the Apple ranch, a lot has changed and new developer tools have emerged. Professional software development has become significantly easier on this platform, but I feel the tooling still isn’t on par with what other ecosystems provide.

Nonetheless, where there’s will there’s a way. In this Series of posts, I’m going to outline the setup I’m currently running. I’ll point to resources that helped me along the way and will describe how to combine the pieces to make it all fit together. I don’t plan publishing in any particular order and since business goes first, I am going to write when I find the time for it.

All posts of the series will be put into the category iOS Continous Integration Series, which also is the best place to find them.

The setup will consist of:
The Dev’s Private Cloud (aka a HyperV Server) hosting:
– Teamcity CI Server
– Build Agents
– Mercurial via hgweb.cgi

Kiwi for Acceptance Testing
OCMock as Isolation framework
GCov for Code Coverage

Planned Posts:

Running Ubuntu Server in HyperVi
Setting up a TeamCity Server on Ubuntu
Setting up a Mercurial Server
iOS Testing Frameworks revisited
Running OCUnit on a build agent
Connecting OCUnit to Teamcity
Integrating OCMock
Using Kiwi for Acceptance Testing
Retrieving Coverage information with GCov
XCode Alternatives

%d bloggers like this: