chore(INFRA-3591): temp Bitrise RN cache + iOS E2E smoke POC #1
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: "[TEMP] Bitrise iOS RN cache + E2E POC" | |
| # TEMPORARY WORKFLOW for INFRA-3591 | |
| # Purpose: Same as temp-bitrise-ios-rn.yml (Bitrise RN build cache + iOS E2E app build), then runs | |
| # iOS E2E smoke tests on Bitrise. Not part of the CI gate; remove when benchmarking is complete. | |
| on: | |
| pull_request: | |
| types: [ labeled, synchronize ] | |
| # Hourly at :30 UTC — staggered from temp-bitrise-ios-rn.yml (:00). Note: temp-bitrise-ios-kv.yml | |
| # also schedules at :30; expect concurrent Bitrise load unless offsets change. | |
| schedule: | |
| - cron: "30 * * * *" | |
| concurrency: | |
| group: bitrise-ios-rn-e2e-${{ github.head_ref || github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| id-token: write | |
| jobs: | |
| build-ios-on-bitrise: | |
| name: Build iOS E2E App (RN cache) | |
| if: >- | |
| (github.event_name != 'pull_request' || | |
| contains(github.event.pull_request.labels.*.name, 'bitrise-poc')) && | |
| !github.event.pull_request.head.repo.fork | |
| runs-on: | |
| group: temp-bitrise-runners | |
| labels: [ "bitrise_pool_name:DemoFAXL" ] | |
| timeout-minutes: 60 | |
| outputs: | |
| artifacts-url: ${{ steps.set-artifacts-url.outputs.artifacts-url }} | |
| env: | |
| IOS_APP_CACHE_VERSION: 2 | |
| RCT_NO_LAUNCH_PACKAGER: 1 | |
| XCODE_BUILD_SETTINGS: "COMPILER_INDEX_STORE_ENABLE=NO" | |
| GITHUB_CI: "true" | |
| PLATFORM: ios | |
| METAMASK_ENVIRONMENT: qa | |
| METAMASK_BUILD_TYPE: main | |
| IS_TEST: true | |
| E2E: "true" | |
| IGNORE_BOXLOGS_DEVELOPMENT: true | |
| CI: "true" | |
| NODE_OPTIONS: "--max-old-space-size=8192" | |
| BRIDGE_USE_DEV_APIS: "true" | |
| RAMP_INTERNAL_BUILD: "true" | |
| SEEDLESS_ONBOARDING_ENABLED: "true" | |
| MM_NOTIFICATIONS_UI_ENABLED: "true" | |
| MM_SECURITY_ALERTS_API_ENABLED: "true" | |
| YARN_ENABLE_GLOBAL_CACHE: "true" | |
| FEATURES_ANNOUNCEMENTS_ACCESS_TOKEN: ${{ secrets.FEATURES_ANNOUNCEMENTS_ACCESS_TOKEN }} | |
| FEATURES_ANNOUNCEMENTS_SPACE_ID: ${{ secrets.FEATURES_ANNOUNCEMENTS_SPACE_ID }} | |
| SEGMENT_WRITE_KEY_QA: ${{ secrets.SEGMENT_WRITE_KEY_QA }} | |
| SEGMENT_PROXY_URL_QA: ${{ secrets.SEGMENT_PROXY_URL_QA }} | |
| SEGMENT_DELETE_API_SOURCE_ID_QA: ${{ secrets.SEGMENT_DELETE_API_SOURCE_ID_QA }} | |
| SEGMENT_REGULATIONS_ENDPOINT_QA: ${{ secrets.SEGMENT_REGULATIONS_ENDPOINT_QA }} | |
| MM_SENTRY_DSN_TEST: ${{ secrets.MM_SENTRY_DSN_TEST }} | |
| MM_SENTRY_AUTH_TOKEN: ${{ secrets.MM_SENTRY_AUTH_TOKEN }} | |
| MAIN_IOS_GOOGLE_CLIENT_ID_UAT: ${{ secrets.MAIN_IOS_GOOGLE_CLIENT_ID_UAT }} | |
| MAIN_IOS_GOOGLE_REDIRECT_URI_UAT: ${{ secrets.MAIN_IOS_GOOGLE_REDIRECT_URI_UAT }} | |
| MAIN_ANDROID_APPLE_CLIENT_ID_UAT: ${{ secrets.MAIN_ANDROID_APPLE_CLIENT_ID_UAT }} | |
| MAIN_ANDROID_GOOGLE_CLIENT_ID_UAT: ${{ secrets.MAIN_ANDROID_GOOGLE_CLIENT_ID_UAT }} | |
| MAIN_ANDROID_GOOGLE_SERVER_CLIENT_ID_UAT: ${{ secrets.MAIN_ANDROID_GOOGLE_SERVER_CLIENT_ID_UAT }} | |
| GOOGLE_SERVICES_B64_IOS: ${{ secrets.GOOGLE_SERVICES_B64_IOS }} | |
| GOOGLE_SERVICES_B64_ANDROID: ${{ secrets.GOOGLE_SERVICES_B64_ANDROID }} | |
| MM_INFURA_PROJECT_ID: ${{ secrets.MM_INFURA_PROJECT_ID }} | |
| MM_PREDICT_GTM_MODAL_ENABLED: "false" | |
| BITRISE_BUILD_CACHE_WORKSPACE_ID: ${{ vars.BITRISE_BUILD_CACHE_WORKSPACE_ID }} | |
| BITRISE_BUILD_CACHE_AUTH_TOKEN: ${{ secrets.BITRISE_BUILD_CACHE_AUTH_TOKEN }} | |
| steps: | |
| - name: Checkout repo | |
| uses: actions/checkout@v6 | |
| - name: Print runner environment diagnostics | |
| run: | | |
| echo "=== Runner Diagnostics ===" | |
| echo "Runner OS: ${{ runner.os }}" | |
| echo "Runner Arch: ${{ runner.arch }}" | |
| echo "macOS version: $(sw_vers -productVersion)" | |
| echo "Memory: $(sysctl -n hw.memsize | awk '{print $1/1024/1024/1024 " GB"}')" | |
| echo "Disk free: $(df -h / | tail -1 | awk '{print $4}')" | |
| echo "CPU cores: $(sysctl -n hw.ncpu)" | |
| echo "=== Xcode ===" | |
| xcode-select -p 2>/dev/null | sed "s|$HOME|~|g" || echo "No Xcode selected" | |
| xcodebuild -version 2>/dev/null | head -2 || echo "xcodebuild not available" | |
| echo "=== Ruby ===" | |
| ruby --version 2>/dev/null || echo "No ruby" | |
| echo "=== Node ===" | |
| node --version 2>/dev/null || echo "No node" | |
| shell: bash | |
| - name: Fix Vagrant environment paths | |
| run: | | |
| if [ -L /Users/runner ]; then | |
| current_target="$(readlink /Users/runner)" | |
| if [ "$current_target" = "/Users/vagrant" ]; then | |
| echo "Symlink already correct: /Users/runner -> /Users/vagrant" | |
| else | |
| echo "Replacing incorrect symlink /Users/runner -> $current_target" | |
| sudo rm /Users/runner | |
| sudo ln -s /Users/vagrant /Users/runner | |
| echo "Recreated symlink: /Users/runner -> /Users/vagrant" | |
| fi | |
| elif [ -e /Users/runner ]; then | |
| echo "Error: /Users/runner exists but is not a symlink" | |
| ls -ld /Users/runner | |
| exit 1 | |
| else | |
| sudo ln -s /Users/vagrant /Users/runner | |
| echo "Created symlink: /Users/runner -> /Users/vagrant" | |
| fi | |
| mkdir -p "$HOME/hostedtoolcache" "$HOME/tmp" | |
| echo "RUNNER_TOOL_CACHE=$HOME/hostedtoolcache" >> "$GITHUB_ENV" | |
| echo "RUNNER_TEMP=$HOME/tmp" >> "$GITHUB_ENV" | |
| shell: bash | |
| # ────────────────────────────────────────────────────────────────────── | |
| # REMOVED: "Restore Xcode derived data from branch cache" step | |
| # Was: cirruslabs/cache for ~/Library/Developer/Xcode/DerivedData + ios/build | |
| # Reason: Replaced by Bitrise React Native Build Cache which provides | |
| # granular, build-system-level caching (Xcode via LLVM CAS, | |
| # C++ via ccache) instead of coarse tar-and-restore of DerivedData. | |
| # | |
| # REMOVED: "Restore Xcode derived data from main cache" step | |
| # Was: cirruslabs/cache/restore (restore-only fallback to main branch) | |
| # Reason: Same as above. | |
| # ────────────────────────────────────────────────────────────────────── | |
| - name: Installing iOS Environment Setup | |
| timeout-minutes: 15 | |
| uses: ./.github/actions/setup-e2e-env | |
| with: | |
| platform: ios | |
| setup-simulator: false | |
| configure-keystores: false | |
| - name: Print iOS tool versions | |
| run: | | |
| echo "Node.js Version: $(node -v || echo 'not found')" | |
| echo "Yarn Version: $(yarn -v || echo 'not found')" | |
| echo "CocoaPods Version: $(pod --version || echo 'not found')" | |
| echo "Xcode Path: $(xcode-select -p || echo 'not found')" | |
| echo "Ruby Version: $(ruby --version || echo 'not found')" | |
| echo "Booted iOS Simulators:" | |
| xcrun simctl list | grep Booted || echo "No booted simulators found" | |
| shell: bash | |
| - name: Clean iOS plist files | |
| run: find ios -name "*.plist" -exec xattr -c {} \; | |
| - name: Restore .metamask folder | |
| id: restore-metamask | |
| uses: actions/cache@v5 | |
| with: | |
| path: .metamask | |
| key: .metamask-${{ hashFiles('package.json', 'yarn.lock') }} | |
| - name: Install Foundry if cache missed | |
| if: steps.restore-metamask.outputs.cache-hit != 'true' | |
| run: yarn install:foundryup | |
| - name: Setup project dependencies with retry | |
| uses: nick-fields/retry@ce71cc2ab81d554ebbe88c79ab5975992d79ba08 # v3.0.2 | |
| with: | |
| timeout_minutes: 10 | |
| max_attempts: 3 | |
| retry_wait_seconds: 30 | |
| command: | | |
| echo "Setting up project..." | |
| yarn setup:github-ci --build-ios --no-build-android | |
| # ────────────────────────────────────────────────────────────────────── | |
| # NEW: Activate Bitrise Build Cache for React Native | |
| # Configures build-system-level remote caching for: | |
| # - Xcode compilation outputs (LLVM CAS via Xcelerate) | |
| # - C++ native modules (ccache backed by Bitrise ABCS) | |
| # Must run BEFORE any step that triggers a native build (xcodebuild). | |
| # Does NOT cache Metro JS bundling or node_modules. | |
| # ────────────────────────────────────────────────────────────────────── | |
| - name: Activate Bitrise Build Cache for React Native | |
| env: | |
| BITRISE_BUILD_CACHE_AUTH_TOKEN: ${{ secrets.BITRISE_BUILD_CACHE_AUTH_TOKEN }} | |
| BITRISE_BUILD_CACHE_WORKSPACE_ID: ${{ vars.BITRISE_BUILD_CACHE_WORKSPACE_ID }} | |
| run: | | |
| #!/usr/bin/env bash | |
| set -euxo pipefail | |
| # Download Bitrise Build Cache CLI | |
| curl --retry 5 -sSfL 'https://raw.githubusercontent.com/bitrise-io/bitrise-build-cache-cli/main/install/installer.sh' | sh -s -- -b /tmp/bin -d | |
| # Activate React Native build cache (configures Xcode + ccache) | |
| /tmp/bin/bitrise-build-cache activate react-native | |
| echo "/tmp/bin" >> "$GITHUB_PATH" | |
| shell: bash | |
| - name: Generate current fingerprint | |
| id: generate-fingerprint | |
| run: | | |
| FINGERPRINT=$(yarn fingerprint:generate) | |
| echo "fingerprint=$FINGERPRINT" >> "$GITHUB_OUTPUT" | |
| echo "Current fingerprint: ${FINGERPRINT}" | |
| # ── Changed: cirruslabs/cache → actions/cache@v5 ── | |
| - name: Restore iOS app matching fingerprint from branch cache | |
| id: cache-restore | |
| uses: actions/cache@v5 | |
| with: | |
| path: | | |
| ios/build/Build/Products/Release-iphonesimulator/MetaMask.app | |
| key: bitrise-ios-app-${{ github.ref_name }}-v${{ env.IOS_APP_CACHE_VERSION | |
| }}-${{ steps.generate-fingerprint.outputs.fingerprint }} | |
| # ── Changed: cirruslabs/cache/restore → actions/cache/restore@v5 ── | |
| - name: Restore iOS app matching fingerprint from main cache | |
| if: ${{ steps.cache-restore.outputs.cache-hit != 'true' && github.ref_name != | |
| 'main' }} | |
| id: cache-restore-main | |
| uses: actions/cache/restore@v5 | |
| with: | |
| path: | | |
| ios/build/Build/Products/Release-iphonesimulator/MetaMask.app | |
| key: bitrise-ios-app-main-v${{ env.IOS_APP_CACHE_VERSION }}-${{ | |
| steps.generate-fingerprint.outputs.fingerprint }} | |
| # ── Changed: Wrapped with bitrise-build-cache react-native run ── | |
| - name: Build iOS E2E App | |
| if: ${{ steps.cache-restore.outputs.cache-hit != 'true' && | |
| steps.cache-restore-main.outputs.cache-hit != 'true' }} | |
| run: | | |
| echo "Building iOS E2E App on Bitrise runner..." | |
| bitrise-build-cache react-native run yarn build:ios:main:e2e | |
| shell: bash | |
| env: | |
| PLATFORM: ios | |
| METAMASK_ENVIRONMENT: qa | |
| METAMASK_BUILD_TYPE: main | |
| IS_TEST: true | |
| IS_SIM_BUILD: "true" | |
| IGNORE_BOXLOGS_DEVELOPMENT: true | |
| GITHUB_CI: "true" | |
| CI: "true" | |
| SEGMENT_WRITE_KEY_QA: ${{ secrets.SEGMENT_WRITE_KEY_QA }} | |
| SEGMENT_PROXY_URL_QA: ${{ secrets.SEGMENT_PROXY_URL_QA }} | |
| SEGMENT_DELETE_API_SOURCE_ID_QA: ${{ secrets.SEGMENT_DELETE_API_SOURCE_ID_QA }} | |
| SEGMENT_REGULATIONS_ENDPOINT_QA: ${{ secrets.SEGMENT_REGULATIONS_ENDPOINT_QA }} | |
| MM_SENTRY_DSN_TEST: ${{ secrets.MM_SENTRY_DSN_TEST }} | |
| MM_SENTRY_AUTH_TOKEN: ${{ secrets.MM_SENTRY_AUTH_TOKEN }} | |
| MAIN_IOS_GOOGLE_CLIENT_ID_UAT: ${{ secrets.MAIN_IOS_GOOGLE_CLIENT_ID_UAT }} | |
| MAIN_IOS_GOOGLE_REDIRECT_URI_UAT: ${{ secrets.MAIN_IOS_GOOGLE_REDIRECT_URI_UAT }} | |
| MAIN_ANDROID_APPLE_CLIENT_ID_UAT: ${{ secrets.MAIN_ANDROID_APPLE_CLIENT_ID_UAT }} | |
| MAIN_ANDROID_GOOGLE_CLIENT_ID_UAT: ${{ secrets.MAIN_ANDROID_GOOGLE_CLIENT_ID_UAT }} | |
| MAIN_ANDROID_GOOGLE_SERVER_CLIENT_ID_UAT: ${{ secrets.MAIN_ANDROID_GOOGLE_SERVER_CLIENT_ID_UAT }} | |
| GOOGLE_SERVICES_B64_IOS: ${{ secrets.GOOGLE_SERVICES_B64_IOS }} | |
| GOOGLE_SERVICES_B64_ANDROID: ${{ secrets.GOOGLE_SERVICES_B64_ANDROID }} | |
| # Repack is JS-only bundling (no native build) — no wrapping needed | |
| - name: Repack iOS app with JS updates | |
| if: ${{ steps.cache-restore.outputs.cache-hit == 'true' || | |
| steps.cache-restore-main.outputs.cache-hit == 'true' }} | |
| run: | | |
| echo "Repacking iOS app with updated JavaScript bundle..." | |
| yarn build:repack:ios | |
| echo "Final app size: $(du -sh "ios/build/Build/Products/Release-iphonesimulator/MetaMask.app" | cut -f1)" | |
| env: | |
| PLATFORM: ios | |
| METAMASK_ENVIRONMENT: qa | |
| METAMASK_BUILD_TYPE: main | |
| IS_TEST: true | |
| E2E: "true" | |
| IGNORE_BOXLOGS_DEVELOPMENT: true | |
| GITHUB_CI: "true" | |
| CI: "true" | |
| NODE_OPTIONS: "--max-old-space-size=8192" | |
| BRIDGE_USE_DEV_APIS: "true" | |
| RAMP_INTERNAL_BUILD: "true" | |
| SEEDLESS_ONBOARDING_ENABLED: "true" | |
| MM_NOTIFICATIONS_UI_ENABLED: "true" | |
| MM_SECURITY_ALERTS_API_ENABLED: "true" | |
| FEATURES_ANNOUNCEMENTS_ACCESS_TOKEN: ${{ secrets.FEATURES_ANNOUNCEMENTS_ACCESS_TOKEN }} | |
| FEATURES_ANNOUNCEMENTS_SPACE_ID: ${{ secrets.FEATURES_ANNOUNCEMENTS_SPACE_ID }} | |
| SEGMENT_WRITE_KEY_QA: ${{ secrets.SEGMENT_WRITE_KEY_QA }} | |
| SEGMENT_PROXY_URL_QA: ${{ secrets.SEGMENT_PROXY_URL_QA }} | |
| SEGMENT_DELETE_API_SOURCE_ID_QA: ${{ secrets.SEGMENT_DELETE_API_SOURCE_ID_QA }} | |
| SEGMENT_REGULATIONS_ENDPOINT_QA: ${{ secrets.SEGMENT_REGULATIONS_ENDPOINT_QA }} | |
| MM_SENTRY_DSN_TEST: ${{ secrets.MM_SENTRY_DSN_TEST }} | |
| MM_SENTRY_AUTH_TOKEN: ${{ secrets.MM_SENTRY_AUTH_TOKEN }} | |
| MAIN_IOS_GOOGLE_CLIENT_ID_UAT: ${{ secrets.MAIN_IOS_GOOGLE_CLIENT_ID_UAT }} | |
| MAIN_IOS_GOOGLE_REDIRECT_URI_UAT: ${{ secrets.MAIN_IOS_GOOGLE_REDIRECT_URI_UAT }} | |
| MAIN_ANDROID_APPLE_CLIENT_ID_UAT: ${{ secrets.MAIN_ANDROID_APPLE_CLIENT_ID_UAT }} | |
| MAIN_ANDROID_GOOGLE_CLIENT_ID_UAT: ${{ secrets.MAIN_ANDROID_GOOGLE_CLIENT_ID_UAT }} | |
| MAIN_ANDROID_GOOGLE_SERVER_CLIENT_ID_UAT: ${{ secrets.MAIN_ANDROID_GOOGLE_SERVER_CLIENT_ID_UAT }} | |
| GOOGLE_SERVICES_B64_IOS: ${{ secrets.GOOGLE_SERVICES_B64_IOS }} | |
| GOOGLE_SERVICES_B64_ANDROID: ${{ secrets.GOOGLE_SERVICES_B64_ANDROID }} | |
| MM_INFURA_PROJECT_ID: ${{ secrets.MM_INFURA_PROJECT_ID }} | |
| - name: Fix iOS bundle executable case and permissions before upload | |
| run: | | |
| APP_PATH="ios/build/Build/Products/Release-iphonesimulator/MetaMask.app" | |
| BUNDLE_EXEC=$(/usr/libexec/PlistBuddy -c "Print CFBundleExecutable" "$APP_PATH/Info.plist" 2>/dev/null) | |
| if [ -z "$BUNDLE_EXEC" ]; then | |
| echo "Could not read CFBundleExecutable from Info.plist" | |
| exit 1 | |
| fi | |
| ACTUAL_PATH=$(find "$APP_PATH" -maxdepth 1 -iname "$BUNDLE_EXEC" -type f | head -1) | |
| if [ -z "$ACTUAL_PATH" ]; then | |
| echo "Bundle executable not found: $BUNDLE_EXEC" | |
| exit 1 | |
| fi | |
| if [ "$(basename "$ACTUAL_PATH")" != "$BUNDLE_EXEC" ]; then | |
| mv "$ACTUAL_PATH" "$APP_PATH/${BUNDLE_EXEC}_fix" | |
| mv "$APP_PATH/${BUNDLE_EXEC}_fix" "$APP_PATH/$BUNDLE_EXEC" | |
| fi | |
| chmod +x "$APP_PATH/$BUNDLE_EXEC" | |
| shell: bash | |
| - name: Upload iOS APP Artifact (Simulator) | |
| id: upload-app | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: main-qa-MetaMask.app | |
| path: ios/build/Build/Products/Release-iphonesimulator/MetaMask.app | |
| retention-days: 7 | |
| if-no-files-found: error | |
| - name: Upload iOS Source Map | |
| id: upload-sourcemap | |
| if: ${{ steps.cache-restore.outputs.cache-hit == 'true' || | |
| steps.cache-restore-main.outputs.cache-hit == 'true' }} | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: main-qa-index.js.map | |
| path: sourcemaps/ios/index.js.map | |
| retention-days: 7 | |
| if-no-files-found: error | |
| continue-on-error: true | |
| - name: Set Artifacts URL and Status | |
| id: set-artifacts-url | |
| run: | | |
| ARTIFACTS_URL="https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" | |
| echo "artifacts-url=${ARTIFACTS_URL}" >> "$GITHUB_OUTPUT" | |
| echo "Artifacts available at: ${ARTIFACTS_URL}" | |
| echo "" | |
| echo "Upload Status Summary:" | |
| echo "- APP (Simulator): ${{ steps.upload-app.outcome }}" | |
| echo "- Source Map: ${{ steps.upload-sourcemap.outcome }}" | |
| env: | |
| GITHUB_REPOSITORY: ${{ github.repository }} | |
| GITHUB_RUN_ID: ${{ github.run_id }} | |
| # Record timing data for benchmarking | |
| - name: Record build timing summary | |
| if: always() | |
| run: | | |
| echo "=== Bitrise iOS RN cache — build timing summary ===" | |
| echo "Runner platform: ${{ runner.os }}/${{ runner.arch }}" | |
| echo "Build outcome: ${{ steps.upload-app.outcome }}" | |
| echo "Cache hit (branch): ${{ steps.cache-restore.outputs.cache-hit }}" | |
| echo "Cache hit (main): ${{ steps.cache-restore-main.outputs.cache-hit || 'N/A' }}" | |
| echo "Build cache: Bitrise React Native Build Cache (Xcode LLVM CAS + ccache)" | |
| echo "Record this data in the benchmark tracker referenced by the related ticket or PR description." | |
| shell: bash | |
| e2e-smoke-tests-ios: | |
| name: iOS E2E Smoke Tests (Bitrise) | |
| permissions: | |
| contents: read | |
| id-token: write | |
| needs: [ build-ios-on-bitrise ] | |
| uses: ./.github/workflows/run-e2e-smoke-tests-ios.yml | |
| with: | |
| use_bitrise_runner: true | |
| secrets: inherit |