chore(INFRA-3591): add bitrise runners with resource monitoring #1331
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 E2E POC" | |
| # TEMPORARY WORKFLOW for Bitrise managed runner benchmarking. | |
| # Purpose: benchmark Bitrise GitHub Actions runners for iOS E2E. | |
| # This workflow is not part of the CI gate and should be removed after the POC. | |
| # | |
| # Triggers: workflow_dispatch (Actions tab — pick branch under "Use workflow from"), | |
| # pull_request (label bitrise-poc), and hourly schedule. | |
| on: | |
| workflow_dispatch: | |
| pull_request: | |
| types: [ labeled, synchronize ] | |
| schedule: | |
| - cron: "0 * * * *" | |
| concurrency: | |
| group: bitrise-ios-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 (Bitrise KV 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: | |
| XCODE_CACHE_VERSION: 1 | |
| 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" | |
| steps: | |
| - name: Checkout repo | |
| uses: actions/checkout@v4 | |
| - 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 | |
| - name: Restore Xcode derived data from branch cache | |
| id: xcode-restore-cache | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/Library/Developer/Xcode/DerivedData | |
| ios/build | |
| key: bitrise-${{ runner.os }}-xcode-${{ github.ref_name }}-${{ | |
| env.XCODE_CACHE_VERSION }}-${{ hashFiles('ios/**/*.{h,m,mm,swift}', | |
| 'ios/**/Podfile.lock', 'yarn.lock') }} | |
| - name: Restore Xcode derived data from main cache | |
| if: ${{ steps.xcode-restore-cache.outputs.cache-hit != 'true' && github.ref_name | |
| != 'main' }} | |
| id: xcode-restore-cache-main | |
| uses: actions/cache/restore@v4 | |
| with: | |
| path: | | |
| ~/Library/Developer/Xcode/DerivedData | |
| ios/build | |
| key: bitrise-${{ runner.os }}-xcode-main-${{ env.XCODE_CACHE_VERSION }}-${{ | |
| hashFiles('ios/**/*.{h,m,mm,swift}', 'ios/**/Podfile.lock', | |
| 'yarn.lock') }} | |
| - 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@v4 | |
| 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 | |
| - name: Generate current fingerprint | |
| id: generate-fingerprint | |
| run: | | |
| FINGERPRINT=$(yarn fingerprint:generate) | |
| echo "fingerprint=$FINGERPRINT" >> "$GITHUB_OUTPUT" | |
| echo "Current fingerprint: ${FINGERPRINT}" | |
| - name: Restore iOS app matching fingerprint from branch cache | |
| id: cache-restore | |
| uses: actions/cache@v4 | |
| 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 }} | |
| - 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@v4 | |
| 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 }} | |
| - 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 with KV cache only..." | |
| 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 }} | |
| - 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@v4 | |
| 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@v4 | |
| 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 }} | |
| - name: Record build timing summary | |
| if: always() | |
| run: | | |
| echo "=== Bitrise Runner Build Timing Summary ===" | |
| echo "Runner platform: ${{ runner.os }}/${{ runner.arch }}" | |
| echo "Build outcome: ${{ steps.upload-app.outcome }}" | |
| echo "Xcode cache hit (branch): ${{ steps.xcode-restore-cache.outputs.cache-hit }}" | |
| echo "Xcode cache hit (main): ${{ steps.xcode-restore-cache-main.outputs.cache-hit || 'N/A' }}" | |
| echo "App cache hit (branch): ${{ steps.cache-restore.outputs.cache-hit }}" | |
| echo "App cache hit (main): ${{ steps.cache-restore-main.outputs.cache-hit || 'N/A' }}" | |
| echo "Cache mode: Bitrise KV cache via actions/cache only" | |
| echo "Record CPU and memory utilization from the VM monitoring CSV for this job." | |
| 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 |