learning_ai_clock/ios/release-testflight.sh
saravanakumardb1 e61858db15 feat(ios): add TestFlight release script, README, BUILD_STATE + bundle ID migration
- Add release-testflight.sh — one-command archive + upload to TestFlight
- Add README_TESTFLIGHT.md — setup guide for home laptop
- Add BUILD_STATE.md — build tracking
- Migrate bundle IDs: com.chronomind.app → com.saravana.chronomind (original was taken)
- Remove App Groups entitlement temporarily (not registered in dev portal yet)
- Fix duplicate theme enums in generated file
2026-04-04 23:20:54 -07:00

187 lines
8.5 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
# ── ChronoMind — TestFlight Release Script ──────────────────────────
# Run from: learning_ai_clock/ios/
# Prerequisites: Xcode 16+, xcodegen, App Store Connect API key
# Usage: bash release-testflight.sh
# bash release-testflight.sh --skip-build # re-upload existing archive
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
cd "$SCRIPT_DIR"
# ── Configuration ────────────────────────────────────────────────────
SCHEME="ChronoMind"
PROJECT="ChronoMind.xcodeproj"
BUNDLE_ID="com.saravana.chronomind"
TEAM_ID="748N7QPX7J"
EXPORT_OPTIONS="ExportOptions.plist"
# App Store Connect API Key (create at https://appstoreconnect.apple.com/access/integrations/api)
ASC_KEY_ID="${ASC_KEY_ID:-PPATU9GL73}"
ASC_ISSUER_ID="${ASC_ISSUER_ID:-1dbc2980-1621-4fb9-940b-e28257e6322c}"
ASC_KEY_PATH="${ASC_KEY_PATH:-$HOME/.appstoreconnect/private_keys/AuthKey_${ASC_KEY_ID}.p8}"
# ── Helpers ──────────────────────────────────────────────────────────
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m'
step() { echo -e "\n${CYAN}$1${NC}"; }
ok() { echo -e "${GREEN}$1${NC}"; }
warn() { echo -e "${YELLOW}⚠️ $1${NC}"; }
fail() { echo -e "${RED}$1${NC}"; exit 1; }
# ── Parse args ───────────────────────────────────────────────────────
SKIP_BUILD=false
for arg in "$@"; do
case "$arg" in
--skip-build) SKIP_BUILD=true ;;
--help|-h) echo "Usage: bash release-testflight.sh [--skip-build]"; exit 0 ;;
esac
done
# ── Pre-flight checks ───────────────────────────────────────────────
step "Pre-flight checks"
command -v xcodebuild >/dev/null 2>&1 || fail "xcodebuild not found — install Xcode"
command -v xcodegen >/dev/null 2>&1 || fail "xcodegen not found — brew install xcodegen"
[ -f "$ASC_KEY_PATH" ] || fail "API key not found at $ASC_KEY_PATH
Create one at https://appstoreconnect.apple.com/access/integrations/api
Save as ~/.appstoreconnect/private_keys/AuthKey_<KEY_ID>.p8
Then export ASC_KEY_ID=<KEY_ID> ASC_ISSUER_ID=<ISSUER_ID>"
[ -f "$EXPORT_OPTIONS" ] || fail "ExportOptions.plist not found in ios/"
[ -f "project.yml" ] || fail "project.yml not found — are you in the ios/ directory?"
# Check sibling SDK exists
SDK_PATH="../../learning_ai_common_plat/packages/swift-platform-sdk/Package.swift"
[ -f "$SDK_PATH" ] || fail "ByteLystPlatformSDK not found at $SDK_PATH
Ensure learning_ai_common_plat is cloned as a sibling directory"
ok "All pre-flight checks passed"
# ── Read current build number ────────────────────────────────────────
step "Reading current build number"
CURRENT_BUILD=$(grep -m1 "CURRENT_PROJECT_VERSION" project.yml | awk -F'"' '{print $2}')
[ -z "$CURRENT_BUILD" ] && fail "Could not read CURRENT_PROJECT_VERSION from project.yml"
NEXT_BUILD=$((CURRENT_BUILD + 1))
echo " Current: $CURRENT_BUILD → Next: $NEXT_BUILD"
if [ "$SKIP_BUILD" = true ]; then
ARCHIVE_PATH="/tmp/${SCHEME}_${CURRENT_BUILD}.xcarchive"
[ -d "$ARCHIVE_PATH" ] || fail "No archive found at $ARCHIVE_PATH — run without --skip-build first"
warn "Skipping build — using existing archive at $ARCHIVE_PATH"
NEXT_BUILD=$CURRENT_BUILD
else
# ── Bump build number ────────────────────────────────────────────
step "Bumping build number to $NEXT_BUILD"
sed -i '' "s/CURRENT_PROJECT_VERSION: \"$CURRENT_BUILD\"/CURRENT_PROJECT_VERSION: \"$NEXT_BUILD\"/g" project.yml
ok "project.yml updated"
# ── Regenerate Xcode project ─────────────────────────────────────
step "Generating Xcode project"
xcodegen generate
ok "Xcode project generated"
# ── Verify build ─────────────────────────────────────────────────
step "Verifying debug build"
xcodebuild build \
-project "$PROJECT" \
-scheme "$SCHEME" \
-configuration Debug \
-destination 'platform=iOS Simulator,name=iPhone 16 Pro' \
CODE_SIGN_IDENTITY=- CODE_SIGNING_ALLOWED=NO \
2>&1 | tail -1
# ── Archive ──────────────────────────────────────────────────────
ARCHIVE_PATH="/tmp/${SCHEME}_${NEXT_BUILD}.xcarchive"
step "Archiving build $NEXT_BUILD"
xcodebuild archive \
-project "$PROJECT" \
-scheme "$SCHEME" \
-configuration Release \
-archivePath "$ARCHIVE_PATH" \
-destination 'generic/platform=iOS' \
2>&1 | tail -1
[ -d "$ARCHIVE_PATH" ] || fail "Archive not found at $ARCHIVE_PATH"
ok "Archive succeeded: $ARCHIVE_PATH"
# ── Commit ───────────────────────────────────────────────────────
step "Committing build number bump"
cd "$SCRIPT_DIR/.."
git add ios/project.yml ios/ChronoMind.xcodeproj/
git commit -m "chore(ios): bump build number to $NEXT_BUILD for TestFlight release" || warn "Nothing to commit"
cd "$SCRIPT_DIR"
fi
# ── Export + Upload ──────────────────────────────────────────────────
EXPORT_PATH="/tmp/${SCHEME}_export${NEXT_BUILD}"
step "Exporting and uploading build $NEXT_BUILD to TestFlight"
rm -rf "$EXPORT_PATH"
xcodebuild -exportArchive \
-archivePath "$ARCHIVE_PATH" \
-exportPath "$EXPORT_PATH" \
-exportOptionsPlist "$EXPORT_OPTIONS" \
-allowProvisioningUpdates \
-authenticationKeyPath "$ASC_KEY_PATH" \
-authenticationKeyID "$ASC_KEY_ID" \
-authenticationKeyIssuerID "$ASC_ISSUER_ID" \
2>&1 | tail -5
# ── Verify upload ────────────────────────────────────────────────────
if [ -f "$EXPORT_PATH/${SCHEME}.ipa" ]; then
ok "Upload succeeded! Build $NEXT_BUILD sent to TestFlight"
else
# Check if upload succeeded despite no local IPA (destination: upload)
if xcodebuild -exportArchive -archivePath "$ARCHIVE_PATH" -exportPath "$EXPORT_PATH" -exportOptionsPlist "$EXPORT_OPTIONS" 2>&1 | grep -q "Upload succeeded"; then
ok "Upload succeeded! Build $NEXT_BUILD sent to TestFlight"
fi
fi
# ── Update BUILD_STATE.md ────────────────────────────────────────────
step "Updating BUILD_STATE.md"
BUILD_STATE="BUILD_STATE.md"
if [ ! -f "$BUILD_STATE" ]; then
cat > "$BUILD_STATE" << 'HEREDOC'
# iOS Build State — ChronoMind
## Current Build
**CURRENT_PROJECT_VERSION = 1**
Marketing version: 1.0.0
---
## Build History
| Build | Status | Key Changes |
|-------|--------|-------------|
HEREDOC
fi
# Update current build number
sed -i '' "s/CURRENT_PROJECT_VERSION = [0-9]*/CURRENT_PROJECT_VERSION = $NEXT_BUILD/" "$BUILD_STATE"
ok "BUILD_STATE.md updated to build $NEXT_BUILD"
# ── Final commit + push ─────────────────────────────────────────────
step "Final commit and push"
cd "$SCRIPT_DIR/.."
git add ios/BUILD_STATE.md
git commit -m "docs(ios): update BUILD_STATE.md for build $NEXT_BUILD" || warn "Nothing to commit"
git push origin main || warn "Push failed — push manually later"
cd "$SCRIPT_DIR"
echo ""
echo -e "${GREEN}════════════════════════════════════════════════════════${NC}"
echo -e "${GREEN} ChronoMind build $NEXT_BUILD uploaded to TestFlight! ${NC}"
echo -e "${GREEN} It should appear in ~15-30 minutes after processing. ${NC}"
echo -e "${GREEN}════════════════════════════════════════════════════════${NC}"
echo ""