- Get a root canal at my desk waiting for Xcode SourceKit crashes to calm down.
- Submit an Apple Radar for one of my many Xcode crashes.
- Write a tutorial on how to set up a Jenkins CI server. (Oh hey!)
Whether you’re being a good developer and doing your unit testing, are tired of co-workers breaking your code, or have clients that can’t live without the latest and greatest build, CI will be your new best friend and butler.
There are tons of posts floating around the internet with bits and pieces on this topic; but none were comprehensive enough for me to get a server up and running. Because of this, I decided to write a thorough guide that will walk you through setting up a Jenkins CI Server on your machine of choice. In this instance we used a Mac Mini.
What is Jenkins?
Jenkins is an open source continuous integration tool which aims to automate certain tasks that developers find themselves repeating. It runs as a local server on a host machine, like a Mac Mini, that can be dedicated to CI.
Jenkins supports over 1,000 plugins allowing you to work with many third party services. The best thing is that if you can’t find or use a plugin, you’re only limited by your own scripting ability as it’s able to run any arbitrary shell script.
- Download the Jenkins Mac OS X native package from http://jenkins-ci.org.
- Double click the .pkg file to install Jenkins.
- Once done, your browser will open to http://localhost:8080 where Jenkins lives.
Note: If your browser opens up to a completely unstyled page (broken css), I recommend reinstalling Jenkins. You can uninstall Jenkins by running the script in /Library/Application Support/Jenkins called Uninstall.command.
Setting up the Jenkins User
Right now, Jenkins isn’t much of a butler. It’s more like the neighbor who moves your trash to the curb and waters your plants when you’re on vacation. Let’s promote it to an admin and give it the keys it needs to really help you out.
- Make the Jenkins user an admin: sudo dseditgroup -o edit -a jenkins -t user adminAdd the Jenkins user to the
Add the Jenkins user to the developer group: sudo dscl . append /Groups/_developer GroupMembership jenkins
Make the Jenkins user automatically login when the computer is restarted:
- a. Go to System Preferences\Users & Groups.
- b. Unlock the settings by clicking the lock in the bottom left corner of the page and entering the login password for the current user.
- c. Select the Jenkins user from the list of users. Note: On our install, the Jenkins user shows up as empty in the list of available users.
To fix this, right-click the empty user and select Advanced Options… which will show you details on the user. Set the Full Name to Jenkins and press OK.
- d. Click Reset Password. Create a new password for the Jenkins user, and make sure to take note of it somewhere secure. This is just like any other Mac user login that has admin access, and should be treated as such. If you are working for a team, use a secure shared password manager like 1Password so the password doesn’t just live in your head.
- e. Select Login Options underneath the list of users.
- f. Select Automatic login and choose Jenkins.
- g. Enter the password you just created for the new Jenkins user.
- h. Restart your computer. Once finished, you should be automatically logged in as the OSX Jenkins user.
Configure the Jenkins User
Now that you are logged in as Jenkins, you can customize the user account however you like. This includes all things from Dock settings to terminal configurations. One simple thing I like to setup is to have the Jenkins Dashboard open every time the computer is restarted.
- To do this, open Safari and go to Safari\Preferences\General…
- a. Set Safari opens with to A new window.
- b. Next set New windows open with to Homepage.
- c. And lastly, set Homepage to http://localhost:8080.
Allow Jenkins to Run GUI Applications
By default, Jenkins is unable to run GUI applications because it runs as a Daemon. A daemon runs in the background as part of the overall system, and isn’t tied to a specific user. This would be like having a butler that couldn’t interact with anyone. Sure it could clean up after us, but what about answering the door or fetching my martini!
A big part of CI is running simulators and other GUI applications, so we’ll need another option. To fix this, you can change Jenkins to run as a Launch Agent. A launch agent runs in the background on behalf of a user.
To change how the Jenkins process is launched, you’ll need to edit the settings file and change its location so it will automatically start on reboots.
- Enter the following command to unload Jenkins as a Daemon: sudo launchctl unload /Library/LaunchDaemons/org.jenkins-ci.plist
- Next, move the .plist file, which defines how Jenkins will run, to the LaunchAgents folder: sudo mv /Library/LaunchDaemons/org.jenkins-ci.plist /Library/LaunchAgents/
- Now for the top secret step. The default .plist file that Jenkins creates adds a key-value pair called SessionCreate. This creates issues when running certain applications, such as iOS simulators. The reason for this? Honestly, I’m not sure. In the past, a fix to many Jenkins CI issues in the iOS world was to add this key manually. It seems that with Xcode 6 things have reversed and it’s now required that you remove the key which solves the issue. Open the file with the editor of your choosing, and remove them.
- sudo vim /Library/LaunchAgents/org.jenkins-ci.plist
- /* Remove the following lines */
- <true />
- /* Save the file */
- Lastly, reload the Launch Agent to restart Jenkins.
- sudo launchctl load /Library/LaunchAgents/org.jenkins-ci.plist
Keep Those Fingers Crossed
Now, restart your computer and make sure that Jenkins successfully starts on its own after the reboot. If working correctly, your Mac should automatically login to the Jenkins user and open Safari to http://localhost:8080 where you will see Jenkins ready at your beck and call!
If for some reason this isn’t happening, jump to the last section on Debugging Jenkins to try and remedy your sickly CI butler.
Setting up SSH
In order to add jobs linked to a Git repository, you will need to setup SSH keys for the Jenkins user. This will allow you to treat Jenkins as a real user in regards to cloning and committing code to your repositories.
- Although you are currently logged in as Jenkins, you need to explicitly substitute the user as follows:
- a. su jenkins
- b. Enter your Jenkins password
- Follow the steps at https://help.github.com/articles/generating-ssh-keys to generate your own SSH keys for the Jenkins user. Note: Do not enter a passphrase while generating the key. I found that Jenkins was unable to authenticate with Git if a passphrase was needed.
Once done, you should have your newly generated key copied to the clipboard using:
- a. pbcopy < ~/.ssh/id_rsa.pub
- Add this key to your Github, Bitbucket, or whatever Git service you use. I created a new user for our Github team to be used by Jenkins. Whenever Jenkins pulls code, makes a change like incrementing build numbers, and pushes the code back up, it shows Jenkins as the committer as if its a real person. After all, we definitely want it to feel like part of our family. A happy Jenkins is a Jenkins that doesn’t ruin your life by blowing up after every build.
- First check if the Launch Agent process failed:
- a. From terminal, type: launchctl list and look for the org.jenkins.plist process.
- b. If there is a status other than 0, you know it failed.
- If it failed to launch, it’s most likely due to a log file not being owned by the Jenkins user. You can verify this by typing: ls -l /var/log/jenkins to see who owns the jenkins.log file. If it says root, you need to change it to the Jenkins user.
- a. Enter the following to change ownership: sudo chown jenkins /var/log/jenkins/jenkins.log
- Now try reloading the process to see if it worked:
- launchctl unload /Library/LaunchAgents/org.jenkins.plist
- launchctl load /Library/LaunchAgents/org.jenkins.plist
Hopefully this guide got you up and running with Jenkins without any problem. I’d like to keep this guide as up to date as possible, so feel free to email me with anything you find at firstname.lastname@example.org.
Jenkins is an awesome tool will save you a ton of time in the long run. We currently have it enabled to do the following for both iOS and Android:
- Nightly builds that automatically increment the build number and send it to the client using hockeyapp.
- Repository hooks which run all testing frameworks whenever code is pushed to the repository.
- Credentials to login remotely and manage Jobs
The possibilities are endless!
For more tech-related insight, please visit: Monsoon
About Eric Cerney:
Eric is the Head of iOS at Monsoon, an innovative software development company based out of Oakland, CA. As the Lead iOS engineer, Eric enjoys pushing the bounds of user interaction and finding ways to create awesome reusable components within a mobile environment.
To find out more about Monsoon visit: www.monsoonco.com
If you get this message when you are adding your server to a team to implement continuous integration using Xcode bots and OS X server:
Server encountered an error with Apple Developer.
Please try again later. If the problem persists, contact Apple Developer Technical Support for help.
It could be a red herring. There’s a support note here: https://support.apple.com/en-us/HT203853 that states the issue has to do with making sure you login as an Agent or Admin. That’s probably a valid scenario, but it may not be as in my case. I got this error message simply because an updated program license agreement needed to be accepted on the web portal. Once that was done, I was able to add the server to the team and could continue on.
(Yes, I know it’s April first, but this is not a joke)