Build Apps and Run Performance E2E Tests #4563
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: Build Apps and Run Performance E2E Tests | |
| # This workflow runs performance E2E tests every 3 hours during weekdays (Monday-Friday) | |
| # Schedule: Every 3 hours at the top of the hour (00:00, 03:00, 06:00, 09:00, 12:00, 15:00, 18:00, 21:00) | |
| # Days: Monday (1) through Friday (6) | |
| on: | |
| schedule: | |
| - cron: '0 */3 * * 1-6' | |
| push: | |
| branches: | |
| - main | |
| - 'release/*' | |
| workflow_dispatch: | |
| inputs: | |
| description: | |
| description: 'Optional description for this test run' | |
| required: false | |
| type: string | |
| sentry_target: | |
| description: 'Sentry target for performance events (test or real)' | |
| required: false | |
| type: choice | |
| options: | |
| - test | |
| - real | |
| default: test | |
| browserstack_app_url_android_onboarding: | |
| description: 'BrowserStack Android Onboarding App URL (bs://...)' | |
| required: false | |
| type: string | |
| browserstack_app_url_ios_onboarding: | |
| description: 'BrowserStack iOS Onboarding App URL (bs://...)' | |
| required: false | |
| type: string | |
| browserstack_app_url_android_imported_wallet: | |
| description: 'BrowserStack Android Imported Wallet App URL (bs://...)' | |
| required: false | |
| type: string | |
| browserstack_app_url_ios_imported_wallet: | |
| description: 'BrowserStack iOS Imported Wallet App URL (bs://...)' | |
| required: false | |
| type: string | |
| workflow_call: | |
| inputs: | |
| description: | |
| description: 'Optional description for this test run' | |
| required: false | |
| type: string | |
| sentry_target: | |
| description: 'Sentry target for performance events (test or real)' | |
| required: false | |
| type: string | |
| default: test | |
| browserstack_app_url_android_onboarding: | |
| description: 'BrowserStack Android Onboarding App URL (bs://...)' | |
| required: false | |
| type: string | |
| browserstack_app_url_ios_onboarding: | |
| description: 'BrowserStack iOS Onboarding App URL (bs://...)' | |
| required: false | |
| type: string | |
| browserstack_app_url_android_imported_wallet: | |
| description: 'BrowserStack Android Imported Wallet App URL (bs://...)' | |
| required: false | |
| type: string | |
| browserstack_app_url_ios_imported_wallet: | |
| description: 'BrowserStack iOS Imported Wallet App URL (bs://...)' | |
| required: false | |
| type: string | |
| branch_name: | |
| description: 'Branch name to use for build names (defaults to auto-detection)' | |
| required: false | |
| type: string | |
| build_variant: | |
| description: 'Build variant for Bitrise (rc = release, exp = experimental)' | |
| required: false | |
| type: string | |
| default: 'rc' | |
| permissions: | |
| contents: read | |
| id-token: write | |
| actions: write | |
| concurrency: | |
| # Include build_variant so experimental (exp) and release/direct (rc) don't share the same | |
| # queue — avoids "Build Apps" (direct/release) and experimental runs canceling each other. | |
| # Note: GitHub allows at most 1 running + 1 pending per group; a 3rd run cancels the pending one. | |
| group: performance-e2e-${{ github.ref }}-${{ github.event_name }}-${{ inputs.build_variant || 'rc' }} | |
| # Do not cancel in-progress runs; pending runs can still be replaced by a newer trigger. | |
| cancel-in-progress: false | |
| env: | |
| BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} | |
| BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} | |
| MM_TEST_ACCOUNT_SRP: ${{ secrets.MM_TEST_ACCOUNT_SRP }} | |
| TEST_SRP_1: ${{ secrets.TEST_SRP_1 }} | |
| TEST_SRP_2: ${{ secrets.TEST_SRP_2 }} | |
| TEST_SRP_3: ${{ secrets.TEST_SRP_3 }} | |
| E2E_PASSWORD: ${{ secrets.E2E_PASSWORD }} | |
| DISABLE_VIDEO_DOWNLOAD: true | |
| jobs: | |
| determine-branch-name: | |
| name: Determine Branch Name for Bitrise | |
| runs-on: ubuntu-latest | |
| outputs: | |
| branch_name: ${{ steps.get-branch.outputs.branch_name }} | |
| steps: | |
| - name: Get correct branch name | |
| id: get-branch | |
| env: | |
| INPUT_BRANCH: ${{ inputs.branch_name }} | |
| HEAD_REF: ${{ github.head_ref }} | |
| REF_NAME: ${{ github.ref_name }} | |
| EVENT_NAME: ${{ github.event_name }} | |
| run: | | |
| # Determine the correct branch name for all trigger scenarios: | |
| # - workflow_call (from ci.yml): uses INPUT_BRANCH (github.head_ref for PRs, github.ref_name for push) | |
| # - pull_request: uses HEAD_REF (source branch, e.g., "feature-branch") | |
| # - schedule: uses REF_NAME (typically "main") | |
| # - workflow_dispatch: uses INPUT_BRANCH if provided, otherwise REF_NAME | |
| # Priority: explicit input > PR head_ref > ref_name | |
| if [ -n "$INPUT_BRANCH" ]; then | |
| BRANCH_NAME="$INPUT_BRANCH" | |
| echo "Using explicit input: $BRANCH_NAME" | |
| elif [ "$EVENT_NAME" = "pull_request" ] && [ -n "$HEAD_REF" ]; then | |
| BRANCH_NAME="$HEAD_REF" | |
| echo "Using PR source branch: $BRANCH_NAME" | |
| else | |
| BRANCH_NAME="$REF_NAME" | |
| echo "Using ref_name: $BRANCH_NAME" | |
| fi | |
| echo "branch_name=$BRANCH_NAME" >> "$GITHUB_OUTPUT" | |
| echo "Branch for Bitrise: $BRANCH_NAME" | |
| read-device-matrix: | |
| name: Read Device Matrix | |
| runs-on: ubuntu-latest | |
| needs: [determine-branch-name] | |
| outputs: | |
| android_matrix: ${{ steps.read-matrix.outputs.android_matrix }} | |
| ios_matrix: ${{ steps.read-matrix.outputs.ios_matrix }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Select Devices | |
| id: read-matrix | |
| env: | |
| BRANCH_NAME: ${{ needs.determine-branch-name.outputs.branch_name }} | |
| run: | | |
| FILE="tests/performance/device-matrix.json" | |
| # Push to main or release branches: Use only "low" category devices | |
| # Schedule and workflow_dispatch: Use all devices | |
| if [ "${{ github.event_name }}" = "push" ]; then | |
| FILTER='[.[] | select(.category == "low")]' | |
| echo "Limited mode: low category devices only (event: ${{ github.event_name }}, branch: $BRANCH_NAME)" | |
| else | |
| FILTER='.' | |
| echo "Full mode: All devices (event: ${{ github.event_name }}, branch: $BRANCH_NAME)" | |
| fi | |
| ANDROID_MATRIX=$(jq ".android_devices | $FILTER" "$FILE") | |
| IOS_MATRIX=$(jq ".ios_devices | $FILTER" "$FILE") | |
| { | |
| echo "android_matrix<<EOF" | |
| echo "$ANDROID_MATRIX" | |
| echo "EOF" | |
| echo "ios_matrix<<EOF" | |
| echo "$IOS_MATRIX" | |
| echo "EOF" | |
| } >> "$GITHUB_OUTPUT" | |
| echo "Selected: $(echo "$ANDROID_MATRIX" | jq length) Android, $(echo "$IOS_MATRIX" | jq length) iOS" | |
| set-build-names: | |
| name: Set Unified BrowserStack Build Names | |
| runs-on: ubuntu-latest | |
| needs: [determine-branch-name] | |
| outputs: | |
| android_build_name: ${{ steps.set-builds.outputs.android_build_name }} | |
| ios_build_name: ${{ steps.set-builds.outputs.ios_build_name }} | |
| steps: | |
| - name: Set unified build names | |
| id: set-builds | |
| run: | | |
| BRANCH_NAME="${{ needs.determine-branch-name.outputs.branch_name }}" | |
| echo "android_build_name=Android-Performance-$BRANCH_NAME-Branch" >> "$GITHUB_OUTPUT" | |
| echo "ios_build_name=iOS-Performance-$BRANCH_NAME-Branch" >> "$GITHUB_OUTPUT" | |
| echo "Set unified build names:" | |
| echo " Android: Android-Performance-$BRANCH_NAME-Branch" | |
| echo " iOS: iOS-Performance-$BRANCH_NAME-Branch" | |
| trigger-android-dual-versions: | |
| name: Trigger Android Dual Versions and Extract BrowserStack URLs | |
| uses: ./.github/workflows/build-android-upload-to-browserstack.yml | |
| needs: [determine-branch-name] | |
| if: (!inputs.browserstack_app_url_android_onboarding && !inputs.browserstack_app_url_android_imported_wallet) | |
| with: | |
| branch_name: ${{ needs.determine-branch-name.outputs.branch_name }} | |
| build_variant: ${{ inputs.build_variant || 'rc' }} | |
| secrets: inherit | |
| trigger-ios-dual-versions: | |
| name: Trigger iOS Dual Versions and Extract BrowserStack URLs | |
| uses: ./.github/workflows/build-ios-upload-to-browserstack.yml | |
| needs: [determine-branch-name] | |
| if: (!inputs.browserstack_app_url_ios_onboarding && !inputs.browserstack_app_url_ios_imported_wallet) | |
| with: | |
| branch_name: ${{ needs.determine-branch-name.outputs.branch_name }} | |
| build_variant: ${{ inputs.build_variant || 'rc' }} | |
| secrets: inherit | |
| # ============================================================================= | |
| # PHASE 1: ONBOARDING TESTS (Run First) | |
| # ============================================================================= | |
| run-android-onboarding-tests: | |
| name: Run Android Onboarding Tests | |
| uses: ./.github/workflows/performance-test-runner.yml | |
| needs: | |
| [ | |
| read-device-matrix, | |
| trigger-android-dual-versions, | |
| set-build-names, | |
| determine-branch-name, | |
| ] | |
| if: always() && !failure() && !cancelled() && (needs.trigger-android-dual-versions.result == 'skipped' || needs.trigger-android-dual-versions.result == 'success') && (inputs.browserstack_app_url_android_onboarding != '' || needs.trigger-android-dual-versions.outputs.without-srp-browserstack-url != '') | |
| with: | |
| platform: android | |
| build_type: onboarding | |
| sentry_target: ${{ inputs.sentry_target || 'test' }} | |
| build_variant: ${{ inputs.build_variant || 'rc' }} | |
| device_matrix: ${{ needs.read-device-matrix.outputs.android_matrix }} | |
| browserstack_app_url: ${{ needs.trigger-android-dual-versions.outputs.without-srp-browserstack-url || inputs.browserstack_app_url_android_onboarding }} | |
| app_version: ${{ needs.trigger-android-dual-versions.outputs.without-srp-version || 'Manual-Input' }} | |
| branch_name: ${{ needs.determine-branch-name.outputs.branch_name }} | |
| browserstack_build_name: ${{ needs.set-build-names.outputs.android_build_name }} | |
| secrets: inherit | |
| run-ios-onboarding-tests: | |
| name: Run iOS Onboarding Tests | |
| uses: ./.github/workflows/performance-test-runner.yml | |
| needs: | |
| [ | |
| read-device-matrix, | |
| trigger-ios-dual-versions, | |
| set-build-names, | |
| determine-branch-name, | |
| ] | |
| if: always() && !failure() && !cancelled() && (needs.trigger-ios-dual-versions.result == 'skipped' || needs.trigger-ios-dual-versions.result == 'success') && (inputs.browserstack_app_url_ios_onboarding != '' || needs.trigger-ios-dual-versions.outputs.without-srp-browserstack-url != '') | |
| with: | |
| platform: ios | |
| build_type: onboarding | |
| sentry_target: ${{ inputs.sentry_target || 'test' }} | |
| build_variant: ${{ inputs.build_variant || 'rc' }} | |
| device_matrix: ${{ needs.read-device-matrix.outputs.ios_matrix }} | |
| browserstack_app_url: ${{ needs.trigger-ios-dual-versions.outputs.without-srp-browserstack-url || inputs.browserstack_app_url_ios_onboarding }} | |
| app_version: ${{ needs.trigger-ios-dual-versions.outputs.without-srp-version || 'Manual-Input' }} | |
| branch_name: ${{ needs.determine-branch-name.outputs.branch_name }} | |
| browserstack_build_name: ${{ needs.set-build-names.outputs.ios_build_name }} | |
| secrets: inherit | |
| # ============================================================================= | |
| # PHASE 2: IMPORTED WALLET TESTS (Run After Onboarding Complete) | |
| # ============================================================================= | |
| wait-for-onboarding-completion: | |
| name: Wait for Onboarding Completion | |
| runs-on: ubuntu-latest | |
| needs: [run-android-onboarding-tests, run-ios-onboarding-tests] | |
| if: always() | |
| steps: | |
| - name: Wait for onboarding tests to complete | |
| run: | | |
| echo "Onboarding tests have completed (success or failure)" | |
| echo "Proceeding with imported wallet tests..." | |
| run-android-imported-wallet-tests: | |
| name: Run Android Imported Wallet Tests | |
| uses: ./.github/workflows/performance-test-runner.yml | |
| needs: | |
| [ | |
| read-device-matrix, | |
| trigger-android-dual-versions, | |
| wait-for-onboarding-completion, | |
| set-build-names, | |
| determine-branch-name, | |
| ] | |
| if: always() && !cancelled() && (needs.trigger-android-dual-versions.result == 'skipped' || needs.trigger-android-dual-versions.result == 'success') && (inputs.browserstack_app_url_android_imported_wallet != '' || needs.trigger-android-dual-versions.outputs.with-srp-browserstack-url != '') | |
| with: | |
| platform: android | |
| build_type: imported-wallet | |
| sentry_target: ${{ inputs.sentry_target || 'test' }} | |
| build_variant: ${{ inputs.build_variant || 'rc' }} | |
| device_matrix: ${{ needs.read-device-matrix.outputs.android_matrix }} | |
| browserstack_app_url: ${{ needs.trigger-android-dual-versions.outputs.with-srp-browserstack-url || inputs.browserstack_app_url_android_imported_wallet }} | |
| app_version: ${{ needs.trigger-android-dual-versions.outputs.with-srp-version || 'Manual-Input' }} | |
| branch_name: ${{ needs.determine-branch-name.outputs.branch_name }} | |
| browserstack_build_name: ${{ needs.set-build-names.outputs.android_build_name }} | |
| secrets: inherit | |
| run-ios-imported-wallet-tests: | |
| name: Run iOS Imported Wallet Tests | |
| uses: ./.github/workflows/performance-test-runner.yml | |
| needs: | |
| [ | |
| read-device-matrix, | |
| trigger-ios-dual-versions, | |
| wait-for-onboarding-completion, | |
| set-build-names, | |
| determine-branch-name, | |
| ] | |
| if: always() && !cancelled() && (needs.trigger-ios-dual-versions.result == 'skipped' || needs.trigger-ios-dual-versions.result == 'success') && (inputs.browserstack_app_url_ios_imported_wallet != '' || needs.trigger-ios-dual-versions.outputs.with-srp-browserstack-url != '') | |
| with: | |
| platform: ios | |
| build_type: imported-wallet | |
| sentry_target: ${{ inputs.sentry_target || 'test' }} | |
| build_variant: ${{ inputs.build_variant || 'rc' }} | |
| device_matrix: ${{ needs.read-device-matrix.outputs.ios_matrix }} | |
| browserstack_app_url: ${{ needs.trigger-ios-dual-versions.outputs.with-srp-browserstack-url || inputs.browserstack_app_url_ios_imported_wallet }} | |
| app_version: ${{ needs.trigger-ios-dual-versions.outputs.with-srp-version || 'Manual-Input' }} | |
| branch_name: ${{ needs.determine-branch-name.outputs.branch_name }} | |
| browserstack_build_name: ${{ needs.set-build-names.outputs.ios_build_name }} | |
| secrets: inherit | |
| fetch-rn-playground-apk-upload-to-browserstack: | |
| name: Fetch RN Playground APK and Upload to BrowserStack | |
| runs-on: ubuntu-latest | |
| needs: [wait-for-onboarding-completion] | |
| if: always() && !cancelled() | |
| outputs: | |
| browserstack-playground-url: ${{ steps.upload-playground.outputs.browserstack-url }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Fetch playground APK from GitHub Releases | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| RN_PLAYGROUND_APK_VERSION: ${{ vars.RN_PLAYGROUND_APK_VERSION || '' }} | |
| run: ./scripts/fetch-rn-playground-apk.sh --output ./tmp/rn-playground.apk | |
| - name: Upload playground APK to BrowserStack | |
| id: upload-playground | |
| run: | | |
| echo "Uploading RN Playground APK to BrowserStack..." | |
| RESPONSE=$(curl -u "$BROWSERSTACK_USERNAME:$BROWSERSTACK_ACCESS_KEY" \ | |
| -X POST "https://api-cloud.browserstack.com/app-automate/upload" \ | |
| -F "file=@./tmp/rn-playground.apk" \ | |
| -F "custom_id=RN-Playground-${{ github.run_id }}") | |
| APP_URL=$(echo "$RESPONSE" | jq -r '.app_url') | |
| if [ -z "$APP_URL" ] || [ "$APP_URL" = "null" ]; then | |
| echo "Error: Failed to upload playground APK to BrowserStack" | |
| echo "Response: $RESPONSE" | |
| exit 1 | |
| fi | |
| echo "browserstack-url=$APP_URL" >> "$GITHUB_OUTPUT" | |
| echo "Playground APK uploaded: $APP_URL" | |
| run-android-mm-connect-tests: | |
| name: Run Android MM-Connect Tests | |
| uses: ./.github/workflows/performance-test-runner.yml | |
| needs: | |
| [ | |
| read-device-matrix, | |
| trigger-android-dual-versions, | |
| fetch-rn-playground-apk-upload-to-browserstack, | |
| set-build-names, | |
| determine-branch-name, | |
| ] | |
| if: always() && !cancelled() && (needs.trigger-android-dual-versions.result == 'skipped' || needs.trigger-android-dual-versions.result == 'success') && (inputs.browserstack_app_url_android_imported_wallet != '' || needs.trigger-android-dual-versions.outputs.with-srp-browserstack-url != '') && needs.fetch-rn-playground-apk-upload-to-browserstack.result == 'success' | |
| with: | |
| platform: android | |
| build_type: mm-connect | |
| sentry_target: ${{ inputs.sentry_target || 'test' }} | |
| build_variant: ${{ inputs.build_variant || 'rc' }} | |
| device_matrix: ${{ needs.read-device-matrix.outputs.android_matrix }} | |
| browserstack_app_url: ${{ needs.trigger-android-dual-versions.outputs.with-srp-browserstack-url || inputs.browserstack_app_url_android_imported_wallet }} | |
| app_version: ${{ needs.trigger-android-dual-versions.outputs.with-srp-version || 'Manual-Input' }} | |
| branch_name: ${{ needs.determine-branch-name.outputs.branch_name }} | |
| browserstack_build_name: ${{ needs.set-build-names.outputs.android_build_name }} | |
| browserstack_playground_url: ${{ needs.fetch-rn-playground-apk-upload-to-browserstack.outputs.browserstack-playground-url }} | |
| secrets: inherit | |
| aggregate-results: | |
| name: Aggregate All Test Results | |
| runs-on: ubuntu-latest | |
| needs: | |
| [ | |
| run-android-imported-wallet-tests, | |
| run-android-onboarding-tests, | |
| run-android-mm-connect-tests, | |
| run-ios-imported-wallet-tests, | |
| run-ios-onboarding-tests, | |
| wait-for-onboarding-completion, | |
| determine-branch-name, | |
| ] | |
| if: always() | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Download All Test Results | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: '*-test-results-*' | |
| path: ./test-results | |
| merge-multiple: true | |
| - name: Run aggregation script | |
| env: | |
| BRANCH_NAME: ${{ needs.determine-branch-name.outputs.branch_name }} | |
| BUILD_VARIANT: ${{ inputs.build_variant || 'rc' }} | |
| run: | | |
| echo "Processing all test results..." | |
| echo "Running aggregation script..." | |
| node scripts/aggregate-performance-reports.mjs | |
| echo "Aggregation completed" | |
| - name: Upload Final Combined Results | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: aggregated-reports | |
| path: | | |
| tests/aggregated-reports/ | |
| if-no-files-found: ignore | |
| retention-days: 30 | |
| slack-notification: | |
| name: Send Slack Notification | |
| runs-on: ubuntu-latest | |
| needs: | |
| [ | |
| run-android-imported-wallet-tests, | |
| run-android-onboarding-tests, | |
| run-android-mm-connect-tests, | |
| run-ios-imported-wallet-tests, | |
| run-ios-onboarding-tests, | |
| aggregate-results, | |
| determine-branch-name, | |
| ] | |
| if: always() | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Download Aggregated Results | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: aggregated-reports | |
| path: ./aggregated-reports | |
| - name: Generate Test Summary | |
| id: summary | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| BRANCH_NAME: ${{ needs.determine-branch-name.outputs.branch_name }} | |
| run: | | |
| { | |
| echo "summary<<EOF" | |
| ./scripts/generate-slack-summary.sh aggregated-reports/summary.json | |
| echo "EOF" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Send Slack Notification | |
| uses: slackapi/slack-github-action@91efab103c0de0a537f72a35f6b8cda0ee76bf0a | |
| with: | |
| webhook: ${{ secrets.PERFORMANCE_E2E_SLACK_WEBHOOK_URL }} | |
| webhook-type: incoming-webhook | |
| payload: | | |
| { | |
| "text": "${{ steps.summary.outputs.summary }}" | |
| } |