A guide for setting up a remote build node for Jenkins CI server.
If you are up to a task to setup remote build node, that means that you already have a server or need to install it first.
I would recommend to do first time agent installation via Jenkins UI
Jenkins -> Manage Jenkins -> Manage Nodes. Select an option to create node, configure node’s properties (make sure root directory exists) and you are almost good to go. The preferred way to launch and agent is via SSH.
If Jenkins Wiki is such a good reference, then why bother with the post? - I bet that’s exactly what you are asking.
Well, again, the main focus is Mobile CI and part of any decent CI is tests, unit tests in particular. With iOS those must run in iOS Simulator which is a GUI application, which means you are facing Daemon vs Agent issue. Here’s a very good discussion on StackOverflow.
So remote node launched via SSH is running as Launch Daemon and has no context to run GUI applications. If SSH session is running under non-login user,
xcodebuild test will fail with error code
139 or alike. Running SSH session under login user will not help either, at best the simulator will launch but tests will never start. That rules out an option of running remote node via SSH. Trying all the tricks like enabling development mode, updating user’s group and security settings, didn’t work for me, probably because that answer was actual for Xcode 5, but not 6 and above.
Next option would be launching slave agent via Java Web Start. Option with clicking button in the browser should be ignored right away, it hardly qualifies as automated approach. Using
javaws it a bit better though requires you to login to slave agent via GUI and then run the command and answer one prompt by clicking Run button. Finally, you can run it in a headless mode
java -jar slave.jar \ -jnlpUrl http://yourserver:8080/jenkins/computer/parasite/slave-agent.jnlp
Now that’s better, this will start a slave agent that is capable of running GUI applications and thus running iOS unit tests in simulator. If you are puzzled where does
slave.jar come from, the answer is that it is put there by Jenkins server when you have created slave agent via Jenkins UI. The
jnlpUrl tells an agent where the server is. The order in which agent and server are fired up seems to be important. Server first, agent second, otherwise agent may fail to start. I had this issue while using GUI mode launch via
javaws and yet to confirm if it’s true for headless mode. Still you have to start an agent manually if remote node machine reboots.
I have one solution to offer. It is not the best I can think of, but it’s the easiest and reasonably reliable to start with. You need to turn the headless slave launch command into a Launch Agent. The Launch Agent will start at the time of user’s login, so make sure the machine is configured for automatic login.
Setting up Launch Agent should become a usual drill for you after you deal with Mac-based CI for a while. Create a plist file in
~/Library/LaunchAgents, name it what you like, for example
org.jenkins-agent-a.plist and put the following content in it.
<?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>StandardOutPath</key> <string>/Shared/Users/Jenkins-Agent-A/log/jenkins-agent-a-out.log</string> <key>StandardErrorPath</key> <string>/Shared/Users/Jenkins-Agent-A/log/jenkins-agent-a-err.log</string> <key>KeepAlive</key> <true/> <key>Label</key> <string>org.jenkins-agent-a</string> <key>ProgramArguments</key> <array> <string>/usr/bin/java</string> <!--<string>-Djava.util.logging.config.file=/opt/hudson/slave/logging.properties</string>--> <string>-Duser.home=/Users/Shared/Jenkins-Agent-A</string> <string>-jar</string> <string>/Users/Shared/Jenkins-Agent-A/slave.jar</string> <string>-jnlpUrl</string> <string>http://yourserver:8080/jenkins/computer/parasite/slave-agent.jnlp</string> </array> <key>RunAtLoad</key> <true/> </dict> </plist>
/Shared/Users/Jenkins-Agent-A is your agent’s home. You should have create one when setting up agent via server UI and
slave.jar should be already located in this folder. Now if agent machine reboots and autologin is configured, the agent will go back online automatically. If the server reboots though, I’m not quite sure what happens, my assumption is that agent will stay alive and will keep trying to reconnect to server and finally they both happily reunite. I’m yet to test how exactly it works in reality.
To manually start and stop an agent use following commands (typealias if you need to use them often)
# Start. launchctl load ~/Library/LaunchAgents/org.jenkins-agent-a.plist # Stop. launchctl unload ~/Library/LaunchAgents/org.jenkins-agent-a.plist # list (by label) launchctl list org.jenkins-agent-a
One additional note. You can run a remote agent on localhost, which technically makes it not remote any more. This may be useful when for some reason Jenkins server is launched under non-login user session (Launch Daemon again) and you can’t use default master node to run unit tests.
That sums it up. To compare how things are in Bamboo universe check out this post.