Code signing in CI builds
Import your signing certificate and provisioning profile into a temporary keychain for each job.
An unattended build can’t answer keychain prompts, so CI jobs usually put the certificate in a temporary keychain of their own. These are the steps from GitHub’s guide; they work in any CI that runs shell commands. Keep the certificate, its password and the profile in your CI’s secrets, never in the repository.
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security list-keychain -d user -s $KEYCHAIN_PATH
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles
Clean up after every job
Your RunMac keeps its files between jobs (unlike GitHub’s own runners), so delete the keychain and profile at the end, even when the job fails:
security delete-keychain $RUNNER_TEMP/app-signing.keychain-db
rm ~/Library/MobileDevice/Provisioning\ Profiles/build_pp.mobileprovision
In GitHub Actions, put that in a final step with if: ${{ always() }}. fastlane can manage signing for you instead.