Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unable to sign iOS builds with Jenkins

Our Jenkins CI build server is set up on a Mac Mini running OSX Lion (10.7.3), and I'm having trouble getting it to sign the iOS builds so they can be uploaded to to TestFlight.

The process is running as a normal user named jenkins, and it's started at boot time using launchd. (The machine isn't accessible to the outside world, so there shouldn't be any security concerns with running this under a normal user account.)

Here is the error in the console output from jenkins:

[workspace] $ /usr/bin/xcodebuild -target iMobileStCloud -configuration Release clean build
=== CLEAN NATIVE TARGET MyApp OF PROJECT MyProject WITH CONFIGURATION Release ===
Check dependencies
[BEROR]Code Sign error: The identity 'iPhone Distribution' doesn't match any valid certificate/private key pair in the default keychain

Part of the problem seems to be that only the System Keychain is available when the process is started from launchd at boot. I added a script to the build process to list the keychains:

[workspace] $ /bin/sh -xe /var/folders/1y/1q3st_ss58z9ffj4dwbkdw8r0000gt/T/hudson8514187812830984272.sh
+ /usr/bin/security list-keychains
    "/Library/Keychains/System.keychain"
    "/Library/Keychains/applepushserviced.keychain"
    "/Library/Keychains/System.keychain"
+ /usr/bin/security find-identity

I was able to find two workarounds, but neither one is really feasible:

  1. If we login to the server and restart the launchd process every time the machine is rebooted then jenkins is able to load the login keychain and access the certificates for signing:

    sudo launchctl unload /Library/LaunchDaemons/org.jenkins-ci.plist
    sudo launchctl load /Library/LaunchDaemons/org.jenkins-ci.plist
    
  2. We can add the certificates to the System Keychain, but this means we can't use this machine for doing our app store distribution builds. (Xcode doesn't like the system keychain).

Has anyone else found any other viable workarounds? Is there something else besides launchd that I can use to run processes at boot time on OSX?

like image 826
Anthony F Avatar asked Mar 08 '12 23:03

Anthony F


3 Answers

I solved this problem by adding SessionCreate=true to my org.jenkins-ci.plist file. This call initializes the Security framework.

Source: http://developer.apple.com/library/mac/#technotes/tn2083/_index.html

See mine in its entirety below:

<?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>EnvironmentVariables</key>
 <dict>
   <key>JENKINS_HOME</key>
   <string>/Users/Shared/Jenkins/Home</string>
 </dict>
<key>GroupName</key>
<string>daemon</string>
<key>KeepAlive</key>
<true/>
<key>Label</key>
<string>org.jenkins-ci</string>
<key>ProgramArguments</key>
<array>
  <string>/bin/bash</string>
  <string>/Library/Application Support/Jenkins/jenkins-runner.sh</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>UserName</key>
<string>jenkins</string>
<key>SessionCreate</key>
<true/>
</dict>
</plist>
like image 115
markshiz Avatar answered Nov 10 '22 23:11

markshiz


You could also try my alternative Jenkins installer which runs Jenkins as an application.

The project is at https://github.com/stisti/jenkins-app. Downloads are at https://github.com/stisti/jenkins-app/downloads

Jenkins needs to run in the user context in order to have access to keychains.

like image 34
sti Avatar answered Nov 11 '22 00:11

sti


I had the same problem. The main issue is actually caused when launchd launches a LaunchDaemon. Even if you specify the user that you want to run the launchd process under it doesn't run it as if you were logged in as that user. Which is why you aren't seeing the login keychain in the list of keychains available to Jenkins.

I came across a work around that involved calling su - yourbuilduser -c ./start-jenkins.sh, where start-jenkins.sh is a custom start script, from your launchd plist (as a LaunchDaemon). This guarantees access to the login keychain but makes Jenkins hard to control from launchd. Specifically, you can't stop Jenkins by calling launctl unload ... you have to kill the process manually.

Currently we are running our iOS CI using a plist in LaunchAgents (that just starts Jenkins using java -jar jenkins.war) rather than in LaunchDaemons. Tediously this means that your user has to be logged into the server (not an issue if your machine is within your private network or in a correctly configured DMZ), however it also means that the Jenkins process can be controlled from launchctl and that it has access to the user's keychain. You can set the user to auto-login so you get Jenkins on start-up.

I have managed to automate almost every aspect of a Continuous Delivery pipeline for iOS binaries this is the only part where my solution just doesn't feel right (ideally, I'd just be able to use a LaunchDaemon that would have access to the user's keychain).

like image 4
Jon Boydell Avatar answered Nov 10 '22 23:11

Jon Boydell