This plugin performs the actual codesigning steps necessary to release MacOS software. No support for iOS, sorry.
See here for the general design that this fits into. TODO(@DoomGerbil): Make this design doc visible to people outside of Improbable.
Important note: this plugin relies upon the build agent already having the necessary keychains created on the machine.
The required keychain items should also have been allowed access for the relevant tools; more on this later.
- Signs binaries, dmgs, pkgs, or apps for MacOS.
- Notarization, with stapling of the notarization ticket.
- Specifying multiple sub targets to sign. For example: the frameworks in a
.app
. - Notarization password can be fetched from keychain.
- Automatic unlocking/locking of signing keychains.
- Secrets for unlocking signing certs can be supplied as env vars or fetched from external secret storage (eg Vault).
- Prevents signing jobs from being run on unapproved machines, or using unsafe workflows.
- There are still a few places left that assume you're a user at Improbable. Sorry.
Your build agent requires a few things for this to work properly.
- XCode 11+ must be installed.
altool
,codesign
, andproductsign
must be on the $PATH.
gon
must be installed and on the $PATH. This is the wrapper which handles codesigning and notarization.jq
must be installed and on the $PATH.- Each item stored in the keychain must have been whitelisted for access by the relevant tool. To do this, double click
on the restricted keychain item, select the "Access Control" tab, and add the tool to the list of applications
to "always allow access to". This means that:
- for PKG signing; the private key for your "Developer ID Installer" cert must have
productsign
added to it. - for signing anything else; the private key for your "Developer ID Application" cert must have
codesign
added to it. - for notarization: your account password should be stored in a keychain item named "apple_password", with the
"account" field being the relevant apple email. It should be accessible by
altool
.
- for PKG signing; the private key for your "Developer ID Installer" cert must have
Using the KEYCHAIN_PW
env var:
- label: "sign-macos-binary"
agents:
- "queue=macos-codesigner"
plugins:
- improbable-eng/mac-codesign#v0.1.2:
input_artifact:
- "mac/software_app.zip"
sign_prerequisites:
- "software.app/Contents/Frameworks/Electron Framework.framework"
- "software.app"
keychain: "production-certs.keychain"
env:
KEYCHAIN_PW: "KeychainPasswordGoesHere"
Using the default Improbable secret-fetching script with keychain_pw_secret_name
set:
- label: "sign-macos-binary"
agents:
- "queue=macos-codesigner"
plugins:
- improbable-eng/mac-codesign#v0.1.2:
input_artifact:
- "mac/software_app.zip"
sign_prerequisites:
- "software.app/Contents/Frameworks/Electron Framework.framework"
- "software.app"
keychain: "production-certs.keychain"
keychain_pw_secret_name: "ci/improbable/production-codesigning"
Using a custom secret-fetching script:
- label: "sign-macos-binary"
agents:
- "queue=macos-codesigner"
plugins:
- improbable-eng/mac-codesign#v0.1.2:
input_artifact:
- "mac/software_app.zip"
sign_prerequisites:
- "software.app/Contents/Frameworks/Electron Framework.framework"
- "software.app"
keychain: "production-certs.keychain"
keychain_pw_helper_script: "~/fetch-keychain-pw.sh"
keychain_pw_secret_name: "production-codesigning-keychain-pw"
This plugin defines hooks for environment
, checkout
, command
, and post-command
which execute in that order.
-
environment
performs pre-execution setup and validation before we can actually perform code signing. It's mostly responsible for checking that the machine in question is allowed to run codesigning jobs. -
checkout
just disables checkout, since the plugin doesn't need a repo. -
command
does the main work:- Unlocks the signing keychain.
- Fetches the artifact to sign from the BK artifact store.
- NOTE: if you are trying to sign a
.app
, you should zip it with the.app
in the toplevel of the zip. EG, to signmyapp.app
, you would want to usezip -y -r -X myapp.zip myapp.app
, which would leave a toplevelmyapp.app
directory in the zip file. Note that the-y
to preserve symlinks is a hard requirement, as apple WILL fail notarization if symlinks are tampered with.
- NOTE: if you are trying to sign a
- Signs the artifact using the cert in the now-unlocked keychain.
- Notarizes the artifact using the account credentials in the keychain.
- Uploads the signed artifact back to BuildKite.
-
post-command
just locks the keychain, regardless of how the rest of the job went.
input_artifact
: Path of artifact to download, sign, and reupload.sign_prerequisites
: (optional) String array of the specific artifact paths to sign. If empty, default toinput_artifact
. In the case of.app
s, make sure to list these bottom-up; internal frameworks/helpers first, and.app
at the end.entitlements
: (optional) Path of artifact containing a valid entitlements plist to apply.keychain
: Name of the keychain storing the secrets. (Note: usually requires the .keychain extension)cert_identity
: Name of the cert to use to sign your artifacts. Should be the "Application" cert, not the "Installer" cert.keychain_pw_secret_name
: (optional) Name of the password to extract from your preferred secret store (eg: Vault)keychain_pw_helper_script
: (optional) Custom helper script to obtain the keychain password.tool_bundle_id
: The apple bundle id to use with your artifacts.apple_user_email
: The account email for your notarization process. Password should be stored in the keychain at theapple_password
key.
Since your signing certs need to be stored in a keychain, and that keychain is assumed to be locked, we need a password to unlock the signing keychain.
There are two ways to supply a keychain unlocking password to this plugin:
-
In the simple case, you can set the environment variable
KEYCHAIN_PW
on your step. -
If
KEYCHAIN_PW
is not set, the command hook will call a helper script, which needs to export your keychain unlock password asKEYCHAIN_PW
- eg:-
export KEYCHAIN_PW="foo-bar-123"
-
If you use a secret store like Vault, you should supply the path to your own helper script to retrieve the secret. By default, the plugin will use
$HOOKS_DIR/helpers/fetch-keychain-pw.sh
, but you can override this with thekeychain_pw_helper_script
parameter. -
If set,
keychain_pw_secret_name
will be available to the helper script, which can be used to supply a name or path for a specific secret to retrieve.
-