Automate Apple notarization with xcrun notarytool for macOS application distribution
Automates Apple notarization for macOS apps using xcrun notarytool, handling submission, status checks, and stapling.
npx claudepluginhub a5c-ai/babysitterThis skill is limited to using the following tools:
README.mdAutomate Apple notarization workflow using xcrun notarytool for macOS applications. This skill handles the complete notarization process including submission, status checking, and stapling.
{
"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"]
}
{
"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"]
}
# 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)
# 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
# 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
# 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"
# 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
#!/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 ==="
name: 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:
codesign --force --options runtime --timestamp --sign "Developer ID" MyApp.app
Error: The executable does not have the hardened runtime enabled.
Fix: Include entitlements in signing:
codesign --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:
find 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 expertiseActivates when the user asks about AI prompts, needs prompt templates, wants to search for prompts, or mentions prompts.chat. Use for discovering, retrieving, and improving prompts.
Search, retrieve, and install Agent Skills from the prompts.chat registry using MCP tools. Use when the user asks to find skills, browse skill catalogs, install a skill for Claude, or extend Claude's capabilities with reusable AI agent components.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.