#!/usr/bin/env bash # compile-ios.sh — build signed iOS IPA and upload to TestFlight (App Store Connect) # # 用**本机 login keychain 的 Apple Distribution 证书** + **本机已装的 App Store # provisioning profile** + **本机 App Store Connect key(.p8)** 签名/上传。 # mac-runner 就是开发机,这些分发材料常驻本机,不再从 base64 secret 导入 # (原方案 IOS_DIST_CERT_P12_BASE64 里的证书过期 → archive 报 "No valid code # signing certificates";本机 login 里那张有效,2026-07 改用本机材料)。 # # 需要 env(非机密标识,gitea secret/变量;缺则用已知默认): # IOS_TEAM_ID(默认 BYL4KQHMTN)/ APPSTORE_API_KEY_ID(默认 3PZTHR8YMJ)/ # APPSTORE_API_ISSUER_ID(必填,无默认) # 需要本机:Apple Distribution 证书在 login keychain;App Store profile 已装; # ~/.appstoreconnect/private_keys/AuthKey_.p8 存在。 set -euo pipefail # shellcheck source=scripts/ci/_env.sh . "$(dirname "$0")/_env.sh" TAG="$1" VER="${TAG#client-v}" # CFBundleVersion(build 号)必须单调递增,否则 TestFlight 拒绝重复上传。 MAJOR="$(echo "$VER" | cut -d. -f1)" MINOR="$(echo "$VER" | cut -d. -f2)" PATCH="$(echo "$VER" | cut -d. -f3)" BUILD=$(( MAJOR * 10000 + MINOR * 100 + PATCH )) echo "==> compile-ios: version=${VER} build=${BUILD}" TEAM_ID="${IOS_TEAM_ID:-BYL4KQHMTN}" KEY_ID="${APPSTORE_API_KEY_ID:-3PZTHR8YMJ}" ISSUER="${APPSTORE_API_ISSUER_ID:-}" BUNDLE_ID="com.yanmei.jiu" P8="$HOME/.appstoreconnect/private_keys/AuthKey_${KEY_ID}.p8" # --- 前置检查:本机分发材料齐备(缺则 fail-fast,不产出未签名/无法上传的包)--- if ! security find-identity -v -p codesigning \ | grep -qF "Apple Distribution: Yanmei (beijing) Technology Co., Ltd (${TEAM_ID})"; then echo "ERROR: login keychain 未找到 Apple Distribution 证书(团队 ${TEAM_ID})——无法签名 iOS。" >&2 echo " 在本机 Xcode 登录 Apple ID / 装好分发证书后重试。" >&2 exit 1 fi if [ ! -f "$P8" ]; then echo "ERROR: 缺 App Store Connect key: $P8" >&2 exit 1 fi if [ -z "$ISSUER" ]; then echo "ERROR: 缺 APPSTORE_API_ISSUER_ID(App Store Connect Issuer ID)——公证/上传需要。" >&2 exit 1 fi WORK="$(mktemp -d)" # shellcheck disable=SC2064 trap "rm -rf '$WORK'" EXIT # --- 同步版本号 --- sed -i '' "s/^version:.*/version: ${VER}+${BUILD}/" client/pubspec.yaml # --- ExportOptions.plist(automatic 签名,app-store 分发)--- # 本机 login 里的 Apple Distribution 证书与旧的手动 profile 不配对 # (profile 是用另一张/旧证书签发的 → archive 报 "profile doesn't include # signing certificate")。改用**自动签名**:Xcode 用登录的 Apple ID + # App Store Connect API key(见下 xcodebuild -authenticationKeyPath) 现场 # 生成/更新与当前证书匹配的 App Store profile(-allowProvisioningUpdates), # 与 local_test.sh --ios 一致,避免手动材料漂移。 cat > "$WORK/ExportOptions.plist" < methodapp-store teamID${TEAM_ID} signingStyleautomatic uploadBitcode uploadSymbols EOF # --- 构建签名 IPA(自动签名;Xcode 现场配对证书+profile)--- # flutter build ipa 的 archive/export 都会带 -allowProvisioningUpdates(自动签名时), # 但云端/无交互登录需用 API key 授权 provisioning 更新 → 传 -authenticationKeyPath 等。 cd client flutter build ipa --release \ --build-name="${VER}" \ --build-number="${BUILD}" \ --export-options-plist="$WORK/ExportOptions.plist" \ "--dart-define=BASE_URL=https://jiu.51yanmei.com" \ "--dart-define=PUBLIC_URL=https://jiu.51yanmei.com" \ "--dart-define=APP_VERSION=v${VER}" cd .. IPA="$(ls client/build/ios/ipa/*.ipa 2>/dev/null | head -1)" if [ -z "$IPA" ] || [ ! -f "$IPA" ]; then echo "ERROR: IPA not found" >&2 exit 1 fi echo "==> compile-ios: built ${IPA}" # --- 上传 TestFlight(App Store Connect;本机 .p8 由 key-id 自动定位)--- echo "==> compile-ios: uploading to TestFlight" xcrun altool --upload-app -f "$IPA" -t ios \ --apiKey "${KEY_ID}" \ --apiIssuer "${ISSUER}" echo "==> compile-ios: done — uploaded build ${BUILD} to TestFlight"