Updated on 2015-04-18: added a simple command to allow web update.
In this post, I will describe how to setup a sandboxed Jenkins server in a virtual machine where you can play and try your iOS (and not only) jobs. I’m using the latest OS X Yosemite 10.10.2 for the host and the guest on a MacBook Pro. Nota Bene: according to the Apple’s EULA for OS X, you are allowed to run the OS on the Apple hardware only.
This how-to assumes you know what a command-line is, how to run commands, and related stuff.
Since you’re working/developing on OS X, you must already know about the Homebrew package manager to install some essential software that Apple doesn’t bundle with OS X. So I am left to mention the Homebrew Cask that extends
brew to install (and update) GUI applications.
As a note, Homebrew requires the OS X Command-Line Tools or, even better, Xcode installed. You can either go to the Mac App Store or download the Xcode installer from https://developer.apple.com/downloads (Apple ID required), as you’ll need it later as well.
1 2 3 4 5 6 7 8
I’m not sure if it’s required for the purpose of this guide, but I always install the
VirtualBox Extension Pack as well. For this, you’ll have to download it from here: https://www.virtualbox.org/wiki/Downloads.
Installing Guest OS X
This section is mostly based on these posts: http://apple.stackexchange.com/questions/106476/how-to-install-osx-mavericks-in-virtualbox/106840#106840 and https://ntk.me/2012/09/07/os-x-on-os-x/.
Install OS X Yosemite.app from the Mac App Store, it will appear in the
Install iESD — a ruby package to work with OS X install images. I don’t like to mess with the system packages (Ruby, Python, etc.) and since I need ruby for
cocoapods too, I use
rbenv to build and install a custom ruby version in my home directory. Therefore, I can easily run
gem install iesd without
sudo. The guide how to install and use
rbenv is here: Installing CocoaPods with rbenv.
We’ve got the bootable image to install the OS X! Launch VirtualBox. Create a new virtual machine. Let the name be
osx (the VirtualBox will automagically deduct its Version as
Mac OS X (64 bit), which is what we need). The default amount of RAM is 2048 MB, let’s set it to 4096 MB for the installation phase (should be faster). Create a virtual hard drive (I’d like a virtual SSD though).
Fix some of the default settings:
1 2 3 4 5 6 7 8 9
Install the OS X as usual. You’ll need to use
Disk Utitlity to repartition the hard drive. Select the only disk on the left >
Partition tab >
1 Partition >
Apply. Let’s name the user
osx as well. Go grab some tea, the installation will take a while.
You can suspend the machine and take a snapshot of a freshly installed OS (do you feel the fresh smell as from a very new printed book?):
1 2 3 4 5 6 7 8 9
It’s convenient to have remote access to your guest in order not to muck around with the UI. SSH is the standard here (you can also use RDP):
1 2 3 4 5
Install updates in the guest. Unfortunately, there are no guest additions for OS X that would provide graphics acceleration, so the graphics inside is pretty slow. To make it less painful, go to
System Settings >
Display > check
Reduce Transparency. You won’t have to use this slow UI much though.
Enable SSH to the guest: go to
System Settings >
Sharing > enable
Remote Login. Now you can SSH into the VM this way from your host:
Either get the Xcode installer in the guest, or copy it from the host (Note: the port in
scp is specified after
-P, whereas in
Install Xcode as usual. Launch Xcode and let it install its components. SSH into the VM and install Homebrew:
Jenkins Setup for iOS
Download and install the latest JDK 8.* from oracle.com. You can either download it in the guest, or again copy from the host with
We’re going to install Jenkins with the official installer: http://mirrors.jenkins-ci.org/osx/latest. Download and install the
pkg file. In the current installer (1.598), Jenkins is setup to start as the
jenkins user, which is nice, because it will be created for us. A browser will open by the end with a freshly installed Jenkins.
A very important part of the post follows. Jenkins is now run as a daemon, and daemons are not allowed to communicate with the UI, which is the reasons we can’t run iOS tests from a Jenkins job (Types of Background Processes). There is a lot of complaints about that online. So we’ll run it as a launch agent (https://issues.jenkins-ci.org/browse/JENKINS-21237).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Now you should login as
jenkins in the guest UI, otherwise the
launchctl doesn’t want to load the agent:
Since you’ve logged in, Jenkins is started automatically. Now we need to setup autologin, so that we don’t bother logging in manually after every restart (it’s not a big deal for a VM, but the same approach should be used on real servers). In the guest, open
System Preferences >
Users & Groups > unlock the settings by pressing the lock at bottom-left corner and entering an admin’s credentials (
osx user in our case). Click
Login Options, and in the
Automatic login: dropdown pick… an empty line (that’s because the OS X apparently uses user’s Full Name for that list, and the Jenkins installer didn’t bother to set that, we don’t care either). In the opened popup window, enter
jenkins’s password. So far so good.
While still here, go to
Remote Login > add
jenkins to the allowed users, so you can SSH in as her directly.
However, it’s a big security hole to setup an automatic password-less login for a system (especially, with real servers). So put this file into
~jenkins/Library/LaunchAgents/ (thanks to http://www.tuaw.com/2011/03/07/terminally-geeky-use-automatic-login-more-securely/):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
This agent runs a command that locks the current account and presents the system Login Window. Since it’s setup to autoload, the OS X will automatically login as
jenkins, and then go back to the Login Window, as if nothing happened. On a real server, that happens pretty fast, but in VirtualBox it can take a few seconds. Go try it now – restart the guest! After the restart, you should see the login window with a checkmark at the jenkins user, which means it’s logged in.
Don’t worry, we’re almost done. Now it’s a good idea to take a snapshot just in case:
If the default Jenkins’ 8080 port is free on your host, it’s a good idea to forward the guest’s 8080 port to the host, so you can use exactly the same URL on both host and guest:
Now that all the automatic stuff is all setup, we can finally run the VM without the UI:
Wait several seconds. Behold: http://127.1:8080/. You should see the Jenkins UI served by the hidden VM. Congratulations, the installation is done!
Now you can SSH as
osx to do some stuff in the guest. NB: with the default setup we’ve done, you won’t be able to use
brew from the
jenkins user. You’ll need to login as
osx to do some brewing, which is fine for me (separation of responsibilities).
Jenkins provides an update button in the web UI if it has found a newer version and the process has permissions to overwrite the
jar file. The latter condition is failing by default with this setup, however it’s very easy to fix. Run this command on the Jenkins host:
iOS Tests Setup
You can run XCTest-based tests in the
iPhone Simulator on OS X in a virtual machine! Just a little more preparation for that, I promise.
In a browser with your jenkins, go to
Configure System >
Global properties > check
Environment variables, add a
PATH variable with the value
Here is a sample build command to run tests:
1 2 3
1 2 3 4 5
Hurray! I’m so happy to see this message, which means the tests are run just fine.
All in all, I’m super happy about the result, because it allows me to test the jobs (and play with plugins) before rolling them out on the real Jenkins server.
Thanks for reading and following along. If some of the steps are unclear or you have any suggestions/improvements/comments, please leave a comment!