Continuous Integration (CI) is a tool that mature development languages have employed for a long time. It provides the benefits of constantly compiling code and exercising it via unit testing to ensure developers are aware of integration issues at the earliest possible moment. Additionally, CI systems provide an automated and consistent process of building and deploying applications, removing the risk and effort associated with manual deployment processes.
For a few years we have seen an influx in the desire for mobile application development in the enterprise setting. Tooling to support these applications has been sparse, but is catching up. We now have the ability to take full advantage of CI and all the benefits it provides.
There are many tools available for this purpose but at Cardinal we have standardized on Jenkins http://jenkins-ci.org. Jenkins is a tool born out of necessity and forged by the community over time. Jenkins is highly extensible, which is why it lends itself to mobile development so well. The basic build process follows the following steps:
- Poll Source Control Management (SCM) - Jenkins supports just about every SCM system. At Cardinal we use a mix of SVN and Git for iOS development.
- Checkout from SCM if there has been a change since the build.
- Perform any necessary pre-build steps. These can include things such as readying build configuration files or cleaning the target directories.
- Build the application.
- Test the application.
- Perform any post-build steps.
The process does not change for mobile applications, but rather a different set of tools are employed for performing builds.
Jenkins is written using the Java runtime environment so it can run in nearly any environment. The simplest installations only require downloading the jenkins.war file from http://mirrors.jenkins-ci.org/war/latest/jenkins.war and running by executing the command:
java –jar ./jenkins.war
This will fire up Winstone, a minimalist java servlet container.
See:http://winstone.sourceforge.net/) on port 8080 on the local host. Jenkins will be available as the root context and accessible by opening a browser to http://localhost:8080/. Re-starting Jenkins manually each and every time the server must restart is not desirable, so Jenkins-ci.org has also prepared several native packages available on the Jenkins homepage, http://jenkins-ci.org/, that will install Jenkins as a service on the various operating systems. Alternatively, if your server already has a J2EE compliant application server running, you can install the jenkins.war into it.
Building iOS Applications
Building iOS applications requires the use of a Mac, and even in the CI realm this is no exception. Luckily Jenkins is built on top of Java and can run in nearly any environment. Before we start configuring the build process we need to be sure to have some aspects of the environment set up. First and foremost we need to have XCode installed on the host machine. This includes installing any iOS sdk versions that your application may be dependent on. Secondly, the development, distribution profiles, and identities should be installed on the host machine. You can use a combination of XCode and Keychain Access to help with this process.
Once the machine has been prepared with all of the pre-requisites we are now ready to configure Jenkins. Open up a browser and navigate to the Jenkins application, you should see a page very similar to this:
The first few things we'll want to do is configure Jenkins as a whole by clicking on the Manage Jenkins link. Here we'll want to set up any security preferences, email SMTP configuration, and install the plugins we'll need to build the iOS application.
To build for iOS we need at minimum the XCode plugin (https://wiki.jenkins-ci.org/display/JENKINS/Xcode+Plugin). You can install this by clicking the Manage Plugins link within the Manage Jenkins page, changing to the available tab, checking the box to the left of the XCode plugin, and then clicking the Install without restart button near the bottom of the screen. Jenkins will take it from there. You may also want to take this time to install any other plugins you might need depending on your environment, like the Git plugin (https://wiki.jenkins-ci.org/display/JENKINS/Git+Plugin) for SCM.
Now, we're ready to configure our first iOS job. Start by clicking the new job icon in the navigation section in the top left of the screen. Jenkins will prompt you for a job name, and ask what style of job do you want to execute. Since Jenkins is able to build for many different languages, there will be several options, we however want to select the Build a free-style software project option after entering a project name and then click OK.
We are immediately taken to the configuration screen. You may want to add a short description of the job, but we'll jump ahead to setting up SCM. For the purpose of this example we're going to use subversion, which is integrated into Jenkins by default. By selecting the Subversion radio option under the Source Code Management header we are prompted to supply a URL to our repository. Once entered and validated, Jenkins will try to make a connection to the repo. If your repo requires authentication, Jenkins will ask you for credentials. Once entered, Jenkins encrypts and stores them in its internal system. Alternatively, you can give it a ssh public/private key pair for authentication.
Next, we need to decide which build triggers to use. We will most often use the Poll SCM option which takes a cron statement to specify how often to poll. The statement */5 * * * * will tell Jenkins to poll scm every 5 minutes and is generally what we stick with.
Now we're ready to configure our build step. Under the Build heading, select the dropdown menu titled Add build step and choose XCode. This should insert a form with all the Xcode configuration parameters available.
For the majority of builds, the only configuration settings you will have to change are: telling the plugin to build an IPA, unlock the keychain, and adding a password to unlock the keychain so that your application can be signed with your profiles.
Click the save button, then the Build Now menu item and Jenkins will fire off its first build of your application.
Once successful, you should be able to navigate to the workspace, find the build/Release-iphoneos/ directory and see your signed IPA.
Deploying your application
So, now you have a compiled and thoroughly tested application, what next? If you're developing for the app store you'll probably just take the IPA and upload it to the developer portal for the approval process, but what if you're in an enterprise environment? You may need to place it in your Mobile Device Management (MDM) software for pushing out to your end users, or maybe you need to place the application on a server, with the appropriate plist and itms-services:// link. We can automate that too. Jenkins has many "Publish" plugins: CIFS, SSH, and FTP just to name a few. We need only install the plugin, configure it, and then tell the plugin what files to place as a result of our build on the remote server.
I'll give an example of SCPing the application to a remote server. First, let's install the plugin. As before, click Manage Jenkins, Manage Plugins, and then the available tab. Select the Publish over SSH Plugin and Install without restart. Now we need to configure our SSH server. Choose Manage Jenkins followed by Configure System.
Near the bottom of the administration page, will be a section for configuring your SSH servers. We could use SSH public/private key pairs for authentication but for simplicity, I'll show you how to configure using a username and password. Click the add button next the SSH Servers label and fill in the form with your server's information: Name, Hostname, User Name, etc. Click the advanced button and check the use password authentication check box. When you've entered everything correctly you can click the Test Configuration button to verify everything works.
Once done, choose Save and we're ready to configure the project.
Open up the job's configuration page and scroll to the bottom, under the section Post-build Actions. Once there select from the dropdown and choose Send build artifacts over SSH.
The configuration form that's added allows you to select the destination server that you just configured, the source files to transfer, any prefix to remove from the files, and the remote directory. This source files section accepts ANT style regular expressions (http://ant.apache.org/manual/dirtasks.html#patterns) for including multiple files, or comma separation for individual files.
Now when you run your build, if the build is successful, the IPA will be transferred to the remote server. You probably don't want to deploy a new IPA for every CI build that runs throughout the day, perhaps you want to deploy once every day (* 5 * * *) at 5AM, or have an automated build that you can only trigger manually, or after it has been promoted through test, QA, and pre-production environments using the Build-Pipeline plugin (https://wiki.jenkins-ci.org/display/JENKINS/Build+Pipeline+Plugin). Regardless, you now have a consistent deployment process that can easily be configured to suit your needs.