$npx -y skills add rorkai/app-store-connect-cli-skills --skill asc-notarizationArchive, export, and notarize macOS apps using xcodebuild and asc. Use when you need to prepare a macOS app for distribution outside the App Store with Developer ID signing and Apple notarization.
| 1 | # macOS Notarization |
| 2 | |
| 3 | Use this skill when you need to notarize a macOS app for distribution outside the App Store. |
| 4 | |
| 5 | ## Preconditions |
| 6 | - Xcode installed and command line tools configured. |
| 7 | - Auth is configured (`asc auth login` or `ASC_*` env vars). |
| 8 | - A Developer ID Application certificate in the local keychain. |
| 9 | - The app's Xcode project builds for macOS. |
| 10 | |
| 11 | ## Preflight: Verify Signing Identity |
| 12 | |
| 13 | Before archiving, confirm a valid Developer ID Application identity exists: |
| 14 | |
| 15 | ```bash |
| 16 | security find-identity -v -p codesigning | grep "Developer ID Application" |
| 17 | ``` |
| 18 | |
| 19 | If no identity is found, create one at https://developer.apple.com/account/resources/certificates/add (the App Store Connect API does not support creating Developer ID certificates). |
| 20 | |
| 21 | ### Fix Broken Trust Settings |
| 22 | |
| 23 | If `codesign` or `xcodebuild` fails with "Invalid trust settings" or "errSecInternalComponent", the certificate may have custom trust overrides that break the chain: |
| 24 | |
| 25 | ```bash |
| 26 | # Check for custom trust settings |
| 27 | security dump-trust-settings 2>&1 | grep -A1 "Developer ID" |
| 28 | |
| 29 | # If overrides exist, export the cert and remove them |
| 30 | security find-certificate -c "Developer ID Application" -p ~/Library/Keychains/login.keychain-db > /tmp/devid-cert.pem |
| 31 | security remove-trusted-cert /tmp/devid-cert.pem |
| 32 | ``` |
| 33 | |
| 34 | ### Verify Certificate Chain |
| 35 | |
| 36 | After fixing trust settings, verify the chain is intact: |
| 37 | |
| 38 | ```bash |
| 39 | codesign --deep --force --options runtime --sign "Developer ID Application: YOUR NAME (TEAM_ID)" /path/to/any.app 2>&1 |
| 40 | ``` |
| 41 | |
| 42 | The signing must show the chain: Developer ID Application → Developer ID Certification Authority → Apple Root CA. |
| 43 | |
| 44 | ## Step 1: Archive |
| 45 | |
| 46 | ```bash |
| 47 | xcodebuild archive \ |
| 48 | -scheme "YourMacScheme" \ |
| 49 | -configuration Release \ |
| 50 | -archivePath /tmp/YourApp.xcarchive \ |
| 51 | -destination "generic/platform=macOS" |
| 52 | ``` |
| 53 | |
| 54 | ## Step 2: Export with Developer ID |
| 55 | |
| 56 | Create an ExportOptions plist for Developer ID distribution: |
| 57 | |
| 58 | ```xml |
| 59 | <?xml version="1.0" encoding="UTF-8"?> |
| 60 | <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
| 61 | <plist version="1.0"> |
| 62 | <dict> |
| 63 | <key>method</key> |
| 64 | <string>developer-id</string> |
| 65 | <key>signingStyle</key> |
| 66 | <string>automatic</string> |
| 67 | <key>teamID</key> |
| 68 | <string>YOUR_TEAM_ID</string> |
| 69 | </dict> |
| 70 | </plist> |
| 71 | ``` |
| 72 | |
| 73 | Export the archive: |
| 74 | |
| 75 | ```bash |
| 76 | xcodebuild -exportArchive \ |
| 77 | -archivePath /tmp/YourApp.xcarchive \ |
| 78 | -exportPath /tmp/YourAppExport \ |
| 79 | -exportOptionsPlist ExportOptions.plist |
| 80 | ``` |
| 81 | |
| 82 | This produces a `.app` bundle signed with Developer ID Application and a secure timestamp. |
| 83 | |
| 84 | ### Verify the Export |
| 85 | |
| 86 | ```bash |
| 87 | codesign -dvvv "/tmp/YourAppExport/YourApp.app" 2>&1 | grep -E "Authority|Timestamp" |
| 88 | ``` |
| 89 | |
| 90 | Confirm: |
| 91 | - Authority chain starts with "Developer ID Application" |
| 92 | - A Timestamp is present |
| 93 | |
| 94 | ## Step 3: Create a ZIP for Notarization |
| 95 | |
| 96 | ```bash |
| 97 | ditto -c -k --keepParent "/tmp/YourAppExport/YourApp.app" "/tmp/YourAppExport/YourApp.zip" |
| 98 | ``` |
| 99 | |
| 100 | ## Step 4: Submit for Notarization |
| 101 | |
| 102 | ### Fire-and-forget |
| 103 | ```bash |
| 104 | asc notarization submit --file "/tmp/YourAppExport/YourApp.zip" |
| 105 | ``` |
| 106 | |
| 107 | ### Wait for result |
| 108 | ```bash |
| 109 | asc notarization submit --file "/tmp/YourAppExport/YourApp.zip" --wait |
| 110 | ``` |
| 111 | |
| 112 | ### Custom polling |
| 113 | ```bash |
| 114 | asc notarization submit --file "/tmp/YourAppExport/YourApp.zip" --wait --poll-interval 30s --timeout 1h |
| 115 | ``` |
| 116 | |
| 117 | ## Step 5: Check Results |
| 118 | |
| 119 | ### Status |
| 120 | ```bash |
| 121 | asc notarization status --id "SUBMISSION_ID" --output table |
| 122 | ``` |
| 123 | |
| 124 | ### Developer Log (for failures) |
| 125 | ```bash |
| 126 | asc notarization log --id "SUBMISSION_ID" |
| 127 | ``` |
| 128 | |
| 129 | Fetch the log URL to see detailed issues: |
| 130 | ```bash |
| 131 | curl -sL "LOG_URL" | python3 -m json.tool |
| 132 | ``` |
| 133 | |
| 134 | ### List Previous Submissions |
| 135 | ```bash |
| 136 | asc notarization list --output table |
| 137 | asc notarization list --limit 5 --output table |
| 138 | ``` |
| 139 | |
| 140 | ## Step 6: Staple (Optional) |
| 141 | |
| 142 | After notarization succeeds, staple the ticket so the app works offline: |
| 143 | |
| 144 | ```bash |
| 145 | xcrun stapler staple "/tmp/YourAppExport/YourApp.app" |
| 146 | ``` |
| 147 | |
| 148 | For DMG or PKG distribution, staple after creating the container: |
| 149 | ```bash |
| 150 | # Create DMG |
| 151 | hdiutil create -volname "YourApp" -srcfolder "/tmp/YourAppExport/YourApp.app" -ov -format UDZO "/tmp/YourApp.dmg" |
| 152 | xcrun stapler staple "/tmp/YourApp.dmg" |
| 153 | ``` |
| 154 | |
| 155 | ## Supported File Formats |
| 156 | |
| 157 | | Format | Use Case | |
| 158 | |--------|----------| |
| 159 | | `.zip` | Simplest; zip a signed `.app` bundle | |
| 160 | | `.dmg` | Disk image for drag-and-drop install | |
| 161 | | `.pkg` | Installer package (requires Developer ID Installer certificate) | |
| 162 | |
| 163 | ## PKG Notarization |
| 164 | |
| 165 | To notarize `.pkg` files, you need a **Developer ID Installer** certificate (separate from Developer ID Application). This certificate type is not available through the App Store Connect API — create it at https://developer.apple.com/account/resources/certificates/add. |
| 166 | |
| 167 | Sign the package: |
| 168 | ```bash |
| 169 | productsign --sign "Developer ID Installer: YOUR NAME (TEAM_ID)" unsigned.pkg si |