18

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?

Anthony F
  • 6,096
  • 4
  • 31
  • 32
  • Will Xcode still have problems if the same keys are _both_ in the login- and the system keychain? – Joachim Isaksson Mar 08 '12 at 23:07
  • Putting the keys in both places is another workaround I suppose, but from past experience I know it can really cause problems with the Keychain Access program. It seems to get confused and won't delete keys that exist in two keychains. – Anthony F Mar 11 '12 at 02:09
  • 1
    See this stack exchange question for a posible solution: http://stackoverflow.com/questions/6827874/missing-certificates-and-keys-in-the-keychain-while-using-jenkins-hudson-as-cont – Glen T Apr 10 '12 at 05:40
  • If the accepted answer is not enough for you, try this.. http://www.egeek.me/2013/02/23/jenkins-and-xcode-user-interaction-is-not-allowed/ – Ravi Dec 11 '14 at 08:02

3 Answers3

23

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>
markshiz
  • 2,501
  • 22
  • 28
  • 2
    This is confirmed to be working for me as well, so I switched my my answer checkmark. This was the last missing piece of the puzzle. Thank you very much! – Anthony F Jun 13 '12 at 23:54
  • 1
    Hi,SessionCreate is correctly placed in my .plist file and security list-keychains display the Jenkins login.keychain but I still get the same error on building. Any idea? – Claus Nov 08 '12 at 15:16
  • Have you rebooted or restarted jenkins? Not sure, there's many things that can go wrong. Also make sure the certificate/private key pair is in the jenkins user's keychain. Finally right click on your key and under the Access Control grant access to all applications. – markshiz Nov 08 '12 at 15:43
  • Where is this file? I've checked in `~/Library/Preferences` and `~/Library/LaunchDaemons` but no luck – kraftydevil Nov 02 '15 at 19:05
  • Check under `/System/Library/` – markshiz Nov 03 '15 at 13:47
5

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.

sti
  • 11,047
  • 1
  • 27
  • 27
4

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).

Jon Boydell
  • 834
  • 7
  • 8
  • Not sure I follow you, but I think what you're saying is that you have the Jenkins user set to Automatic Login, and then added a shell script as one of that user's Login Items. Is that correct? – Anthony F Mar 11 '12 at 02:27
  • OK, don't add the plist to /Library/LaunchDeamons, add it to /Users/youruserhere/Library/LaunchAgents then set that user to auto-login. This jenkins instance will have acces to 'youruserhere's login keychain. You may have to unlock it during the jenkins job that builds your code. – Jon Boydell Mar 11 '12 at 18:16
  • Leaving the Jenkins user logged in isn't ideal, but it's not a deal-breaker, and it does make the CI build server fully automated. Thanks for the help! – Anthony F Mar 12 '12 at 20:13