Skip to content

Build Apps and Run Performance E2E Tests #8143

Build Apps and Run Performance E2E Tests

Build Apps and Run Performance E2E Tests #8143

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
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
performance_tags:
description: 'JSON array of performance tags (e.g. ["@PerformanceLogin","@PerformanceSwaps"]). Omit/blank = run all. "[]" = skip all (PR with no relevant changes).'
required: false
type: string
default: ''
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: 'BrowserStack build profile (e2e = build-e2e, works on feature branches; rc = build-rc, release branches only; exp = experimental)'
required: false
type: string
default: 'e2e'
performance_tags:
description: 'JSON array of performance tags (e.g. ["@PerformanceLogin","@PerformanceSwaps"]). Omit/blank = run all. "[]" = skip all (PR with no relevant changes).'
required: false
type: string
default: ''
performance_tags_reasoning:
description: 'Human-readable explanation of why performance tags were selected'
required: false
type: string
default: ''
pr_number:
description: 'Pull request number for posting results as a PR comment (workflow_call only)'
required: false
type: string
default: ''
reuse_main_builds:
description: >-
When true, skip fresh Android BrowserStack builds and reuse the latest
uploaded apps from main-branch CI instead.
required: false
type: boolean
default: false
permissions:
contents: write
id-token: write
actions: write
pull-requests: write
concurrency:
# Include build_variant so e2e, experimental (exp), and release/direct (rc) don't share the same
# queue — avoids "Build Apps" (direct/release) and e2e/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 || 'e2e' }}
# 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 }}
TEST_SRP_4: ${{ secrets.TEST_SRP_4 }}
E2E_PASSWORD: ${{ secrets.E2E_PASSWORD }}
DISABLE_VIDEO_DOWNLOAD: true
jobs:
determine-branch-name:
name: Determine Branch Name
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: $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 }}
android_mm_connect_matrix: ${{ steps.read-matrix.outputs.android_mm_connect_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 }}
PR_NUMBER: ${{ inputs.pr_number }}
run: |
FILE="tests/performance/device-matrix.json"
# PRs: run the AI-selected performance tests on Android low profile only.
# Pushes keep the previous low-device behavior to control cost and flakes.
# Schedule and workflow_dispatch use the full device matrix.
if [ -n "$PR_NUMBER" ]; then
ANDROID_MATRIX=$(jq '[.android_devices[] | select(.category == "low")]' "$FILE")
ANDROID_MM_CONNECT_MATRIX="$ANDROID_MATRIX"
IOS_MATRIX='[]'
echo "PR mode: Android low category devices only (PR: $PR_NUMBER, branch: $BRANCH_NAME)"
elif [ "${{ github.event_name }}" = "push" ]; then
ANDROID_MATRIX=$(jq '[.android_devices[] | select(.category == "low")]' "$FILE")
ANDROID_MM_CONNECT_MATRIX=$(jq '[.android_devices[] | select(.name | contains("Samsung"))]' "$FILE")
IOS_MATRIX=$(jq '[.ios_devices[] | select(.category == "low")]' "$FILE")
echo "Push mode: low category devices only (event: ${{ github.event_name }}, branch: $BRANCH_NAME)"
else
ANDROID_MATRIX=$(jq ".android_devices" "$FILE")
ANDROID_MM_CONNECT_MATRIX=$(jq '[.android_devices[] | select(.name | contains("Samsung"))]' "$FILE")
IOS_MATRIX=$(jq ".ios_devices" "$FILE")
echo "Full mode: All devices (event: ${{ github.event_name }}, branch: $BRANCH_NAME)"
fi
{
echo "android_matrix<<EOF"
echo "$ANDROID_MATRIX"
echo "EOF"
echo "android_mm_connect_matrix<<EOF"
echo "$ANDROID_MM_CONNECT_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"
echo "Selected for Android MM-Connect: $(echo "$ANDROID_MM_CONNECT_MATRIX" | jq length)"
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"
compute-test-selection:
name: Compute Performance Test Selection
runs-on: ubuntu-latest
outputs:
run_tests: ${{ steps.map.outputs.run_tests }}
grep_pattern: ${{ steps.map.outputs.grep_pattern }}
steps:
- name: Map performance tags to grep pattern
id: map
env:
PERF_REASONING: ${{ inputs.performance_tags_reasoning }}
run: |
TAGS='${{ inputs.performance_tags }}'
REASONING="$PERF_REASONING"
# No input (blank) → schedule/dispatch: run all tests with no grep filter
if [[ -z "$TAGS" ]]; then
echo "run_tests=true" >> "$GITHUB_OUTPUT"
echo "grep_pattern=" >> "$GITHUB_OUTPUT"
echo "▶️ Running all performance tests (schedule/dispatch mode — no tag filter)"
exit 0
fi
# '[]' → PR with no performance-relevant changes: skip all tests
if [[ "$TAGS" == "[]" ]]; then
echo "run_tests=false" >> "$GITHUB_OUTPUT"
echo "grep_pattern=" >> "$GITHUB_OUTPUT"
echo "⏭️ Skipping performance tests — no performance-relevant changes detected in this PR"
if [[ -n "$REASONING" ]]; then
echo " Reasoning: $REASONING"
fi
exit 0
fi
# Build OR-pattern for playwright --grep (e.g. "@PerformanceSwaps|@PerformanceLogin")
# Playwright matches against the full test name (describe + test title), so each tag
# selects only the individual tests that use it in their describe block.
GREP=$(echo "$TAGS" | jq -r 'join("|")')
echo "run_tests=true" >> "$GITHUB_OUTPUT"
echo "grep_pattern=$GREP" >> "$GITHUB_OUTPUT"
echo "⚡ Performance test selection:"
echo " Tags: $TAGS"
echo " Grep pattern: $GREP"
if [[ -n "$REASONING" ]]; then
echo " Reasoning: $REASONING"
fi
resolve-main-browserstack-urls:
name: Resolve main-branch BrowserStack Android apps
runs-on: ubuntu-latest
if: >-
inputs.reuse_main_builds &&
!inputs.browserstack_app_url_android_onboarding &&
!inputs.browserstack_app_url_android_imported_wallet
outputs:
found: ${{ steps.resolve.outputs.found }}
with-srp-browserstack-url: ${{ steps.resolve.outputs.with-srp-browserstack-url }}
without-srp-browserstack-url: ${{ steps.resolve.outputs.without-srp-browserstack-url }}
with-srp-version: ${{ steps.resolve.outputs.with-srp-version }}
without-srp-version: ${{ steps.resolve.outputs.without-srp-version }}
steps:
- name: Checkout
uses: actions/checkout@v6
with:
sparse-checkout: |
.github/scripts/resolve-main-browserstack-apps.cjs
.github/scripts/browserstack-app-validation.cjs
sparse-checkout-cone-mode: false
- name: Resolve latest main BrowserStack apps
id: resolve
run: node .github/scripts/resolve-main-browserstack-apps.cjs
env:
BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }}
BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }}
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, resolve-main-browserstack-urls]
# Build/upload when not reusing main, or when main reuse lookup found nothing.
if: >-
always() &&
!cancelled() &&
(needs.resolve-main-browserstack-urls.result == 'skipped' || needs.resolve-main-browserstack-urls.result == 'success') &&
(!inputs.browserstack_app_url_android_onboarding && !inputs.browserstack_app_url_android_imported_wallet) &&
(
!inputs.reuse_main_builds ||
needs.resolve-main-browserstack-urls.outputs.found != 'true'
)
with:
branch_name: ${{ needs.determine-branch-name.outputs.branch_name }}
build_variant: ${{ inputs.build_variant || 'e2e' }}
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: false # temporarily disabled — iOS tests not yet active
# 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 || 'e2e' }}
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,
resolve-main-browserstack-urls,
set-build-names,
determine-branch-name,
compute-test-selection,
]
if: >-
always() && !failure() && !cancelled() &&
needs.compute-test-selection.outputs.run_tests == 'true' &&
(
needs.trigger-android-dual-versions.result == 'skipped' ||
needs.trigger-android-dual-versions.result == 'success'
) &&
(
needs.resolve-main-browserstack-urls.result == 'skipped' ||
needs.resolve-main-browserstack-urls.result == 'success'
) &&
(
inputs.browserstack_app_url_android_onboarding != '' ||
needs.trigger-android-dual-versions.outputs.without-srp-browserstack-url != '' ||
needs.resolve-main-browserstack-urls.outputs.without-srp-browserstack-url != ''
)
with:
platform: android
build_type: onboarding
sentry_target: ${{ inputs.sentry_target || 'test' }}
build_variant: ${{ inputs.build_variant || 'e2e' }}
device_matrix: ${{ needs.read-device-matrix.outputs.android_matrix }}
browserstack_app_url: ${{ needs.resolve-main-browserstack-urls.outputs.without-srp-browserstack-url || needs.trigger-android-dual-versions.outputs.without-srp-browserstack-url || inputs.browserstack_app_url_android_onboarding }}
app_version: ${{ needs.resolve-main-browserstack-urls.outputs.without-srp-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 }}
grep_tags: ${{ needs.compute-test-selection.outputs.grep_pattern }}
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,
compute-test-selection,
]
if: always() && !failure() && !cancelled() && needs.compute-test-selection.outputs.run_tests == 'true' && (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 || 'e2e' }}
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 }}
grep_tags: ${{ needs.compute-test-selection.outputs.grep_pattern }}
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,
resolve-main-browserstack-urls,
wait-for-onboarding-completion,
set-build-names,
determine-branch-name,
compute-test-selection,
]
if: >-
always() && !cancelled() &&
needs.compute-test-selection.outputs.run_tests == 'true' &&
(
needs.trigger-android-dual-versions.result == 'skipped' ||
needs.trigger-android-dual-versions.result == 'success'
) &&
(
needs.resolve-main-browserstack-urls.result == 'skipped' ||
needs.resolve-main-browserstack-urls.result == 'success'
) &&
(
inputs.browserstack_app_url_android_imported_wallet != '' ||
needs.trigger-android-dual-versions.outputs.with-srp-browserstack-url != '' ||
needs.resolve-main-browserstack-urls.outputs.with-srp-browserstack-url != ''
)
with:
platform: android
build_type: imported-wallet
sentry_target: ${{ inputs.sentry_target || 'test' }}
build_variant: ${{ inputs.build_variant || 'e2e' }}
device_matrix: ${{ needs.read-device-matrix.outputs.android_matrix }}
browserstack_app_url: ${{ needs.resolve-main-browserstack-urls.outputs.with-srp-browserstack-url || needs.trigger-android-dual-versions.outputs.with-srp-browserstack-url || inputs.browserstack_app_url_android_imported_wallet }}
app_version: ${{ needs.resolve-main-browserstack-urls.outputs.with-srp-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 }}
grep_tags: ${{ needs.compute-test-selection.outputs.grep_pattern }}
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,
compute-test-selection,
]
if: always() && !cancelled() && needs.compute-test-selection.outputs.run_tests == 'true' && (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 || 'e2e' }}
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 }}
grep_tags: ${{ needs.compute-test-selection.outputs.grep_pattern }}
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: ./tests/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,
resolve-main-browserstack-urls,
fetch-rn-playground-apk-upload-to-browserstack,
set-build-names,
determine-branch-name,
compute-test-selection,
]
if: >-
always() && !cancelled() &&
needs.compute-test-selection.outputs.run_tests == 'true' &&
(
needs.trigger-android-dual-versions.result == 'skipped' ||
needs.trigger-android-dual-versions.result == 'success'
) &&
(
needs.resolve-main-browserstack-urls.result == 'skipped' ||
needs.resolve-main-browserstack-urls.result == 'success'
) &&
(
inputs.browserstack_app_url_android_imported_wallet != '' ||
needs.trigger-android-dual-versions.outputs.with-srp-browserstack-url != '' ||
needs.resolve-main-browserstack-urls.outputs.with-srp-browserstack-url != ''
)
with:
platform: android
build_type: mm-connect
sentry_target: ${{ inputs.sentry_target || 'test' }}
build_variant: ${{ inputs.build_variant || 'e2e' }}
device_matrix: ${{ needs.read-device-matrix.outputs.android_mm_connect_matrix }}
browserstack_app_url: ${{ needs.resolve-main-browserstack-urls.outputs.with-srp-browserstack-url || needs.trigger-android-dual-versions.outputs.with-srp-browserstack-url || inputs.browserstack_app_url_android_imported_wallet }}
app_version: ${{ needs.resolve-main-browserstack-urls.outputs.with-srp-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 }}
grep_tags: ${{ needs.compute-test-selection.outputs.grep_pattern }}
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 || 'e2e' }}
run: |
echo "Processing all test results..."
echo "Running aggregation script..."
node tests/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
post-pr-comment:
name: Post Performance Results to PR
runs-on: ubuntu-latest
needs: [aggregate-results, determine-branch-name]
# Only runs when triggered from a PR via workflow_call with a pr_number
if: always() && !cancelled() && inputs.pr_number != ''
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Download Aggregated Results
uses: actions/download-artifact@v4
with:
name: aggregated-reports
path: ./aggregated-reports
continue-on-error: true
- name: Generate PR comment markdown
env:
GITHUB_RUN_ID: ${{ github.run_id }}
GITHUB_REPOSITORY: ${{ github.repository }}
run: |
node tests/scripts/generate-performance-pr-comment.mjs \
aggregated-reports/summary.json \
performance-pr-comment.md
- name: Delete previous performance results comments
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
ALL_COMMENT_IDS=$(gh api "repos/${{ github.repository }}/issues/${{ inputs.pr_number }}/comments" \
--jq '.[] | select(.body | contains("<!-- perf-test-results -->")) | .id')
if [ -n "$ALL_COMMENT_IDS" ]; then
echo "$ALL_COMMENT_IDS" | while read -r COMMENT_ID; do
if [ -n "$COMMENT_ID" ]; then
gh api "repos/${{ github.repository }}/issues/comments/$COMMENT_ID" \
--method DELETE > /dev/null 2>&1 || true
fi
done
echo "Deleted previous performance results comments"
fi
- name: Post PR comment
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
MARKER="<!-- perf-test-results -->"
COMMENT=$(cat performance-pr-comment.md 2>/dev/null || echo "⚠️ Performance test results are not available.")
FULL_COMMENT="${COMMENT}"$'\n'"${MARKER}"
gh pr comment "${{ inputs.pr_number }}" --repo "${{ github.repository }}" --body "$FULL_COMMENT"
echo "Performance results posted to PR #${{ inputs.pr_number }}"
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"
./tests/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 }}"
}