mirror of
https://github.com/stablyai/orca.git
synced 2026-11-13 09:27:29 +00:00
192 lines
7.2 KiB
YAML
192 lines
7.2 KiB
YAML
name: Mobile iOS Release
|
||
|
||
# Release iOS independently so App Store review cannot block Android.
|
||
on:
|
||
push:
|
||
tags:
|
||
- 'mobile-ios-v*'
|
||
workflow_dispatch:
|
||
inputs:
|
||
bump_patch_version:
|
||
description: 'Use the first open iOS patch version after the checked-in version, skipping versions already closed on the App Store.'
|
||
required: false
|
||
default: false
|
||
type: boolean
|
||
release_version:
|
||
description: 'Optional exact iOS marketing version override, e.g. 0.0.15'
|
||
required: false
|
||
type: string
|
||
testflight_changelog:
|
||
description: 'Optional TestFlight external tester changelog'
|
||
required: false
|
||
type: string
|
||
|
||
permissions:
|
||
contents: read
|
||
|
||
jobs:
|
||
ios-build:
|
||
# Xcode 27.1's iOS SDK enables iPhone Duo resizing; Expo SDK 55 requires Swift 6 syntax.
|
||
runs-on: xcode-27
|
||
# Archive + upload is ~30–40m when healthy; 90m leaves margin for setup.
|
||
timeout-minutes: 90
|
||
|
||
env:
|
||
# Freeze Gemfile.lock in both jobs; installs ~25min apart must not resolve different fastlane versions.
|
||
BUNDLE_FROZEN: 'true'
|
||
|
||
outputs:
|
||
release_version: ${{ steps.release_metadata.outputs.version }}
|
||
build_number: ${{ steps.release_metadata.outputs.build_number }}
|
||
|
||
defaults:
|
||
run:
|
||
working-directory: mobile
|
||
|
||
steps:
|
||
- name: Checkout
|
||
uses: actions/checkout@v6
|
||
|
||
- name: Select Xcode
|
||
uses: maxim-lobanov/setup-xcode@v1
|
||
with:
|
||
# iOS 27.1 SDK: iPhone Duo resizing and reserved (fold/camera) regions.
|
||
xcode-version: '27.1'
|
||
|
||
- name: Setup Node.js
|
||
uses: actions/setup-node@v6
|
||
with:
|
||
node-version: 24
|
||
check-latest: true
|
||
|
||
- name: Setup pnpm
|
||
uses: pnpm/setup@v2
|
||
with:
|
||
install: false
|
||
|
||
- name: Install dependencies
|
||
run: pnpm install --frozen-lockfile
|
||
|
||
- name: Setup Ruby and fastlane
|
||
uses: ruby/setup-ruby@v1
|
||
with:
|
||
ruby-version: '3.3'
|
||
bundler-cache: true
|
||
working-directory: mobile
|
||
|
||
- name: Resolve release version and build number
|
||
env:
|
||
ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
|
||
ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
|
||
ASC_API_KEY_P8: ${{ secrets.ASC_API_KEY_P8 }}
|
||
MOBILE_IOS_RELEASE_VERSION: ${{ github.event.inputs.release_version }}
|
||
MOBILE_IOS_BUMP_PATCH_VERSION: ${{ github.event.inputs.bump_patch_version }}
|
||
FASTLANE_SKIP_UPDATE_CHECK: '1'
|
||
FASTLANE_HIDE_CHANGELOG: '1'
|
||
run: bundle exec fastlane ios prepare_release_version version:"$MOBILE_IOS_RELEASE_VERSION" bump_patch:"$MOBILE_IOS_BUMP_PATCH_VERSION"
|
||
|
||
- name: Capture release metadata
|
||
id: release_metadata
|
||
run: node -e 'const fs = require("node:fs"); const { expo } = require("./app.json"); fs.appendFileSync(process.env.GITHUB_OUTPUT, `version=${expo.version}\nbuild_number=${expo.ios.buildNumber}\n`)'
|
||
|
||
- name: Expo prebuild
|
||
env:
|
||
ORCA_IOS_APS_ENVIRONMENT: production
|
||
run: npx expo prebuild --platform ios --no-install
|
||
|
||
- name: Install CocoaPods
|
||
working-directory: mobile/ios
|
||
run: bundle exec pod install
|
||
|
||
# Import exported distribution .p12 into ephemeral runner keychain: provisioning updates/API key cannot recreate its private key.
|
||
- name: Import distribution certificate
|
||
env:
|
||
IOS_DIST_CERT_P12: ${{ secrets.IOS_DIST_CERT_P12 }}
|
||
IOS_DIST_CERT_PASSWORD: ${{ secrets.IOS_DIST_CERT_PASSWORD }}
|
||
run: |
|
||
set -euo pipefail
|
||
KEYCHAIN_PATH="$RUNNER_TEMP/orca-signing.keychain-db"
|
||
# Random per-run keychain password; never persisted.
|
||
KEYCHAIN_PASSWORD="$(openssl rand -base64 24)"
|
||
CERT_PATH="$RUNNER_TEMP/orca-dist-cert.p12"
|
||
|
||
echo "$IOS_DIST_CERT_P12" | base64 --decode > "$CERT_PATH"
|
||
|
||
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
|
||
security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
|
||
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
|
||
security import "$CERT_PATH" -P "$IOS_DIST_CERT_PASSWORD" \
|
||
-A -t cert -f pkcs12 -k "$KEYCHAIN_PATH"
|
||
# Allow codesign/xcodebuild to use the key without an interactive prompt.
|
||
security set-key-partition-list -S apple-tool:,apple: \
|
||
-k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" >/dev/null
|
||
# Put our keychain in the search list so xcodebuild can find the identity.
|
||
security list-keychains -d user -s "$KEYCHAIN_PATH" login.keychain-db
|
||
rm -f "$CERT_PATH"
|
||
|
||
- name: Build and upload to TestFlight
|
||
env:
|
||
ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
|
||
ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
|
||
ASC_API_KEY_P8: ${{ secrets.ASC_API_KEY_P8 }}
|
||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||
# Keep fastlane non-interactive and quiet about analytics in CI.
|
||
FASTLANE_SKIP_UPDATE_CHECK: '1'
|
||
FASTLANE_HIDE_CHANGELOG: '1'
|
||
# Metro's release transform of the 3.6 MB Mermaid module needs >2 GB heap; the runner's default cap sits at that edge.
|
||
NODE_OPTIONS: --max-old-space-size=4096
|
||
run: bundle exec fastlane ios build_and_upload
|
||
|
||
- name: Upload .ipa artifact
|
||
if: always()
|
||
uses: actions/upload-artifact@v7
|
||
with:
|
||
name: orca-mobile-ipa
|
||
path: mobile/build/*.ipa
|
||
if-no-files-found: ignore
|
||
|
||
ios-distribute:
|
||
name: Distribute to external TestFlight testers
|
||
needs: ios-build
|
||
runs-on: ubuntu-latest
|
||
# Fastlane's processing wait fails after 20m; this outer bound leaves setup
|
||
# margin without allowing App Store Connect polling to hang for hours.
|
||
timeout-minutes: 30
|
||
|
||
env:
|
||
# Same fastlane as ios-build, or fail before touching App Store Connect.
|
||
BUNDLE_FROZEN: 'true'
|
||
|
||
defaults:
|
||
run:
|
||
working-directory: mobile
|
||
|
||
steps:
|
||
- name: Checkout
|
||
uses: actions/checkout@v6
|
||
|
||
- name: Setup Ruby and fastlane
|
||
uses: ruby/setup-ruby@v1
|
||
with:
|
||
ruby-version: '3.3'
|
||
bundler-cache: true
|
||
working-directory: mobile
|
||
|
||
- name: Wait for processing and distribute to peeps
|
||
env:
|
||
ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
|
||
ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
|
||
ASC_API_KEY_P8: ${{ secrets.ASC_API_KEY_P8 }}
|
||
TESTFLIGHT_CHANGELOG: ${{ github.event.inputs.testflight_changelog }}
|
||
MOBILE_IOS_RELEASE_VERSION: ${{ needs.ios-build.outputs.release_version }}
|
||
MOBILE_IOS_BUILD_NUMBER: ${{ needs.ios-build.outputs.build_number }}
|
||
FASTLANE_SKIP_UPDATE_CHECK: '1'
|
||
FASTLANE_HIDE_CHANGELOG: '1'
|
||
run: |
|
||
if ! bundle exec fastlane ios distribute_testflight \
|
||
version:"$MOBILE_IOS_RELEASE_VERSION" \
|
||
build_number:"$MOBILE_IOS_BUILD_NUMBER"; then
|
||
echo "::error::TestFlight $MOBILE_IOS_RELEASE_VERSION ($MOBILE_IOS_BUILD_NUMBER) was uploaded but not distributed to peeps. If App Store Connect is still processing it, rerun only this failed job after the build becomes Ready; otherwise inspect the Fastlane error above."
|
||
exit 1
|
||
fi
|