Created
June 20, 2026 18:18
-
-
Save nfarina/78a6873ef66603abf8f80f2944db8239 to your computer and use it in GitHub Desktop.
Submit to App Store from macOS 27 Beta
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # | |
| # GUI-free App Store release. | |
| # | |
| # The Xcode *app* won't launch on the macOS 27 beta, but the stable (GM) Xcode's | |
| # command-line tools work fine — and App Store Connect only rejects builds made | |
| # with the *beta* SDK. So we archive, export, and upload entirely from the CLI | |
| # using the GM toolchain via DEVELOPER_DIR, no Xcode GUI required. | |
| # | |
| # Bump CURRENT_PROJECT_VERSION in the project first (App Store build numbers | |
| # can't be reused), then just run from the repo root: | |
| # | |
| # ./ios/release.sh | |
| # | |
| # Credentials, in precedence order: | |
| # 1. App Store Connect API key, if ASC_KEY_ID/ASC_ISSUER_ID are exported | |
| # (put AuthKey_<KEY_ID>.p8 in ~/.appstoreconnect/private_keys/ first). | |
| # 2. App-specific password pulled from the macOS login keychain item | |
| # "AC_PASSWORD" — never stored on disk. Apple ID defaults to APPLE_ID | |
| # below. See "Keychain setup" at the bottom to (re)store it on a machine. | |
| set -euo pipefail | |
| # Apple ID for the app-specific-password upload path; override by exporting | |
| # APPLE_ID. The password is read from the login keychain (no plaintext on disk); | |
| # override with APP_PW=… for a one-off. | |
| APPLE_ID="${APPLE_ID:-nfarina@me.com}" | |
| APP_PW="${APP_PW:-@keychain:AC_PASSWORD}" | |
| # Which Xcode to build with. App Store Connect rejects both *beta* SDKs and | |
| # anything older than the current Release Candidate (ITMS-90111), so this must | |
| # point at the latest RC/GM Xcode — NOT whatever `xcode-select` happens to be. | |
| # Override per-run, e.g.: | |
| # DEVELOPER_DIR=/Applications/Xcode-26.6.app/Contents/Developer ./ios/release.sh | |
| # Check the accepted versions at https://developer.apple.com/news/releases/. | |
| export DEVELOPER_DIR="${DEVELOPER_DIR:-/Applications/Xcode.app/Contents/Developer}" | |
| ARCHIVE=/tmp/Sage.xcarchive | |
| EXPORT=/tmp/SageExport | |
| IPA="$EXPORT/Sage.ipa" | |
| echo "Using $(xcodebuild -version | tr '\n' ' ')" | |
| rm -rf "$ARCHIVE" "$EXPORT" | |
| xcodebuild archive \ | |
| -project ios/Sage.xcodeproj -scheme Sage \ | |
| -destination 'generic/platform=iOS' \ | |
| -archivePath "$ARCHIVE" \ | |
| -allowProvisioningUpdates | |
| xcodebuild -exportArchive \ | |
| -archivePath "$ARCHIVE" \ | |
| -exportPath "$EXPORT" \ | |
| -exportOptionsPlist ios/ExportOptions.plist \ | |
| -allowProvisioningUpdates | |
| echo "Built $IPA" | |
| if [[ -n "${ASC_KEY_ID:-}" ]]; then | |
| echo "Validating + uploading with App Store Connect API key…" | |
| xcrun altool --validate-app -f "$IPA" -t ios --apiKey "$ASC_KEY_ID" --apiIssuer "$ASC_ISSUER_ID" | |
| xcrun altool --upload-app -f "$IPA" -t ios --apiKey "$ASC_KEY_ID" --apiIssuer "$ASC_ISSUER_ID" | |
| elif [[ -n "${APP_PW:-}" ]]; then | |
| echo "Validating + uploading as $APPLE_ID with app-specific password…" | |
| xcrun altool --validate-app -f "$IPA" -t ios -u "$APPLE_ID" -p "$APP_PW" | |
| xcrun altool --upload-app -f "$IPA" -t ios -u "$APPLE_ID" -p "$APP_PW" | |
| else | |
| echo "No credentials found — IPA is ready at $IPA." | |
| echo "Store the keychain item (see below), or re-run with APP_PW / ASC_KEY_ID+ASC_ISSUER_ID." | |
| fi | |
| # --- Keychain setup (one-time per machine) ---------------------------------- | |
| # Store the app-specific password (account.apple.com → Sign-In & Security) in | |
| # the login keychain so this script reads it without a plaintext file. altool's | |
| # own --store-password-in-keychain-item flag is inconsistent across versions, so | |
| # create the item with `security` and grant altool access so it won't prompt: | |
| # | |
| # ALTOOL="$DEVELOPER_DIR/../SharedFrameworks/ContentDelivery.framework/Resources/altool" | |
| # security add-generic-password -U -s AC_PASSWORD -a "$APPLE_ID" \ | |
| # -w "<app-specific-password>" -T "$ALTOOL" -T /usr/bin/xcrun | |
| # | |
| # Verify: xcrun altool --validate-app -f <ipa> -t ios -u "$APPLE_ID" -p @keychain:AC_PASSWORD |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment