Loading skill
Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Automate Apple notarization with xcrun notarytool for macOS application distribution
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 736% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 179% | 0% |
| case-15 | ✗→✓ | ▲ Improved | 56% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 191% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 256% | 0% |
Automate Apple notarization workflow using xcrun notarytool for macOS applications. This skill handles the complete notarization process including submission, status checking, and stapling.
json{ "type": "object", "properties": { "projectPath": { "type": "string", "description": "Path to the project" }, "appPath": { "type": "string", "description": "Path to the signed app bundle or DMG" }, "authMethod": { "enum": ["app-store-connect-api", "apple-id", "keychain"], "default": "app-store-connect-api" }, "credentials": { "type": "object", "properties": { "keyId": { "type": "string" }, "issuerId": { "type": "string" }, "keyPath": { "type": "string" }, "appleId": { "type": "string" }, "teamId": { "type": "string" } } }, "waitForCompletion": { "type": "boolean", "default": true }, "staple": { "type": "boolean", "default": true } }, "required": ["projectPath", "appPath"] }
json{ "type": "object", "properties": { "success": { "type": "boolean" }, "submissionId": { "type": "string" }, "status": { "enum": ["Accepted", "Invalid", "In Progress", "Rejected"] }, "logUrl": { "type": "string" }, "errors": { "type": "array" }, "stapled": { "type": "boolean" } }, "required": ["success"] }
bash# Ensure Xcode command line tools are installed xcode-select --install # Verify code signing codesign --verify --deep --strict MyApp.app codesign -vvv --deep --strict MyApp.app # Check hardened runtime codesign -dvvv MyApp.app | grep runtime # Should show: flags=0x10000(runtime)
bash# Store App Store Connect API key in keychain xcrun notarytool store-credentials "MyProfile" \ --key ~/private_keys/AuthKey_XXXXXXXXXX.p8 \ --key-id XXXXXXXXXX \ --issuer xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx # Or store Apple ID credentials xcrun notarytool store-credentials "MyAppleIDProfile" \ --apple-id your.email@example.com \ --team-id XXXXXXXXXX \ --password @keychain:AC_PASSWORD
bash# Using stored credentials xcrun notarytool submit MyApp.app \ --keychain-profile "MyProfile" \ --wait # Using API key directly xcrun notarytool submit MyApp.app \ --key ~/private_keys/AuthKey_XXXXXXXXXX.p8 \ --key-id XXXXXXXXXX \ --issuer xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx \ --wait # Using Apple ID xcrun notarytool submit MyApp.app \ --apple-id your.email@example.com \ --team-id XXXXXXXXXX \ --password @keychain:AC_PASSWORD \ --wait
bash# Check specific submission xcrun notarytool info <submission-id> \ --keychain-profile "MyProfile" # Get submission log xcrun notarytool log <submission-id> \ --keychain-profile "MyProfile" \ developer_log.json # List recent submissions xcrun notarytool history \ --keychain-profile "MyProfile"
bash# Staple to app bundle xcrun stapler staple MyApp.app # Staple to DMG xcrun stapler staple MyApp.dmg # Staple to pkg xcrun stapler staple MyApp.pkg # Validate stapling xcrun stapler validate MyApp.app
bash#!/bin/bash # notarize.sh set -e APP_PATH="${1}" KEYCHAIN_PROFILE="${2:-MyProfile}" echo "=== Validating app bundle ===" codesign --verify --deep --strict "$APP_PATH" echo "=== Submitting for notarization ===" SUBMISSION_OUTPUT=$(xcrun notarytool submit "$APP_PATH" \ --keychain-profile "$KEYCHAIN_PROFILE" \ --wait \ --output-format json) SUBMISSION_ID=$(echo "$SUBMISSION_OUTPUT" | jq -r '.id') STATUS=$(echo "$SUBMISSION_OUTPUT" | jq -r '.status') echo "Submission ID: $SUBMISSION_ID" echo "Status: $STATUS" if [ "$STATUS" != "Accepted" ]; then echo "=== Notarization failed, fetching log ===" xcrun notarytool log "$SUBMISSION_ID" \ --keychain-profile "$KEYCHAIN_PROFILE" \ notarization_log.json cat notarization_log.json exit 1 fi echo "=== Stapling ticket ===" xcrun stapler staple "$APP_PATH" echo "=== Validating staple ===" xcrun stapler validate "$APP_PATH" echo "=== Notarization complete ==="
yamlname: Build and Notarize on: push: tags: ['v*'] jobs: build: runs-on: macos-latest steps: - uses: actions/checkout@v4 - name: Import signing certificate env: CERTIFICATE_BASE64: ${{ secrets.MACOS_CERTIFICATE }} CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PWD }} run: | CERTIFICATE_PATH=$RUNNER_TEMP/certificate.p12 KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db KEYCHAIN_PASSWORD=$(openssl rand -base64 32) echo -n "$CERTIFICATE_BASE64" | base64 --decode > $CERTIFICATE_PATH 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 "$CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH security list-keychain -d user -s $KEYCHAIN_PATH - name: Build app run: | xcodebuild -project MyApp.xcodeproj \ -scheme MyApp \ -configuration Release \ -archivePath build/MyApp.xcarchive \ archive xcodebuild -exportArchive \ -archivePath build/MyApp.xcarchive \ -exportOptionsPlist ExportOptions.plist \ -exportPath build/ - name: Store notarization credentials env: API_KEY: ${{ secrets.NOTARIZATION_API_KEY }} API_KEY_ID: ${{ secrets.NOTARIZATION_API_KEY_ID }} API_ISSUER: ${{ secrets.NOTARIZATION_API_ISSUER }} run: | mkdir -p ~/private_keys echo -n "$API_KEY" > ~/private_keys/AuthKey.p8 xcrun notarytool store-credentials "CI_PROFILE" \ --key ~/private_keys/AuthKey.p8 \ --key-id "$API_KEY_ID" \ --issuer "$API_ISSUER" - name: Notarize app run: | xcrun notarytool submit build/MyApp.app \ --keychain-profile "CI_PROFILE" \ --wait xcrun stapler staple build/MyApp.app - name: Create DMG run: | create-dmg build/MyApp.app build/ xcrun notarytool submit build/*.dmg \ --keychain-profile "CI_PROFILE" \ --wait xcrun stapler staple build/*.dmg - name: Upload artifact uses: actions/upload-artifact@v4 with: name: MyApp path: build/*.dmg
Error: The signature does not include a secure timestamp.Fix: Sign with hardened runtime and timestamp:
bashcodesign --force --options runtime --timestamp --sign "Developer ID" MyApp.app
Error: The executable does not have the hardened runtime enabled.Fix: Include entitlements in signing:
bashcodesign --force --options runtime --timestamp \ --entitlements MyApp.entitlements \ --sign "Developer ID Application: Company" MyApp.app
Error: The signature of the binary is invalid.Fix: Sign all nested components:
bashfind MyApp.app -name "*.dylib" -o -name "*.framework" | \ xargs -I {} codesign --force --options runtime --timestamp --sign "Developer ID" {}
macos-entitlements-generator - Entitlements configurationmacos-codesign-workflow - Code signingcode-signing-setup process - Full signing workflowswiftui-macos-expert - macOS developmentcode-signing-specialist - Signing expertiseOther measured skills in the registry, with their headline benchmark lift.