Upload to TestFlight #30
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
| name: Upload to TestFlight | |
| # Dedicated workflow to build iOS (via build.yml) and upload to TestFlight. | |
| # All TestFlight logic lives here; build.yml only builds and uploads artifacts. | |
| # | |
| on: | |
| workflow_call: | |
| inputs: | |
| source_branch: | |
| description: 'Branch, tag, or SHA to build' | |
| required: true | |
| type: string | |
| environment: | |
| description: 'Build environment / track (exp, beta, rc)' | |
| required: true | |
| type: string | |
| testflight_group: | |
| description: 'TestFlight external testing group' | |
| required: false | |
| type: string | |
| default: 'MetaMask BETA & Release Candidates' | |
| workflow_dispatch: | |
| inputs: | |
| source_branch: | |
| description: 'Branch, tag, or SHA to build' | |
| required: true | |
| type: string | |
| default: 'main' | |
| environment: | |
| description: 'Build environment / track' | |
| required: true | |
| type: choice | |
| options: | |
| - exp | |
| - beta | |
| - rc | |
| default: rc | |
| testflight_group: | |
| description: 'TestFlight external testing group' | |
| required: true | |
| type: choice | |
| default: 'MetaMask BETA & Release Candidates' | |
| options: | |
| - 'MetaMask BETA & Release Candidates' | |
| - 'MM Card Team' | |
| - 'Ramp Provider Testing' | |
| # contents: write required by build.yml update-build-version job (version bump) | |
| permissions: | |
| contents: write | |
| id-token: write | |
| jobs: | |
| # Create a temporary branch so the version-bump commit can be pushed without | |
| # hitting branch-protection rules on the source branch (e.g. main). | |
| prepare-build-branch: | |
| uses: ./.github/workflows/create-build-branch.yml | |
| with: | |
| source_branch: ${{ inputs.source_branch }} | |
| secrets: inherit | |
| build: | |
| name: Build iOS (${{ inputs.environment || 'rc' }}) | |
| needs: [prepare-build-branch] | |
| uses: ./.github/workflows/build.yml | |
| with: | |
| build_name: main-${{ inputs.environment || 'rc' }} | |
| platform: ios | |
| skip_version_bump: false | |
| source_branch: ${{ needs.prepare-build-branch.outputs.build_branch }} | |
| secrets: inherit | |
| testflight-upload-summary: | |
| name: TestFlight upload summary | |
| needs: [build, prepare-build-branch] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| ref: ${{ needs.prepare-build-branch.outputs.build_branch }} | |
| - name: Display TestFlight upload summary | |
| run: | | |
| BUILD_VERSION=$(node -p "require('./package.json').version") | |
| BUILD_NUMBER=$(awk '/versionCode/{print $2}' android/app/build.gradle) | |
| { | |
| echo "### 📲 TestFlight Upload" | |
| echo "" | |
| echo "| Field | Value |" | |
| echo "| --- | --- |" | |
| echo "| **Source branch** | \`${{ inputs.source_branch }}\` |" | |
| echo "| **Build branch** | \`${{ needs.prepare-build-branch.outputs.build_branch }}\` |" | |
| echo "| **Build name** | \`main-${{ inputs.environment || 'rc' }}\` |" | |
| echo "| **Build version** | \`${BUILD_VERSION}\` |" | |
| echo "| **Build number** | \`${BUILD_NUMBER}\` |" | |
| echo "| **TestFlight group** | ${{ inputs.testflight_group || 'MetaMask BETA & Release Candidates' }} |" | |
| echo "| **Workflow ref** | \`${{ github.ref_name }}\` (required for AWS) |" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| # Pulls App Store Connect API keys from AWS Secrets Manager (OIDC). | |
| # Workflow must run from main; build uses the temporary build branch. | |
| upload-ios-testflight: | |
| name: Upload iOS to TestFlight | |
| needs: [build, testflight-upload-summary] | |
| runs-on: ghcr.io/cirruslabs/macos-runner:sequoia-xl | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup Ruby (iOS) | |
| uses: ruby/setup-ruby@44511735964dcb71245e7e55f72539531f7bc0eb #v1 | |
| with: | |
| ruby-version: '3.2.9' | |
| working-directory: ios | |
| bundler-cache: true | |
| - name: Download iOS IPA artifact | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: ios-ipa-main-${{ inputs.environment || 'rc' }} | |
| - name: Find IPA path | |
| id: ipa | |
| run: | | |
| IPA=$(find . -name '*.ipa' -type f | head -1) | |
| if [ -z "$IPA" ]; then | |
| echo "::error::No .ipa file found in artifact" | |
| exit 1 | |
| fi | |
| case "$IPA" in /*) ABS="$IPA" ;; *) ABS="$PWD/$IPA" ;; esac | |
| echo "path=$ABS" >> "$GITHUB_OUTPUT" | |
| - name: Configure AWS credentials | |
| uses: aws-actions/configure-aws-credentials@v4 | |
| with: | |
| role-to-assume: ${{ secrets.AWS_ROLE_APPLE_TESTFLIGHT }} | |
| aws-region: 'us-east-2' | |
| - name: Fetch Apple API keys from AWS Secrets Manager | |
| run: | | |
| echo "🔐 Fetching App Store Connect API keys from Secrets Manager..." | |
| secret_id="metamask-mobile-main-apple-api-keys" | |
| secret_json=$(aws secretsmanager get-secret-value \ | |
| --region 'us-east-2' \ | |
| --secret-id "$secret_id" \ | |
| --query SecretString \ | |
| --output text) | |
| for key in APP_STORE_CONNECT_API_KEY_ISSUER_ID APP_STORE_CONNECT_API_KEY_KEY_ID; do | |
| value=$(echo "$secret_json" | jq -r --arg k "$key" '.[$k] // empty') | |
| if [ -z "$value" ]; then | |
| echo "::error::Missing key in secret: $key" | |
| exit 1 | |
| fi | |
| echo "::add-mask::$value" | |
| echo "${key}=${value}" >> "$GITHUB_ENV" | |
| done | |
| key=APP_STORE_CONNECT_API_KEY_KEY_CONTENT | |
| value=$(echo "$secret_json" | jq -r --arg k "$key" '.[$k] // empty') | |
| if [ -z "$value" ]; then | |
| echo "::error::Missing key in secret: $key" | |
| exit 1 | |
| fi | |
| while IFS= read -r line || [ -n "$line" ]; do | |
| [ -n "$line" ] && echo "::add-mask::$line" | |
| done <<< "$(printf '%s\n' "$value")" | |
| delim="APPLEP8$(openssl rand -hex 16)" | |
| { | |
| printf '%s<<%s\n' "$key" "$delim" | |
| printf '%s\n' "$value" | |
| printf '%s\n' "$delim" | |
| } >> "$GITHUB_ENV" | |
| echo "✅ Apple API keys loaded from AWS" | |
| - name: Setup App Store Connect API Key | |
| run: | | |
| bash scripts/setup-app-store-connect-api-key.sh \ | |
| "$APP_STORE_CONNECT_API_KEY_ISSUER_ID" \ | |
| "$APP_STORE_CONNECT_API_KEY_KEY_ID" \ | |
| "$APP_STORE_CONNECT_API_KEY_KEY_CONTENT" | |
| - name: Upload to TestFlight | |
| run: | | |
| bash scripts/upload-to-testflight.sh \ | |
| "github_actions_main-${{ inputs.environment || 'rc' }}" \ | |
| "${{ inputs.source_branch }}" \ | |
| "${{ steps.ipa.outputs.path }}" \ | |
| "${{ inputs.testflight_group || 'MetaMask BETA & Release Candidates' }}" | |
| - name: Cleanup API Key | |
| if: always() | |
| run: | | |
| rm -f ios/AuthKey.p8 | |
| echo "🧹 Cleaned up API key file" | |
| cleanup-build-branch: | |
| name: Cleanup build branch | |
| needs: [prepare-build-branch, upload-ios-testflight] | |
| if: always() | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| token: ${{ secrets.PR_TOKEN || github.token }} | |
| - name: Delete temporary build branch | |
| env: | |
| BRANCH: ${{ needs.prepare-build-branch.outputs.build_branch }} | |
| run: | | |
| if [ -n "$BRANCH" ]; then | |
| git push origin --delete "$BRANCH" || true | |
| echo "🧹 Deleted build branch: $BRANCH" | |
| fi |