name: macos-notarization-workflow description: 使用 xcrun notarytool 自动化苹果公证流程,用于 macOS 应用程序分发 allowed-tools: Read, Write, Edit, Bash, Glob, Grep tags: [macos, notarization, codesign, apple, distribution]
macOS 公证工作流
使用 xcrun notarytool 自动化苹果公证工作流,用于 macOS 应用程序。此技能处理完整的公证流程,包括提交、状态检查和钉合。
能力
- 通过 notarytool 提交应用程序进行公证
- 监控公证状态
- 将公证票据钉合到应用程序
- 处理公证错误
- 生成 CI/CD 公证脚本
- 配置 App Store Connect API 密钥
- 提交前验证应用程序
- 生成公证报告
输入模式
{
"type": "object",
"properties": {
"projectPath": {
"type": "string",
"description": "项目路径"
},
"appPath": {
"type": "string",
"description": "已签名的应用程序包或 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"]
}
公证工作流
1. 先决条件
# 确保已安装 Xcode 命令行工具
xcode-select --install
# 验证代码签名
codesign --verify --deep --strict MyApp.app
codesign -vvv --deep --strict MyApp.app
# 检查强化运行时
codesign -dvvv MyApp.app | grep runtime
# 应显示:flags=0x10000(runtime)
2. 存储凭据(推荐)
# 将 App Store Connect API 密钥存储在钥匙串中
xcrun notarytool store-credentials "MyProfile" \
--key ~/private_keys/AuthKey_XXXXXXXXXX.p8 \
--key-id XXXXXXXXXX \
--issuer xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
# 或存储 Apple ID 凭据
xcrun notarytool store-credentials "MyAppleIDProfile" \
--apple-id your.email@example.com \
--team-id XXXXXXXXXX \
--password @keychain:AC_PASSWORD
3. 提交公证
# 使用存储的凭据
xcrun notarytool submit MyApp.app \
--keychain-profile "MyProfile" \
--wait
# 直接使用 API 密钥
xcrun notarytool submit MyApp.app \
--key ~/private_keys/AuthKey_XXXXXXXXXX.p8 \
--key-id XXXXXXXXXX \
--issuer xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx \
--wait
# 使用 Apple ID
xcrun notarytool submit MyApp.app \
--apple-id your.email@example.com \
--team-id XXXXXXXXXX \
--password @keychain:AC_PASSWORD \
--wait
4. 检查状态
# 检查特定提交
xcrun notarytool info <submission-id> \
--keychain-profile "MyProfile"
# 获取提交日志
xcrun notarytool log <submission-id> \
--keychain-profile "MyProfile" \
developer_log.json
# 列出最近的提交
xcrun notarytool history \
--keychain-profile "MyProfile"
5. 钉合票据
# 钉合到应用程序包
xcrun stapler staple MyApp.app
# 钉合到 DMG
xcrun stapler staple MyApp.dmg
# 钉合到 pkg
xcrun stapler staple MyApp.pkg
# 验证钉合
xcrun stapler validate MyApp.app
完整脚本
#!/bin/bash
# notarize.sh
set -e
APP_PATH="${1}"
KEYCHAIN_PROFILE="${2:-MyProfile}"
echo "=== 验证应用程序包 ==="
codesign --verify --deep --strict "$APP_PATH"
echo "=== 提交公证 ==="
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 "提交 ID: $SUBMISSION_ID"
echo "状态: $STATUS"
if [ "$STATUS" != "Accepted" ]; then
echo "=== 公证失败,获取日志 ==="
xcrun notarytool log "$SUBMISSION_ID" \
--keychain-profile "$KEYCHAIN_PROFILE" \
notarization_log.json
cat notarization_log.json
exit 1
fi
echo "=== 钉合票据 ==="
xcrun stapler staple "$APP_PATH"
echo "=== 验证钉合 ==="
xcrun stapler validate "$APP_PATH"
echo "=== 公证完成 ==="
GitHub Actions 集成
name: 构建与公证
on:
push:
tags: ['v*']
jobs:
build:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: 导入签名证书
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: 构建应用程序
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: 存储公证凭据
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: 公证应用程序
run: |
xcrun notarytool submit build/MyApp.app \
--keychain-profile "CI_PROFILE" \
--wait
xcrun stapler staple build/MyApp.app
- name: 创建 DMG
run: |
create-dmg build/MyApp.app build/
xcrun notarytool submit build/*.dmg \
--keychain-profile "CI_PROFILE" \
--wait
xcrun stapler staple build/*.dmg
- name: 上传制品
uses: actions/upload-artifact@v4
with:
name: MyApp
path: build/*.dmg
常见问题
问题:未启用强化运行时
错误:签名不包含安全时间戳。
修复:使用强化运行时和时间戳签名:
codesign --force --options runtime --timestamp --sign "Developer ID" MyApp.app
问题:缺少权利
错误:可执行文件未启用强化运行时。
修复:在签名中包含权利:
codesign --force --options runtime --timestamp \
--entitlements MyApp.entitlements \
--sign "Developer ID Application: Company" MyApp.app
问题:嵌套代码未签名
错误:二进制文件的签名无效。
修复:对所有嵌套组件签名:
find MyApp.app -name "*.dylib" -o -name "*.framework" | \
xargs -I {} codesign --force --options runtime --timestamp --sign "Developer ID" {}
最佳实践
- 使用 App Store Connect API:比 Apple ID 更可靠
- 安全存储凭据:使用钥匙串配置文件
- 提交前验证:codesign --verify
- 始终钉合:使离线验证成为可能
- 归档提交日志:用于调试
- 在全新 Mac 上测试:验证 Gatekeeper 接受度
相关技能
macos-entitlements-generator- 权利配置macos-codesign-workflow- 代码签名code-signing-setupprocess - 完整签名工作流
相关代理
swiftui-macos-expert- macOS 开发code-signing-specialist- 签名专业知识