Continuous Integration for iOS – Install and configure Jenkins

Now that the user account is ready for use, the next step is to install and configure Jenkins server itself. Let’s start right away…

Install Jenkins server

  1. The best way to install Jenkins is via Homebrew. It is less problematic compared to the installation with the installer or the war-file. If you’re still interested in these ways of installing Jenkins, please see this nice blog post.
    $ brew install jenkins
  2. Install and set Java (at least version 1.7). Please follow these instructionsIf this does not work, try these steps:
    • Check the current java version
      $ java -version
    • Check current Java version link (typically this returns ‘java -> /System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/java’)
      $ ls -la /usr/bin | grep java
    • Remove current Java version link
      $ sudo rm /usr/bin/java
    • Create new Java version link
      $ sudo ln -s /Library/Internet\ Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin/java /usr/bin
  3. Configure Jenkins as Launch Agent not as Launch Daemon. A few words about this, because it is important. There are some differences. Most important, Jenkins needs to be run as a Launch Agent to execute tests. Otherwise it has no access to the window manager and therefore can not launch and interact with the iOS Simulator. The other major difference is, that a Launch Agent is started after the user has logged in (because it runs on behalf of the user), while the Launch Daemon is started at system start.
    For more information about the differences between Launch Agent and Launch Daemon see these blog posts [1][2].
    If you see the following error, then Jenkins is configured as a Launch Daemon (described here and there):

    [DEBUG] Session could not be started: Error 
    Domain=DTiPhoneSimulatorErrorDomain Code=2 
    UserInfo=0x100270b90 “Simulator session timed out.”

    To configure Jenkins as a Launch Agent, do the following:

    • Create the directory for Launch Agents if it does not exist.
      $ mkdir -p ~/Library/LaunchAgents
    • Copy Jenkins’ plist to the Launch Agents directory.
      $ cp /usr/local/opt/jenkins/*.plist ~/Library/LaunchAgents

    Now Jenkins gets started right after the user has logged into the system. If you want more automation, you can use the automatic login, (‘System Preferences → Users & Groups → Login Options → Automatic login’). But this reduces security because the system is not password protected anymore. I am ok with entering a password if I work directly on the machine once in a while. And usually the Jenkins does not reboot that often.

  4. Start and stop the Jenkins manually with these commands:
    Start:
    $ launchctl load ~/Library/LaunchAgents/homebrew.mxcl.jenkins.plist
    
    Stop:
    $ launchctl unload ~/Library/LaunchAgents/homebrew.mxcl.jenkins.plist
  5. The web interface/dashboard of the Jenkins server should be reachable at http://localhost:8080.

 


 

Configure the Jenkins server

  1. Configure the server parameters, mainly to grant more memory to Jenkins. Edit the default plist file at ~/Library/LaunchAgents/homebrew.mxcl.jenkins.plist. My plist looks like this:
    <?xml version=“1.0” encoding=“UTF-8”?>
    		<!DOCTYPE plist PUBLIC “-//Apple//DTD PLIST 1.0//EN” “http://www.apple.com/DTDs/PropertyList-1.0.dtd”>
    		<plist version=“1.0”>
    			<dict>
    				<key>Label</key>
    				<string>homebrew.mxcl.jenkins</string>
    				<key>ProgramArguments</key>
    				<array>
    					<string>/usr/bin/java</string>
    					<string>-d64</string>
    					<string>-Xms1024m</string>
    					<string>-Xmx2048m</string>
    					<!-- Use Concurrent GC-->
    					<string>-XX:+UseConcMarkSweepGC</string>
    					<string>-XX:+CMSClassUnloadingEnabled</string>
    					<string>-XX:MaxPermSize=256m</string>
    					<string>-Dmail.smtp.starttls.enable=true</string>
    					<string>-jar</string>
    					<string>/usr/local/opt/jenkins/libexec/jenkins.war</string>
    					<string>--httpListenAddress=127.0.0.1</string>
    					<string>--httpPort=8080</string>
    				</array>
    				<key>RunAtLoad</key>
    				<true/>
    			</dict>
    		</plist>
    

     

    JVM Virtual Memory

    Set the heap size minimum and maximum with -Xms and -Xmx to give Jenkins a bit more memory power and let Jenkins use a 64-bit data model, if available, with -d64.

    <string>-d64</string>
    <string>-Xms1024m</string>
    <string>-Xmx2048m</string>
    
    HTTP address and port

    Usually Jenkins runs on ‘127.0.0.1:8080’. These values have to go to the ‘jenkins.war’ file.
    If you face the problem that the Jenkins dashboard does not open, try to enable access from all IP addresses by setting ‘httpListenAddress=0.0.0.0.’.

    <string>-jar</string>
    <string>/usr/local/opt/jenkins/libexec/jenkins.war</string>
    <string>--httpListenAddress=127.0.0.1</string>
    <string>--httpPort=8080</string>
    

    If you want to know more about the other configurations like HTTP proxy, garbage collection, environment variables, redirect stdout and stderr, etc., read the configuration part in this blog post.

  2. Install the Jenkins plugins you need for your workflow. Here are some of my favorites:
  3.  The last plugin helps to configure the iOS part of the build process. You make some definitions and the plugin executes the rest of the build process. If you don’t want to have your build configuration stored on Jenkins only and want to build your apps locally on your machine the same way, use build scripts. Commit these scripts to your VCS and you have an executable build system everywhere where the project is checked out.
    Xcode plugin settings

    Xcode plugin settings

    To default build tool is xcodebuild, which is part of the command line tools. But its output and flexibility regarding test execution could be better. And xctool does this job better. The configuration is almost the same.

    $ brew install xctool
  4. Manage the Jenkins. From the left side of the dashboard select Manage Jenkins.
    Configure System
    • Extend PATH variable, otherwise you have to to specify full qualified paths for commands. Under Global properties select Environment variables and add a variable named “PATH” with the value /usr/local/bin:$PATH.
    • Configure Git
    • Configure Jenkins Location
    • Configure Git plugin
    • Configure Xcode Builder (= Xcode plugin) optionally
    • Configure E-Mail setup (to notify users about a broken build)
    • Configure other appropriate settings according your taste
    Configure system

    Configure system

    Configure Global Security
    • Define how users are created and how they authenticate, under Access Control. The easiest way is to let Jenkins manage usernames and passwords by choosing Jenkins’ own user database under Security Realm.
    Configure global security

    Configure global security

    Manage credentials
    • Set credentials which will be used by build jobs and Jenkins itself (i.e. for access to integrated systems like GitHub or Stash, …).
    • Set a SSH private key and give it a speakable name.
  5. Share all schemes that should be used in Jenkins.
    • Open project in Xcode
    • ‘Product ➝ Schemes ➝ Manage Schemes…’
    • Select the Shared checkbox for the required scheme

    Share a scheme

    Share a scheme

 

?  How to setup a build job that runs on the installed Jenkins server is the topic of the next post

Comments are closed.