| 1 | #!/usr/bin/env bash |
| 2 | # Build, sign, and upload a TestFlight release of the iPhone app (LChat), then |
| 3 | # hand the uploaded build to ios/asc-testflight.mjs, which does the App Store |
| 4 | # Connect side: wait out processing, declare export compliance, seed "What to |
| 5 | # Test", and place the build with the tester groups. |
| 6 | # |
| 7 | # The Mac's counterpart is ios/release-mac.sh (Developer ID + Sparkle). Mint |
| 8 | # both from the same version/build so the two apps stay in step — they are one |
| 9 | # product (CLAUDE.md § Conventions). |
| 10 | # |
| 11 | # Usage: ios/release-ios.sh <marketing-version> <build-number> [--external] |
| 12 | # ios/release-ios.sh 0.9.0 1 # internal testers only |
| 13 | # ios/release-ios.sh 0.9.0 1 --external # …and submit for external review |
| 14 | # |
| 15 | # Required env: |
| 16 | # LCHAT_ASC_KEY_PATH / LCHAT_ASC_KEY_ID / LCHAT_ASC_ISSUER_ID |
| 17 | # The App Store Connect API key (see the operator runbook). The key must |
| 18 | # have Admin role — an App Manager key can't mint the cloud distribution |
| 19 | # certificate and dies with "Cloud signing permission error". |
| 20 | # Optional env: |
| 21 | # LCHAT_RELEASE_NOTES_MD Path to the release notes; becomes the build's |
| 22 | # "What to Test" text for testers. |
| 23 | # LCHAT_ASC_APP_ID Default 6787060127 (the Eleven Messenger app record). |
| 24 | # |
| 25 | # ALWAYS pass the -authenticationKey* flags (they're built in below): without |
| 26 | # them xcodebuild scans Xcode's stored ASC accounts, and one dead account |
| 27 | # session aborts the whole export with "Error Downloading App Information". |
| 28 | set -euo pipefail |
| 29 | |
| 30 | VERSION="${1:?usage: release-ios.sh <marketing-version> <build-number> [--external]}" |
| 31 | BUILD="${2:?usage: release-ios.sh <marketing-version> <build-number> [--external]}" |
| 32 | EXTERNAL="${3:-}" |
| 33 | : "${LCHAT_ASC_KEY_PATH:?LCHAT_ASC_KEY_PATH is required (ASC API key .p8 path)}" |
| 34 | : "${LCHAT_ASC_KEY_ID:?LCHAT_ASC_KEY_ID is required}" |
| 35 | : "${LCHAT_ASC_ISSUER_ID:?LCHAT_ASC_ISSUER_ID is required}" |
| 36 | TEAM_ID="J5NDF5PCD8" |
| 37 | |
| 38 | REPO="$(cd "$(dirname "$0")/.." && pwd)" |
| 39 | IOS="$REPO/ios" |
| 40 | TMP="$(mktemp -d)" |
| 41 | trap 'rm -rf "$TMP"' EXIT |
| 42 | |
| 43 | ASC_AUTH=(-authenticationKeyPath "$LCHAT_ASC_KEY_PATH" -authenticationKeyID "$LCHAT_ASC_KEY_ID" -authenticationKeyIssuerID "$LCHAT_ASC_ISSUER_ID") |
| 44 | |
| 45 | echo "Building Eleven $VERSION ($BUILD) — App Store Connect / TestFlight" |
| 46 | |
| 47 | cd "$IOS" |
| 48 | xcodegen generate |
| 49 | |
| 50 | xcodebuild archive \ |
| 51 | -project LChat.xcodeproj -scheme LChat -destination 'generic/platform=iOS' \ |
| 52 | -archivePath "$TMP/LChat.xcarchive" -allowProvisioningUpdates \ |
| 53 | CODE_SIGN_STYLE=Automatic MARKETING_VERSION="$VERSION" CURRENT_PROJECT_VERSION="$BUILD" |
| 54 | |
| 55 | # -exportOptionsPlist has no CLI flag form, so template a throwaway one. |
| 56 | # manageAppVersionAndBuildNumber is deliberately FALSE: it would override the |
| 57 | # numbers we just archived with whatever ASC thinks comes next, and the whole |
| 58 | # point is that the Mac and the iPhone carry the same version. |
| 59 | cat > "$TMP/export-options.plist" <<PLIST |
| 60 | <?xml version="1.0" encoding="UTF-8"?> |
| 61 | <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
| 62 | <plist version="1.0"> |
| 63 | <dict> |
| 64 | <key>method</key> |
| 65 | <string>app-store-connect</string> |
| 66 | <key>destination</key> |
| 67 | <string>upload</string> |
| 68 | <key>teamID</key> |
| 69 | <string>$TEAM_ID</string> |
| 70 | <key>signingStyle</key> |
| 71 | <string>automatic</string> |
| 72 | <key>uploadSymbols</key> |
| 73 | <true/> |
| 74 | <key>manageAppVersionAndBuildNumber</key> |
| 75 | <false/> |
| 76 | </dict> |
| 77 | </plist> |
| 78 | PLIST |
| 79 | |
| 80 | xcodebuild -exportArchive -archivePath "$TMP/LChat.xcarchive" -exportPath "$TMP/export" \ |
| 81 | -exportOptionsPlist "$TMP/export-options.plist" -allowProvisioningUpdates "${ASC_AUTH[@]}" |
| 82 | |
| 83 | echo "Uploaded. Handing over to App Store Connect…" |
| 84 | exec node "$IOS/asc-testflight.mjs" "$VERSION" "$BUILD" $EXTERNAL |
| 85 | |