@Mobile E2E • Manual • iOS & Android • develop • abdurrahman-ledger #3883
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: "[Mobile] E2E Only - Scheduled/Manual" | |
| run-name: > | |
| @Mobile E2E • ${{ github.event_name == 'workflow_dispatch' && 'Manual' || github.event_name == 'schedule' && 'Scheduled' || github.event_name }} • ${{ inputs.tests_type || 'All' }} • ${{ inputs.ref || github.ref_name }} • ${{ github.actor }} | |
| on: | |
| schedule: | |
| - cron: "0 3 * * 1-5" | |
| workflow_dispatch: | |
| inputs: | |
| ref: | |
| description: "The branch which triggered this workflow" | |
| required: false | |
| test_filter: | |
| description: Filter pattern(s) to execute only selected test suites by test name, file path or tag, separated by ',' or '|' (e.g. "Accounts @smoke", "@bitcoin,@family-evm" or "addAccount deeplinks") | |
| required: false | |
| type: string | |
| tests_type: | |
| description: "Which tests to run" | |
| required: true | |
| type: choice | |
| default: "iOS & Android" | |
| options: | |
| - "Android Only" | |
| - "iOS Only" | |
| - "iOS & Android" | |
| speculos_device: | |
| description: Speculos device to use | |
| required: true | |
| type: choice | |
| options: | |
| - nanoS | |
| - nanoSP | |
| - nanoX | |
| - stax | |
| - flex | |
| - nanoGen5 | |
| default: nanoX | |
| production_firebase: | |
| description: "Target Firebase Production env" | |
| required: false | |
| type: boolean | |
| default: false | |
| enable_broadcast: | |
| description: "Enable transaction broadcast" | |
| required: false | |
| type: boolean | |
| default: false | |
| export_to_xray: | |
| description: "Send results to Xray" | |
| required: false | |
| type: boolean | |
| default: false | |
| test_execution_android: | |
| description: "Xray execution key for Android (optional)" | |
| required: false | |
| type: string | |
| test_execution_ios: | |
| description: "Xray execution key for iOS (optional)" | |
| required: false | |
| type: string | |
| smoke_tests: | |
| description: "Run smoke tests" | |
| required: false | |
| type: boolean | |
| default: false | |
| workflow_call: | |
| inputs: | |
| ref: | |
| description: "The branch which triggered this workflow" | |
| required: false | |
| type: string | |
| test_filter: | |
| description: Filter pattern(s) to execute only selected test suites by test name, file path or tag, separated by ',' or '|' (e.g. "Accounts @smoke", "@bitcoin,@family-evm" or "addAccount deeplinks") | |
| required: false | |
| type: string | |
| tests_type: | |
| description: "Which tests to run" | |
| required: true | |
| type: string | |
| default: "iOS & Android" | |
| speculos_device: | |
| description: "Speculos device to use" | |
| required: true | |
| type: string | |
| production_firebase: | |
| description: "Target Firebase Production env" | |
| required: false | |
| type: boolean | |
| default: false | |
| enable_broadcast: | |
| description: "Enable transaction broadcast" | |
| required: false | |
| type: boolean | |
| default: false | |
| smoke_tests: | |
| description: "Run smoke tests" | |
| required: false | |
| type: boolean | |
| default: false | |
| permissions: | |
| id-token: write | |
| contents: read | |
| env: | |
| cache-bucket: ll-gha-s3-cache | |
| ANDROID_APK_PATH: apps/ledger-live-mobile/android/app/build/outputs/apk/detox${{ inputs.production_firebase == true && 'PreRelease' || '' }}/app-x86_64-detox${{ inputs.production_firebase == true && 'PreRelease' || '' }}.apk | |
| ANDROID_JSBUNDLE_PATH: apps/ledger-live-mobile/main.jsbundle | |
| ANDROID_TESTBINARY_PATH: apps/ledger-live-mobile/android/app/build/outputs/apk/androidTest/detox${{ inputs.production_firebase == true && 'PreRelease' || '' }}/app-detox${{ inputs.production_firebase == true && 'PreRelease' || '' }}-androidTest.apk | |
| IOS_JSBUNDLE_PATH: apps/ledger-live-mobile/ios/build/Build/Products/Release-iphonesimulator/ledgerlivemobile.app/main.jsbundle | |
| IOS_NATIVE_PATH: apps/ledger-live-mobile/ios/build/Build/Products/Release-iphonesimulator | |
| SPECULOS_IMAGE_TAG: ghcr.io/ledgerhq/speculos:${{ inputs.speculos_device == 'nanoS' && '1be65b91a8e0691866f880fd437ac05fce78af9d' || 'latest' }} | |
| COINAPPS: ${{ github.workspace }}/coin-apps | |
| SPECULOS_DEVICE: ${{ inputs.speculos_device || 'nanoX' }} | |
| # Android AVD configuration | |
| AVD_API: 35 | |
| AVD_ARCH: x86_64 | |
| AVD_PROFILE: pixel_7_pro | |
| AVD_TARGET: google_apis | |
| AVD_NAME: "Android_Emulator" | |
| AVD_CORES: 4 | |
| AVD_RAM_SIZE: 5120M | |
| AVD_HEAP_SIZE: 1023M | |
| AVD_OPTIONS: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none | |
| jobs: | |
| determine-builds: | |
| name: "Determine Builds & Generate Shards" | |
| runs-on: ledger-live-medium | |
| outputs: | |
| matrix_ios: ${{ steps.generate-shards.outputs.matrix_ios }} | |
| matrix_android: ${{ steps.generate-shards.outputs.matrix_android }} | |
| test_files_for_sharding: ${{ steps.generate-shards.outputs.test_files_for_sharding }} | |
| test_filter: ${{ steps.test-filter.outputs.filter }} | |
| ios_timeout: ${{ steps.generate-shards.outputs.ios_timeout }} | |
| android_timeout: ${{ steps.generate-shards.outputs.android_timeout }} | |
| ios_native_exists: ${{ steps.check-ios-native.outputs.cache-hit }} | |
| ios_js_exists: ${{ steps.check-ios-js.outputs.cache-hit }} | |
| android_native_exists: ${{ steps.check-android-native.outputs.cache-hit }} | |
| android_js_exists: ${{ steps.check-android-js.outputs.cache-hit }} | |
| ios_native_key: ${{ steps.cache-keys.outputs.ios_native_key }} | |
| ios_js_key: ${{ steps.cache-keys.outputs.ios_js_key }} | |
| android_native_key: ${{ steps.cache-keys.outputs.android_native_key }} | |
| android_js_key: ${{ steps.cache-keys.outputs.android_js_key }} | |
| android_timing_cache_key: ${{ steps.cache-keys.outputs.android_timing_cache_key }} | |
| ios_timing_cache_key: ${{ steps.cache-keys.outputs.ios_timing_cache_key }} | |
| avd_cache_key: ${{ steps.cache-keys.outputs.avd_cache_key }} | |
| avd_exists: ${{ steps.check-avd.outputs.cache-hit }} | |
| steps: | |
| - name: generate token | |
| id: generate-token | |
| uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # 2.2.1 | |
| with: | |
| app-id: ${{ secrets.GH_BOT_APP_ID }} | |
| private-key: ${{ secrets.GH_BOT_PRIVATE_KEY }} | |
| repositories: | | |
| coin-apps | |
| ledger-live | |
| - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 | |
| with: | |
| ref: ${{ inputs.ref || github.sha }} | |
| repository: LedgerHQ/ledger-live | |
| persist-credentials: false | |
| sparse-checkout: | | |
| apps/ledger-live-mobile | |
| e2e/mobile | |
| token: ${{ steps.generate-token.outputs.token }} | |
| - name: Write summary | |
| run: | | |
| FILTER_PATTERN="${{ inputs.test_filter || '' }}" | |
| DEVICE="${{ inputs.speculos_device || 'nanoX' }}" | |
| BUILD_TYPE="${{ inputs.production_firebase && 'production' || 'testing' }}" | |
| BROADCAST_ENABLED="${{ inputs.enable_broadcast }}" | |
| SMOKE_TESTS="${{ inputs.smoke_tests }}" | |
| TEST_EXECUTION_ANDROID="${{ inputs.test_execution_android || '(not provided)' }}" | |
| TEST_EXECUTION_IOS="${{ inputs.test_execution_ios || '(not provided)' }}" | |
| if [ -z "$FILTER_PATTERN" ]; then | |
| FILTER_PATTERN="(none)" | |
| fi | |
| { | |
| echo "## Workflow Context" | |
| echo "" | |
| echo "- **Workflow source branch:** ${{ github.ref_name }}" | |
| echo "- **Triggered branch:** ${{ inputs.ref || github.ref_name }}" | |
| echo "- **Commit SHA:** ${{ github.sha }}" | |
| echo "- **Device selected:** $DEVICE" | |
| echo "- **Filtered pattern:** $FILTER_PATTERN" | |
| echo "- **Smoke tests:** $SMOKE_TESTS" | |
| echo "- **Test Execution ticket ID (Android):** $TEST_EXECUTION_ANDROID" | |
| echo "- **Test Execution ticket ID (iOS):** $TEST_EXECUTION_IOS" | |
| echo "- **Firebase env to target:** $BUILD_TYPE" | |
| echo "- **Broadcast enabled:** $BROADCAST_ENABLED" | |
| } >> $GITHUB_STEP_SUMMARY | |
| - name: Configure AWS credentials | |
| uses: aws-actions/configure-aws-credentials@v4 | |
| id: aws | |
| with: | |
| role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID_PROD }}:role/${{ secrets.AWS_CACHE_ROLE_NAME }} | |
| aws-region: ${{ secrets.AWS_CACHE_REGION }} | |
| - name: Determine cache keys | |
| id: cache-keys | |
| run: | | |
| echo "ios_native_key=longterm-${{ hashFiles('apps/ledger-live-mobile/ios') }}-detox-native-ios-${{ inputs.production_firebase == true && 'prod-2' || '3' }}" >> $GITHUB_OUTPUT | |
| echo "android_native_key=longterm-${{ hashFiles('apps/ledger-live-mobile/android') }}-detox-native-android-${{ inputs.production_firebase == true && 'prod-2' || '3' }}" >> $GITHUB_OUTPUT | |
| echo "ios_js_key=${{ inputs.ref || github.sha }}-detox-js-ios" >> $GITHUB_OUTPUT | |
| echo "android_js_key=${{ inputs.ref || github.sha }}-detox-js-android" >> $GITHUB_OUTPUT | |
| echo "android_timing_cache_key=android-e2e-timing-${{ hashFiles('e2e/mobile/specs') }}-2" >> $GITHUB_OUTPUT | |
| echo "ios_timing_cache_key=ios-e2e-timing-${{ hashFiles('e2e/mobile/specs') }}-2" >> $GITHUB_OUTPUT | |
| echo "avd_cache_key=${{ runner.os }}-detox-avd-${{ env.AVD_NAME }}_4-${{ env.AVD_PROFILE }}-${{ env.AVD_TARGET }}-${{ env.AVD_API }}-${{ env.AVD_ARCH }}-${{ env.AVD_CORES }}-${{ env.AVD_RAM_SIZE }}-${{ env.AVD_HEAP_SIZE }}-r2-v8" >> $GITHUB_OUTPUT | |
| - name: Prepare test filter | |
| id: test-filter | |
| run: | | |
| BASE_FILTER="${{ inputs.test_filter }}" | |
| if [ -n "$BASE_FILTER" ]; then | |
| BASE_FILTER="${BASE_FILTER//,/|}" | |
| fi | |
| if [ "${{ inputs.smoke_tests }}" = "true" ]; then | |
| if [ -n "$BASE_FILTER" ]; then | |
| echo "filter=@smoke $BASE_FILTER" >> $GITHUB_OUTPUT | |
| else | |
| echo "filter=@smoke" >> $GITHUB_OUTPUT | |
| fi | |
| else | |
| echo "filter=$BASE_FILTER" >> $GITHUB_OUTPUT | |
| fi | |
| shell: bash | |
| - name: Generate shards matrix and test files | |
| id: generate-shards | |
| uses: LedgerHQ/ledger-live/tools/actions/composites/generate-shards-matrix@develop | |
| with: | |
| test_directory: e2e/mobile | |
| test_filter: ${{ steps.test-filter.outputs.filter }} | |
| event_name: ${{ github.event_name }} | |
| ref: ${{ inputs.ref || github.ref_name }} | |
| ios_timing_cache_key: ${{ steps.cache-keys.outputs.ios_timing_cache_key }} | |
| android_timing_cache_key: ${{ steps.cache-keys.outputs.android_timing_cache_key }} | |
| aws_access_key_id: ${{ env.AWS_ACCESS_KEY_ID }} | |
| aws_secret_access_key: ${{ env.AWS_SECRET_ACCESS_KEY }} | |
| aws_session_token: ${{ env.AWS_SESSION_TOKEN }} | |
| cache_bucket: ${{ env.cache-bucket }} | |
| aws_region: ${{ secrets.AWS_CACHE_REGION }} | |
| - name: Check if iOS Native Build exists already | |
| id: check-ios-native | |
| uses: tespkg/actions-cache/restore@v1.9.0 | |
| with: | |
| path: apps/ledger-live-mobile/ios/build/Build/Products/Release-iphonesimulator | |
| key: ${{ steps.cache-keys.outputs.ios_native_key }} | |
| accessKey: ${{ env.AWS_ACCESS_KEY_ID }} | |
| secretKey: ${{ env.AWS_SECRET_ACCESS_KEY }} | |
| sessionToken: ${{ env.AWS_SESSION_TOKEN}} | |
| bucket: ${{ env.cache-bucket }} | |
| region: ${{ secrets.AWS_CACHE_REGION }} | |
| use-fallback: false | |
| lookup-only: true | |
| - name: Check if Android Native Build exists already | |
| id: check-android-native | |
| uses: tespkg/actions-cache/restore@v1.9.0 | |
| with: | |
| path: apps/ledger-live-mobile/android/app/build/outputs/apk/detox/app-x86_64-detox.apk | |
| key: ${{ steps.cache-keys.outputs.android_native_key }} | |
| accessKey: ${{ env.AWS_ACCESS_KEY_ID }} | |
| secretKey: ${{ env.AWS_SECRET_ACCESS_KEY }} | |
| sessionToken: ${{ env.AWS_SESSION_TOKEN}} | |
| bucket: ${{ env.cache-bucket }} | |
| region: ${{ secrets.AWS_CACHE_REGION }} | |
| use-fallback: false | |
| lookup-only: true | |
| - name: Check if iOS JS Build exists already | |
| id: check-ios-js | |
| uses: tespkg/actions-cache/restore@v1.9.0 | |
| with: | |
| path: apps/ledger-live-mobile/main.jsbundle | |
| key: ${{ steps.cache-keys.outputs.ios_js_key }} | |
| accessKey: ${{ env.AWS_ACCESS_KEY_ID }} | |
| secretKey: ${{ env.AWS_SECRET_ACCESS_KEY }} | |
| sessionToken: ${{ env.AWS_SESSION_TOKEN}} | |
| bucket: ${{ env.cache-bucket }} | |
| region: ${{ secrets.AWS_CACHE_REGION }} | |
| use-fallback: false | |
| lookup-only: true | |
| - name: Check if Android JS Build exists already | |
| id: check-android-js | |
| uses: tespkg/actions-cache/restore@v1.9.0 | |
| with: | |
| path: apps/ledger-live-mobile/main.jsbundle | |
| key: ${{ steps.cache-keys.outputs.android_js_key }} | |
| accessKey: ${{ env.AWS_ACCESS_KEY_ID }} | |
| secretKey: ${{ env.AWS_SECRET_ACCESS_KEY }} | |
| sessionToken: ${{ env.AWS_SESSION_TOKEN}} | |
| bucket: ${{ env.cache-bucket }} | |
| region: ${{ secrets.AWS_CACHE_REGION }} | |
| use-fallback: false | |
| lookup-only: true | |
| - name: Check if Android AVD cache exists | |
| id: check-avd | |
| if: ${{ inputs.tests_type != 'iOS Only' }} | |
| uses: LedgerHQ/ledger-live/tools/actions/composites/cache/exists@develop | |
| with: | |
| key: ${{ steps.cache-keys.outputs.avd_cache_key }} | |
| accessKey: ${{ secrets.R2_ACCESS_KEY_ID }} | |
| secretKey: ${{ secrets.R2_SECRET_ACCESS_KEY }} | |
| bucket: ledger-live-cache | |
| endpoint: ${{ secrets.R2_ENDPOINT }} | |
| region: auto | |
| build-ios: | |
| name: "iOS Build" | |
| needs: [determine-builds] | |
| if: ${{ inputs.tests_type != 'Android Only' && needs.determine-builds.outputs.test_files_for_sharding != '' }} | |
| uses: LedgerHQ/ledger-live/.github/workflows/test-mobile-build-ios-reusable.yml@develop | |
| with: | |
| ref: ${{ inputs.ref || github.sha }} | |
| macos-specificity-runner-label: "general-pool" | |
| disable-turbo-cache: false | |
| build-ios-js: ${{ needs.determine-builds.outputs.ios_js_exists == 'false' }} | |
| build-ios-native: ${{ needs.determine-builds.outputs.ios_native_exists == 'false' }} | |
| ios-native-cache-key: ${{ needs.determine-builds.outputs.ios_native_key }} | |
| ios-js-cache-key: ${{ needs.determine-builds.outputs.ios_js_key }} | |
| production_firebase: ${{ inputs.production_firebase || false }} | |
| secrets: inherit | |
| build-android: | |
| name: "Android Build" | |
| needs: [determine-builds] | |
| if: ${{ inputs.tests_type != 'iOS Only' && needs.determine-builds.outputs.test_files_for_sharding != '' }} | |
| uses: LedgerHQ/ledger-live/.github/workflows/test-mobile-build-android-reusable.yml@develop | |
| with: | |
| ref: ${{ inputs.ref || github.sha }} | |
| disable-turbo-cache: false | |
| build-android-js: ${{ needs.determine-builds.outputs.android_js_exists == 'false' }} | |
| build-android-native: ${{ needs.determine-builds.outputs.android_native_exists == 'false' }} | |
| android-native-cache-key: ${{ needs.determine-builds.outputs.android_native_key }} | |
| android-js-cache-key: ${{ needs.determine-builds.outputs.android_js_key }} | |
| production_firebase: ${{ inputs.production_firebase || false }} | |
| secrets: inherit | |
| setup-android-avd: | |
| name: "Setup Android AVD" | |
| needs: [determine-builds] | |
| if: ${{ inputs.tests_type != 'iOS Only' && needs.determine-builds.outputs.test_files_for_sharding != '' && needs.determine-builds.outputs.avd_exists != 'true' }} | |
| runs-on: [ledger-live-linux-e2e-8CPU-32RAM] | |
| steps: | |
| - name: Setup Android AVDs | |
| uses: LedgerHQ/ledger-live/tools/actions/composites/setup-android-avd@develop | |
| with: | |
| ref: ${{ inputs.ref || github.sha }} | |
| avd_api: ${{ env.AVD_API }} | |
| avd_arch: ${{ env.AVD_ARCH }} | |
| avd_profile: ${{ env.AVD_PROFILE }} | |
| avd_target: ${{ env.AVD_TARGET }} | |
| avd_name: ${{ env.AVD_NAME }} | |
| avd_cores: ${{ env.AVD_CORES }} | |
| avd_ram_size: ${{ env.AVD_RAM_SIZE }} | |
| avd_heap_size: ${{ env.AVD_HEAP_SIZE }} | |
| avd_options: ${{ env.AVD_OPTIONS }} | |
| cache_key: ${{ needs.determine-builds.outputs.avd_cache_key }} | |
| r2_access_key: ${{ secrets.R2_ACCESS_KEY_ID }} | |
| r2_secret_key: ${{ secrets.R2_SECRET_ACCESS_KEY }} | |
| r2_endpoint: ${{ secrets.R2_ENDPOINT }} | |
| detox-tests-ios: | |
| name: iOS E2E Tests (${{ matrix.shard }}, ${{ matrix.total }}) | |
| needs: [build-ios, determine-builds] | |
| if: ${{ inputs.tests_type != 'Android Only' && needs.determine-builds.outputs.test_files_for_sharding != '' }} | |
| runs-on: | |
| [ | |
| "${{ github.event_name == 'schedule' && 'performance-pool' || 'general-pool' }}", | |
| macOS, | |
| ARM64, | |
| ] | |
| env: | |
| NODE_OPTIONS: "--max-old-space-size=8192" | |
| LANG: en_US.UTF-8 | |
| LANGUAGE: en_US.UTF-8 | |
| LC_ALL: en_US.UTF-8 | |
| outputs: | |
| status: ${{ steps.run-ios.outcome }} | |
| artifact: ${{ steps.test-artifacts.outputs.artifact-id }} | |
| status_1: ${{ steps.set-output.outputs.status_1 }} | |
| status_2: ${{ steps.set-output.outputs.status_2 }} | |
| status_3: ${{ steps.set-output.outputs.status_3 }} | |
| status_4: ${{ steps.set-output.outputs.status_4 }} | |
| status_5: ${{ steps.set-output.outputs.status_5 }} | |
| status_6: ${{ steps.set-output.outputs.status_6 }} | |
| status_7: ${{ steps.set-output.outputs.status_7 }} | |
| status_8: ${{ steps.set-output.outputs.status_8 }} | |
| status_9: ${{ steps.set-output.outputs.status_9 }} | |
| status_10: ${{ steps.set-output.outputs.status_10 }} | |
| status_11: ${{ steps.set-output.outputs.status_11 }} | |
| status_12: ${{ steps.set-output.outputs.status_12 }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: ${{ fromJSON(needs.determine-builds.outputs.matrix_ios) }} | |
| steps: | |
| - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 | |
| with: | |
| ref: ${{ inputs.ref || github.sha }} | |
| repository: LedgerHQ/ledger-live | |
| persist-credentials: false | |
| - name: Configure AWS credentials | |
| uses: aws-actions/configure-aws-credentials@v4 | |
| id: aws | |
| with: | |
| role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID_PROD }}:role/${{ secrets.AWS_CACHE_ROLE_NAME }} | |
| aws-region: ${{ secrets.AWS_CACHE_REGION }} | |
| - name: Set shard test files from pre-computed matrix | |
| run: | | |
| echo "SHARD_TEST_FILES<<EOF" >> $GITHUB_ENV | |
| echo "${{ matrix.files }}" >> $GITHUB_ENV | |
| echo "EOF" >> $GITHUB_ENV | |
| echo "Using pre-computed shard ${{ matrix.shard }}/${{ matrix.total }} with $(echo '${{ matrix.files }}' | wc -w) test files" | |
| - name: Install scrcpy | |
| run: | | |
| brew install scrcpy | |
| - name: Boot iOS Simulator in Background | |
| run: | | |
| LOG_FILE="e2e/mobile/artifacts/simulator-boot.log" | |
| mkdir -p e2e/mobile/artifacts | |
| nohup bash -c ' | |
| echo "🔧 Creating iOS Simulator 2 (if not exists)" | |
| xcrun simctl list devices | grep -q "iOS Simulator 2" || xcrun simctl create "iOS Simulator 2" "iPhone 15" | |
| echo "🔧 Creating iOS Simulator 3 (if not exists)" | |
| xcrun simctl list devices | grep -q "iOS Simulator 3" || xcrun simctl create "iOS Simulator 3" "iPhone 15" | |
| echo "🛫 Booting simulator..." | |
| xcrun simctl boot "iOS Simulator" || true | |
| xcrun simctl bootstatus "iOS Simulator" | |
| echo "✅ Simulator is now fully booted" | |
| xcrun simctl boot "iOS Simulator 2" || true | |
| xcrun simctl bootstatus "iOS Simulator 2" | |
| echo "✅ Simulator 2 is now fully booted" | |
| xcrun simctl boot "iOS Simulator 3" || true | |
| xcrun simctl bootstatus "iOS Simulator 3" | |
| echo "✅ Simulator 3 is now fully booted" | |
| ' > "$LOG_FILE" 2>&1 & | |
| echo "ℹ️ Simulator boot started in background." | |
| shell: bash | |
| - name: setup caches | |
| id: setup-caches | |
| uses: LedgerHQ/ledger-live/tools/actions/composites/setup-caches@develop | |
| with: | |
| skip-pod-cache: "true" | |
| skip-turbo-cache: "false" | |
| skip-pnpm-cache: "true" | |
| accountId: ${{ secrets.AWS_ACCOUNT_ID_PROD }} | |
| roleName: ${{ secrets.AWS_CACHE_ROLE_NAME }} | |
| region: ${{ secrets.AWS_CACHE_REGION }} | |
| turbo-server-token: ${{ secrets.TURBOREPO_SERVER_TOKEN }} | |
| - uses: nick-fields/retry@v3 | |
| name: install dependencies | |
| id: install-dependencies | |
| with: | |
| max_attempts: 2 | |
| timeout_minutes: 15 | |
| command: pnpm i --filter="live-mobile..." --filter="ledger-live" --filter="ledger-live-mobile-e2e-tests" --filter="@ledgerhq/dummy-*-app..." --filter="live-cli*..." --no-frozen-lockfile --unsafe-perm --ignore-scripts | |
| new_command_on_retry: rm -rf ~/.cocoapods/ && pnpm clean && pnpm i --filter="live-mobile..." --filter="ledger-live" --filter="ledger-live-mobile-e2e-tests" --filter="@ledgerhq/dummy-*-app..." --filter="live-cli*..." --no-frozen-lockfile --unsafe-perm | |
| - name: Detox Post Install | |
| run: node apps/ledger-live-mobile/node_modules/detox/scripts/postinstall.js | |
| - name: Download Native Build | |
| uses: LedgerHQ/ledger-live/tools/actions/composites/cache/download@develop | |
| with: | |
| endpoint: ${{ secrets.S3_DIRECTCONNECT_ENDPOINT }} | |
| key: ${{ needs.determine-builds.outputs.ios_native_key }} | |
| accessKey: ${{ env.AWS_ACCESS_KEY_ID }} | |
| secretKey: ${{ env.AWS_SECRET_ACCESS_KEY }} | |
| sessionToken: ${{ env.AWS_SESSION_TOKEN}} | |
| bucket: ${{ env.cache-bucket }} | |
| region: ${{ secrets.AWS_CACHE_REGION }} | |
| - name: Download JS Build | |
| uses: LedgerHQ/ledger-live/tools/actions/composites/cache/download@develop | |
| with: | |
| endpoint: ${{ secrets.S3_DIRECTCONNECT_ENDPOINT }} | |
| key: ${{ needs.determine-builds.outputs.ios_js_key }} | |
| accessKey: ${{ env.AWS_ACCESS_KEY_ID }} | |
| secretKey: ${{ env.AWS_SECRET_ACCESS_KEY }} | |
| sessionToken: ${{ env.AWS_SESSION_TOKEN}} | |
| bucket: ${{ env.cache-bucket }} | |
| region: ${{ secrets.AWS_CACHE_REGION }} | |
| - name: Copy JS build | |
| run: | | |
| cp apps/ledger-live-mobile/main.jsbundle ${{ env.IOS_JSBUNDLE_PATH }} | |
| cp apps/ledger-live-mobile/main.jsbundle ${{ env.IOS_NATIVE_PATH }}/main.jsbundle | |
| - name: Build dependencies | |
| uses: LedgerHQ/ledger-live/tools/actions/composites/turbo-step@develop | |
| with: | |
| command: pnpm build:llm:deps | |
| turbo_server_token: ${{ secrets.TURBOREPO_SERVER_TOKEN }} | |
| turbo_port: ${{ steps.setup-caches.outputs.port }} | |
| disable_cache: false | |
| - name: Build CLI | |
| uses: LedgerHQ/ledger-live/tools/actions/composites/turbo-step@develop | |
| with: | |
| command: pnpm build:cli | |
| turbo_server_token: ${{ secrets.TURBOREPO_SERVER_TOKEN }} | |
| turbo_port: ${{ steps.setup-caches.outputs.port }} | |
| disable_cache: false | |
| - name: Setup Speculos image and Coin Apps | |
| id: setup-speculos | |
| uses: LedgerHQ/ledger-live/tools/actions/composites/setup-speculos_image@develop | |
| with: | |
| coinapps_path: ${{ env.COINAPPS }} | |
| bot_id: ${{ secrets.GH_BOT_APP_ID }} | |
| bot_key: ${{ secrets.GH_BOT_PRIVATE_KEY }} | |
| - name: Set DISABLE_TRANSACTION_BROADCAST | |
| uses: LedgerHQ/ledger-live/tools/actions/composites/setup-e2e-env@develop | |
| with: | |
| enable_broadcast: ${{ inputs.enable_broadcast }} | |
| build_type: ${{ inputs.production_firebase == 'true' && 'js' || 'testing' }} | |
| - name: Run iOS Detox shard ${{ matrix.shard }}/${{ matrix.total }} | |
| id: run-ios | |
| timeout-minutes: ${{ fromJSON(needs.determine-builds.outputs.ios_timeout) }} | |
| run: | | |
| pnpm mobile e2e:ci -p ios -t \ | |
| --e2e \ | |
| $([[ "$PRODUCTION" == "true" ]] && printf %s '--production') \ | |
| $SHARD_TEST_FILES \ | |
| --outputFile=artifacts/e2e-test-results-ios-shard-${{ matrix.shard }}.json | |
| env: | |
| SEED: ${{ secrets.SEED_QAA_B2C }} | |
| GITHUB_TOKEN: ${{ secrets.LL_SPECULOS_CI }} | |
| INPUT_E2E: "true" | |
| REMOTE_SPECULOS: "true" | |
| PRODUCTION: ${{ inputs.production_firebase }} | |
| INPUTS_TEST_FILTER: ${{ needs.determine-builds.outputs.test_filter }} | |
| SWAP_API_BASE: ${{ env.SWAP_API_BASE }} | |
| SHARD_INDEX: ${{ matrix.shard }} | |
| - name: Upload iOS artifacts | |
| if: (!cancelled() || steps.run-ios.outcome == 'cancelled') | |
| uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 | |
| id: "test-artifacts" | |
| with: | |
| name: ios-test-artifacts-${{ matrix.shard }} | |
| path: e2e/mobile/artifacts | |
| - name: Setup iOS Shard timing artifacts | |
| if: (!cancelled() || steps.run-ios.outcome == 'cancelled') | |
| uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 | |
| with: | |
| path: e2e/mobile/artifacts/e2e-test-results-ios-shard-${{ matrix.shard }}.json | |
| name: "${{ needs.determine-builds.outputs.ios_timing_cache_key }}-${{ matrix.shard }}" | |
| - name: Set job output based on detox result | |
| id: set-output | |
| if: ${{ !cancelled() }} | |
| run: | | |
| echo "status_${{ matrix.shard }}=${{ steps.run-ios.outcome }}" >> $GITHUB_OUTPUT | |
| - name: Delete iOS Simulators | |
| if: always() | |
| run: | | |
| xcrun simctl delete "iOS Simulator 2" | |
| xcrun simctl delete "iOS Simulator 3" | |
| xcrun simctl list devices | grep 'iOS Simulator' | awk -F '[()]' '{print $2}' | xargs -I {} sh -c 'echo "Deleting simulator: {}"; xcrun simctl delete {} || echo "Failed to delete: {}"' | |
| detox-tests-android: | |
| name: Android E2E Tests (${{ matrix.shard }}, ${{ matrix.total }}) | |
| needs: [build-android, determine-builds] | |
| if: ${{ inputs.tests_type != 'iOS Only' && needs.determine-builds.outputs.test_files_for_sharding != '' }} | |
| runs-on: [ledger-live-linux-e2e-8CPU-32RAM] | |
| env: | |
| NODE_OPTIONS: "--max-old-space-size=7168" | |
| LANG: en_US.UTF-8 | |
| LANGUAGE: en_US.UTF-8 | |
| LC_ALL: en_US.UTF-8 | |
| outputs: | |
| status: ${{ steps.run-android.outcome }} | |
| artifact: ${{ steps.test-artifacts.outputs.artifact-id }} | |
| status_1: ${{ steps.set-output.outputs.status_1 }} | |
| status_2: ${{ steps.set-output.outputs.status_2 }} | |
| status_3: ${{ steps.set-output.outputs.status_3 }} | |
| status_4: ${{ steps.set-output.outputs.status_4 }} | |
| status_5: ${{ steps.set-output.outputs.status_5 }} | |
| status_6: ${{ steps.set-output.outputs.status_6 }} | |
| status_7: ${{ steps.set-output.outputs.status_7 }} | |
| status_8: ${{ steps.set-output.outputs.status_8 }} | |
| status_9: ${{ steps.set-output.outputs.status_9 }} | |
| status_10: ${{ steps.set-output.outputs.status_10 }} | |
| status_11: ${{ steps.set-output.outputs.status_11 }} | |
| status_12: ${{ steps.set-output.outputs.status_12 }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: ${{ fromJSON(needs.determine-builds.outputs.matrix_android) }} | |
| steps: | |
| - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 | |
| with: | |
| ref: ${{ inputs.ref || github.sha }} | |
| repository: LedgerHQ/ledger-live | |
| persist-credentials: false | |
| - name: Configure AWS credentials | |
| uses: aws-actions/configure-aws-credentials@v4 | |
| id: aws | |
| with: | |
| role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID_PROD }}:role/${{ secrets.AWS_CACHE_ROLE_NAME }} | |
| aws-region: ${{ secrets.AWS_CACHE_REGION }} | |
| - name: Install scrcpy 3.x and dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y ffmpeg xvfb libsdl2-2.0-0 libusb-1.0-0 | |
| # Download scrcpy 3.3.4 from GitHub releases (apt/snap versions are obsolete) | |
| # See: https://github.com/Genymobile/scrcpy/blob/master/doc/linux.md | |
| SCRCPY_VERSION="3.3.4" | |
| curl -L "https://github.com/Genymobile/scrcpy/releases/download/v${SCRCPY_VERSION}/scrcpy-linux-x86_64-v${SCRCPY_VERSION}.tar.gz" -o /tmp/scrcpy.tar.gz | |
| tar -xzf /tmp/scrcpy.tar.gz -C /tmp | |
| sudo cp /tmp/scrcpy-linux-x86_64-v${SCRCPY_VERSION}/scrcpy /usr/local/bin/ | |
| sudo cp /tmp/scrcpy-linux-x86_64-v${SCRCPY_VERSION}/scrcpy-server /usr/local/bin/ | |
| scrcpy --version | |
| ls -la /usr/local/bin/scrcpy* | |
| - name: Set shard test files from pre-computed matrix | |
| run: | | |
| echo "SHARD_TEST_FILES<<EOF" >> $GITHUB_ENV | |
| echo "${{ matrix.files }}" >> $GITHUB_ENV | |
| echo "EOF" >> $GITHUB_ENV | |
| echo "Using pre-computed shard ${{ matrix.shard }}/${{ matrix.total }} with $(echo '${{ matrix.files }}' | wc -w) test files" | |
| - name: Setup Android Environment | |
| uses: LedgerHQ/ledger-live/tools/actions/composites/setup-android-env@develop | |
| - name: Download Native Build | |
| uses: LedgerHQ/ledger-live/tools/actions/composites/cache/download@develop | |
| with: | |
| key: ${{ needs.determine-builds.outputs.android_native_key }} | |
| accessKey: ${{ env.AWS_ACCESS_KEY_ID }} | |
| secretKey: ${{ env.AWS_SECRET_ACCESS_KEY }} | |
| sessionToken: ${{ env.AWS_SESSION_TOKEN}} | |
| bucket: ${{ env.cache-bucket }} | |
| region: ${{ secrets.AWS_CACHE_REGION }} | |
| - name: Download JS Bundle | |
| uses: LedgerHQ/ledger-live/tools/actions/composites/cache/download@develop | |
| with: | |
| key: ${{ needs.determine-builds.outputs.android_js_key }} | |
| accessKey: ${{ env.AWS_ACCESS_KEY_ID }} | |
| secretKey: ${{ env.AWS_SECRET_ACCESS_KEY }} | |
| sessionToken: ${{ env.AWS_SESSION_TOKEN}} | |
| bucket: ${{ env.cache-bucket }} | |
| region: ${{ secrets.AWS_CACHE_REGION }} | |
| - name: Prepare APK | |
| run: | | |
| mkdir -p /tmp/apk/assets | |
| mkdir /home/runner/.android/ | |
| mv ${{ env.ANDROID_JSBUNDLE_PATH }} /tmp/apk/assets/index.android.bundle | |
| mv ${{ env.ANDROID_APK_PATH }} /tmp/apk/tmp.apk | |
| (cd /tmp/apk/; zip -r tmp.apk assets/index.android.bundle) | |
| /usr/local/lib/android/sdk/build-tools/34.0.0/zipalign -p 4 /tmp/apk/tmp.apk ${{ env.ANDROID_APK_PATH }} | |
| /usr/local/lib/android/sdk/build-tools/34.0.0/apksigner sign --ks ${{ secrets.ANDROID_KEYSTORE_PATH }} --ks-pass ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} --ks-key-alias staging --key-pass ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} ${{ env.ANDROID_APK_PATH }} | |
| - name: Setup the caches | |
| uses: LedgerHQ/ledger-live/tools/actions/composites/setup-caches@develop | |
| id: setup-caches | |
| with: | |
| install-proto: true | |
| skip-pnpm-cache: "false" | |
| skip-turbo-cache: "false" | |
| accountId: ${{ secrets.AWS_ACCOUNT_ID_PROD }} | |
| roleName: ${{ secrets.AWS_CACHE_ROLE_NAME }} | |
| region: ${{ secrets.AWS_CACHE_REGION }} | |
| turbo-server-token: ${{ secrets.TURBOREPO_SERVER_TOKEN }} | |
| - name: Install dependencies | |
| run: | | |
| pnpm i --filter="live-mobile..." --filter="ledger-live" --filter="ledger-live-mobile-e2e-tests" --filter="live-cli*..." --filter="@ledgerhq/dummy-*-app..." --no-frozen-lockfile --unsafe-perm --ignore-scripts | |
| - name: Detox Post Install | |
| run: node apps/ledger-live-mobile/node_modules/detox/scripts/postinstall.js | |
| - name: Build dependencies | |
| uses: LedgerHQ/ledger-live/tools/actions/composites/turbo-step@develop | |
| with: | |
| command: pnpm build:llm:deps | |
| turbo_server_token: ${{ secrets.TURBOREPO_SERVER_TOKEN }} | |
| turbo_port: ${{ steps.setup-caches.outputs.port }} | |
| disable_cache: false | |
| - name: Download android emulator | |
| timeout-minutes: 10 | |
| uses: LedgerHQ/ledger-live/tools/actions/composites/cache/download@develop | |
| id: detox-avd | |
| with: | |
| key: ${{ needs.determine-builds.outputs.avd_cache_key }} | |
| accessKey: "${{ secrets.R2_ACCESS_KEY_ID }}" | |
| secretKey: "${{ secrets.R2_SECRET_ACCESS_KEY }}" | |
| bucket: ledger-live-cache | |
| endpoint: ${{ secrets.R2_ENDPOINT }} | |
| region: auto | |
| destination: / | |
| - name: Duplicate AVDs for parallel testing | |
| uses: LedgerHQ/ledger-live/tools/actions/composites/duplicate-avd@develop | |
| with: | |
| source_avd_name: ${{ env.AVD_NAME }} | |
| target_suffixes: "_2,_3" | |
| avd_api: ${{ env.AVD_API }} | |
| - name: Cleanup any running Android emulators (pre-boot) | |
| run: tools/scripts/cleanup-android-emulators.sh || true | |
| - name: Boot Android Emulators in Background | |
| run: | | |
| LOG_FILE="e2e/mobile/artifacts/emulator-boot.log" | |
| mkdir -p e2e/mobile/artifacts | |
| nohup bash -c ' | |
| log() { echo "[$(date +%H:%M:%S)] $1"; } | |
| ts() { while IFS= read -r line; do echo "[$(date +%H:%M:%S)] [$1] $line"; done; } | |
| log "🛫 Starting emulators..." | |
| $ANDROID_HOME/emulator/emulator -avd ${{ env.AVD_NAME }} ${{ env.AVD_OPTIONS }} --read-only 2>&1 | ts "EMU1" & | |
| $ANDROID_HOME/emulator/emulator -avd ${{ env.AVD_NAME }}_2 ${{ env.AVD_OPTIONS }} --read-only 2>&1 | ts "EMU2" & | |
| $ANDROID_HOME/emulator/emulator -avd ${{ env.AVD_NAME }}_3 ${{ env.AVD_OPTIONS }} --read-only 2>&1 | ts "EMU3" & | |
| log "⏳ Waiting for 3 emulators to connect..." | |
| while [ "$(adb devices | grep -c emulator)" -lt 3 ]; do sleep 2; done | |
| log "✅ 3 emulators connected" | |
| log "⏳ Waiting for all emulators to finish booting..." | |
| adb -s emulator-5554 wait-for-device shell "while [ \"\$(getprop sys.boot_completed)\" != 1 ]; do sleep 1; done" | |
| log "✅ emulator-5554 booted" | |
| adb -s emulator-5556 wait-for-device shell "while [ \"\$(getprop sys.boot_completed)\" != 1 ]; do sleep 1; done" | |
| log "✅ emulator-5556 booted" | |
| adb -s emulator-5558 wait-for-device shell "while [ \"\$(getprop sys.boot_completed)\" != 1 ]; do sleep 1; done" | |
| log "✅ emulator-5558 booted" | |
| log "✅ All 3 emulators are fully booted" | |
| ' > "$LOG_FILE" 2>&1 & | |
| echo "ℹ️ Android emulators boot started in background." | |
| shell: bash | |
| - name: Setup Speculos image and Coin Apps | |
| id: setup-speculos | |
| uses: LedgerHQ/ledger-live/tools/actions/composites/setup-speculos_image@develop | |
| with: | |
| coinapps_path: ${{ env.COINAPPS }} | |
| bot_id: ${{ secrets.GH_BOT_APP_ID }} | |
| bot_key: ${{ secrets.GH_BOT_PRIVATE_KEY }} | |
| - name: Build CLI | |
| uses: LedgerHQ/ledger-live/tools/actions/composites/turbo-step@develop | |
| with: | |
| command: pnpm build:cli | |
| turbo_server_token: ${{ secrets.TURBOREPO_SERVER_TOKEN }} | |
| turbo_port: ${{ steps.setup-caches.outputs.port }} | |
| disable_cache: false | |
| - name: Set DISABLE_TRANSACTION_BROADCAST | |
| uses: LedgerHQ/ledger-live/tools/actions/composites/setup-e2e-env@develop | |
| with: | |
| enable_broadcast: ${{ inputs.enable_broadcast }} | |
| build_type: ${{ inputs.production_firebase == 'true' && 'js' || 'testing' }} | |
| - name: Run Android Detox shard ${{ matrix.shard }}/${{ matrix.total }} | |
| id: run-android | |
| timeout-minutes: ${{ fromJSON(needs.determine-builds.outputs.android_timeout) }} | |
| run: | | |
| pnpm mobile e2e:ci -p android -t \ | |
| --e2e \ | |
| $([[ "$PRODUCTION" == "true" ]] && printf %s '--production') \ | |
| $SHARD_TEST_FILES \ | |
| --outputFile=artifacts/e2e-test-results-android-shard-${{ matrix.shard }}.json | |
| env: | |
| DETOX_INSTALL_TIMEOUT: 120000 | |
| SEED: ${{ secrets.SEED_QAA_B2C }} | |
| INPUT_E2E: "true" | |
| PRODUCTION: ${{ inputs.production_firebase }} | |
| INPUTS_TEST_FILTER: ${{ needs.determine-builds.outputs.test_filter }} | |
| SWAP_API_BASE: ${{ env.SWAP_API_BASE }} | |
| SHARD_INDEX: ${{ matrix.shard }} | |
| ADB: /usr/local/lib/android/sdk/platform-tools/adb # Needed for scrcpy | |
| - name: Upload Android artifacts | |
| if: (!cancelled() || steps.run-android.outcome == 'cancelled') | |
| uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 | |
| id: "test-artifacts" | |
| with: | |
| name: android-test-artifacts-${{ matrix.shard }} | |
| path: e2e/mobile/artifacts | |
| - name: Setup Android Shard timing artifacts | |
| if: (!cancelled() || steps.run-android.outcome == 'cancelled') | |
| uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 | |
| with: | |
| path: e2e/mobile/artifacts/e2e-test-results-android-shard-${{ matrix.shard }}.json | |
| name: "${{ needs.determine-builds.outputs.android_timing_cache_key }}-${{ matrix.shard }}" | |
| - name: Set job output based on detox result | |
| id: set-output | |
| if: ${{ !cancelled() }} | |
| run: | | |
| echo "status_${{ matrix.shard }}=${{ steps.run-android.outcome }}" >> $GITHUB_OUTPUT | |
| allure-report-ios: | |
| name: Allure Report iOS | |
| runs-on: [ledger-live-medium] | |
| if: ${{ !cancelled() && needs.detox-tests-ios.outputs.artifact && inputs.tests_type != 'Android Only' }} | |
| needs: [determine-builds, detox-tests-ios] | |
| outputs: | |
| report-url: ${{ steps.upload.outputs.report-url }} | |
| result: ${{ steps.summary.outputs.test_result }} | |
| finalStatus: ${{ steps.aggregate.outputs.finalStatus }} | |
| missingShards: ${{ steps.aggregate.outputs.missingShards }} | |
| steps: | |
| - name: Download Allure Report | |
| uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 | |
| with: | |
| path: ios-test-artifacts | |
| pattern: ios-test-artifacts* | |
| merge-multiple: true | |
| - uses: LedgerHQ/ledger-live/tools/actions/composites/upload-allure-report@develop | |
| if: ${{ !cancelled() }} | |
| id: upload | |
| with: | |
| platform: ios-e2e | |
| login: ${{ vars.ALLURE_USERNAME }} | |
| password: ${{ secrets.ALLURE_LEDGER_LIVE_PASSWORD }} | |
| path: ios-test-artifacts | |
| report_path_suffix: ${{ github.event_name != 'schedule' && 'manual' || '' }} | |
| - name: Get summary | |
| id: summary | |
| if: ${{ !cancelled() }} | |
| uses: LedgerHQ/ledger-live/tools/actions/composites/get-allure-summary@develop | |
| with: | |
| allure-results-path: ios-test-artifacts | |
| platform: iOS | |
| - name: Aggregate test results | |
| id: aggregate | |
| uses: LedgerHQ/ledger-live/tools/actions/composites/aggregate-shard-results@develop | |
| with: | |
| total_shards: ${{ fromJSON(needs.determine-builds.outputs.matrix_ios)[0].total }} | |
| status_1: ${{ needs.detox-tests-ios.outputs.status_1 }} | |
| status_2: ${{ needs.detox-tests-ios.outputs.status_2 }} | |
| status_3: ${{ needs.detox-tests-ios.outputs.status_3 }} | |
| status_4: ${{ needs.detox-tests-ios.outputs.status_4 }} | |
| status_5: ${{ needs.detox-tests-ios.outputs.status_5 }} | |
| status_6: ${{ needs.detox-tests-ios.outputs.status_6 }} | |
| status_7: ${{ needs.detox-tests-ios.outputs.status_7 }} | |
| status_8: ${{ needs.detox-tests-ios.outputs.status_8 }} | |
| status_9: ${{ needs.detox-tests-ios.outputs.status_9 }} | |
| status_10: ${{ needs.detox-tests-ios.outputs.status_10 }} | |
| status_11: ${{ needs.detox-tests-ios.outputs.status_11 }} | |
| status_12: ${{ needs.detox-tests-ios.outputs.status_12 }} | |
| allure-report-android: | |
| name: Allure Report Android | |
| runs-on: [ledger-live-medium] | |
| if: ${{ !cancelled() && needs.detox-tests-android.outputs.artifact && inputs.tests_type != 'iOS Only' }} | |
| outputs: | |
| report-url: ${{ steps.upload.outputs.report-url }} | |
| result: ${{ steps.summary.outputs.test_result }} | |
| finalStatus: ${{ steps.aggregate.outputs.finalStatus }} | |
| missingShards: ${{ steps.aggregate.outputs.missingShards }} | |
| needs: [determine-builds, detox-tests-android] | |
| steps: | |
| - name: Download Allure Report | |
| uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 | |
| with: | |
| path: android-test-artifacts | |
| pattern: android-test-artifacts* | |
| merge-multiple: true | |
| - uses: LedgerHQ/ledger-live/tools/actions/composites/upload-allure-report@develop | |
| id: upload | |
| if: ${{ !cancelled() }} | |
| with: | |
| platform: android-e2e | |
| login: ${{ vars.ALLURE_USERNAME }} | |
| password: ${{ secrets.ALLURE_LEDGER_LIVE_PASSWORD }} | |
| path: android-test-artifacts | |
| report_path_suffix: ${{ github.event_name != 'schedule' && 'manual' || '' }} | |
| - name: Get summary | |
| id: summary | |
| if: ${{ !cancelled() }} | |
| uses: LedgerHQ/ledger-live/tools/actions/composites/get-allure-summary@develop | |
| with: | |
| allure-results-path: android-test-artifacts | |
| platform: android | |
| - name: Aggregate test results | |
| id: aggregate | |
| uses: LedgerHQ/ledger-live/tools/actions/composites/aggregate-shard-results@develop | |
| with: | |
| total_shards: ${{ fromJSON(needs.determine-builds.outputs.matrix_android)[0].total }} | |
| status_1: ${{ needs.detox-tests-android.outputs.status_1 }} | |
| status_2: ${{ needs.detox-tests-android.outputs.status_2 }} | |
| status_3: ${{ needs.detox-tests-android.outputs.status_3 }} | |
| status_4: ${{ needs.detox-tests-android.outputs.status_4 }} | |
| status_5: ${{ needs.detox-tests-android.outputs.status_5 }} | |
| status_6: ${{ needs.detox-tests-android.outputs.status_6 }} | |
| status_7: ${{ needs.detox-tests-android.outputs.status_7 }} | |
| status_8: ${{ needs.detox-tests-android.outputs.status_8 }} | |
| status_9: ${{ needs.detox-tests-android.outputs.status_9 }} | |
| status_10: ${{ needs.detox-tests-android.outputs.status_10 }} | |
| status_11: ${{ needs.detox-tests-android.outputs.status_11 }} | |
| status_12: ${{ needs.detox-tests-android.outputs.status_12 }} | |
| upload-to-xray: | |
| name: "Test Mobile E2E > XRAY Report" | |
| runs-on: [ledger-live-medium] | |
| strategy: | |
| matrix: | |
| platform: | |
| - android | |
| - ios | |
| exclude: | |
| - platform: ${{ github.event.inputs.tests_type == 'Android Only' && 'ios' }} | |
| fail-fast: false | |
| env: | |
| XRAY_CLIENT_ID: ${{ secrets.XRAY_CLIENT_ID }} | |
| XRAY_CLIENT_SECRET: ${{ secrets.XRAY_CLIENT_SECRET }} | |
| XRAY_API_URL: https://xray.cloud.getxray.app/api/v2 | |
| JIRA_URL: https://ledgerhq.atlassian.net/browse | |
| TEST_EXECUTION: ${{ matrix.platform == 'android' && inputs.test_execution_android || inputs.test_execution_ios }} | |
| needs: [detox-tests-android, detox-tests-ios] | |
| if: ${{ !cancelled() && inputs.export_to_xray }} | |
| steps: | |
| - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 | |
| with: | |
| ref: ${{ inputs.ref || github.sha }} | |
| repository: LedgerHQ/ledger-live | |
| - name: Download Allure Results | |
| uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 | |
| with: | |
| path: "artifacts-${{ matrix.platform }}" | |
| pattern: ${{ matrix.platform }}-test-artifacts-* | |
| merge-multiple: true | |
| - name: Format Xray results | |
| run: e2e/mobile/xray.formater.sh artifacts-${{ matrix.platform }} ${{ matrix.platform }} ${{ env.TEST_EXECUTION}} | |
| - name: Upload aggregated xray results | |
| uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 | |
| with: | |
| retention-days: 1 | |
| name: xray-reports-${{ matrix.platform }} | |
| path: "artifacts-${{ matrix.platform }}/xray_report.json" | |
| - name: Authenticate to Xray | |
| id: authenticate | |
| run: | | |
| response=$(curl -H "Content-Type: application/json" -X POST \ | |
| --data '{"client_id": "${{ env.XRAY_CLIENT_ID }}", "client_secret": "${{ env.XRAY_CLIENT_SECRET }}"}' \ | |
| ${{ env.XRAY_API_URL }}/authenticate) | |
| echo "Xray Authentication Response: $response" | |
| echo "xray_token=$response" >> $GITHUB_OUTPUT | |
| - name: Publish report on Xray | |
| id: publish-xray | |
| run: | | |
| response=$(curl -H "Content-Type: application/json" \ | |
| -H "Authorization: Bearer ${{ steps.authenticate.outputs.xray_token }}" \ | |
| -X POST \ | |
| --data @artifacts-${{ matrix.platform }}/xray_report.json \ | |
| ${{ env.XRAY_API_URL }}/import/execution) | |
| echo "Xray Report Response: $response" | |
| key=$(echo $response | jq -r '.key') | |
| echo "xray_key=$key" >> $GITHUB_OUTPUT | |
| - name: Write Xray report link in summary | |
| shell: bash | |
| run: echo "::notice title=${{ matrix.platform }} Xray report URL::${{ env.JIRA_URL }}/${{ steps.publish-xray.outputs.xray_key }}" | |
| report-on-slack: | |
| name: "Test Mobile E2E > Slack Report" | |
| runs-on: ubuntu-22.04 | |
| needs: [allure-report-android, allure-report-ios] | |
| if: ${{ !cancelled() && (needs.allure-report-ios.outputs.report-url || needs.allure-report-android.outputs.report-url) }} | |
| env: | |
| IOS_STATUS: ${{ needs.allure-report-ios.outputs.finalStatus }} | |
| IOS_REPORT_URL: ${{ needs.allure-report-ios.outputs.report-url }} | |
| IOS_MISSING_SHARDS: ${{ needs.allure-report-ios.outputs.missingShards }} | |
| ANDROID_STATUS: ${{ needs.allure-report-android.outputs.finalStatus }} | |
| ANDROID_REPORT_URL: ${{ needs.allure-report-android.outputs.report-url }} | |
| ANDROID_MISSING_SHARDS: ${{ needs.allure-report-android.outputs.missingShards }} | |
| TESTS_TYPE: ${{ inputs.tests_type }} | |
| steps: | |
| - name: format message | |
| uses: actions/github-script@v7 | |
| id: message | |
| with: | |
| script: | | |
| const fs = require("fs"); | |
| const text = "Ledger Live Mobile E2E tests finished"; | |
| const header = [ | |
| { | |
| "type": "header", | |
| "text": { | |
| "type": "plain_text", | |
| "text": ":ledger-logo: Ledger Live Mobile E2E tests results on ${{ inputs.ref || github.ref_name }} - ${{ inputs.speculos_device || 'nanoX' }}${{ inputs.smoke_tests && ' (Smoke Tests)' || '' }}", | |
| "emoji": true | |
| } | |
| }, | |
| { | |
| "type": "divider" | |
| } | |
| ]; | |
| const iOSResult = [ | |
| { | |
| "type": "section", | |
| "text": { | |
| "type": "mrkdwn", | |
| "text": `- 🍏 iOS - ${{ inputs.speculos_device }}: ${process.env.IOS_STATUS !== 'success' ? '❌' : '✅'} ${{ needs.allure-report-ios.outputs.result || 'No test results' }}` | |
| } | |
| } | |
| ]; | |
| const androidResult = [ | |
| { | |
| "type": "section", | |
| "text": { | |
| "type": "mrkdwn", | |
| "text": `- 🤖 Android - ${{ inputs.speculos_device }}: ${process.env.ANDROID_STATUS !== 'success' ? '❌' : '✅'} ${{ needs.allure-report-android.outputs.result || 'No test results' }}` | |
| } | |
| } | |
| ]; | |
| const iOSInfo = [ | |
| { | |
| "type": "mrkdwn", | |
| "text": process.env.IOS_REPORT_URL ? `*Allure Report iOS*\n<${process.env.IOS_REPORT_URL}|Allure Report iOS>` : '*Allure Report iOS*\nNo Allure Report' | |
| } | |
| ]; | |
| const androidInfo = [ | |
| { | |
| "type": "mrkdwn", | |
| "text": process.env.ANDROID_REPORT_URL ? `*Allure Report Android*\n<${process.env.ANDROID_REPORT_URL}|Allure Report Android>` : '*Allure Report Android*\nNo Allure Report' | |
| } | |
| ]; | |
| const infoFields = [] | |
| .concat(${{ env.TESTS_TYPE == 'Android Only' }} ? [] : iOSInfo) | |
| .concat(${{ env.TESTS_TYPE == 'iOS Only' }} ? [] : androidInfo) | |
| .concat([ | |
| { | |
| "type": "mrkdwn", | |
| "text": `*Workflow*\n<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|Workflow run>` | |
| } | |
| ]); | |
| const infoBlock = [ | |
| { | |
| "type": "divider" | |
| }, | |
| { | |
| "type": "section", | |
| "fields": infoFields | |
| } | |
| ]; | |
| // Warning block for failed shards | |
| const warningBlocks = []; | |
| if (process.env.IOS_MISSING_SHARDS) { | |
| warningBlocks.push({ | |
| "type": "section", | |
| "text": { | |
| "type": "mrkdwn", | |
| "text": `⚠️ *Warning*: iOS - Missing results for shard(s): ${process.env.IOS_MISSING_SHARDS}.` | |
| } | |
| }); | |
| } | |
| if (process.env.ANDROID_MISSING_SHARDS) { | |
| warningBlocks.push({ | |
| "type": "section", | |
| "text": { | |
| "type": "mrkdwn", | |
| "text": `⚠️ *Warning*: Android - Missing results for shard(s): ${process.env.ANDROID_MISSING_SHARDS}.` | |
| } | |
| }); | |
| } | |
| const blocks = [] | |
| .concat(header) | |
| .concat(${{ env.TESTS_TYPE == 'Android Only' }} ? [] : iOSResult) | |
| .concat(${{ env.TESTS_TYPE == 'iOS Only' }} ? [] : androidResult) | |
| .concat(warningBlocks.length > 0 ? warningBlocks : []) | |
| .concat(infoBlock); | |
| const result = { | |
| attachments: [ | |
| { | |
| color: process.env.ANDROID_STATUS !== 'success' || (process.env.IOS_STATUS !== 'success' && process.env.ANDROID_ONLY == 'false') | |
| ? "#FF333C" | |
| : "#33FF39", | |
| blocks, | |
| }, | |
| ], | |
| }; | |
| fs.writeFileSync(`./payload-slack-content.json`, JSON.stringify(result, null, 2)); | |
| - name: post to a Slack channel | |
| id: slack | |
| uses: slackapi/slack-github-action@v1.23.0 | |
| with: | |
| channel-id: "CTMQ0S5SB" | |
| payload-file-path: "./payload-slack-content.json" | |
| env: | |
| SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_USER_OAUTH_ACCESS_TOKEN }} | |
| - name: post to a Slack channel | |
| if: ${{ contains(fromJson('["release"]'), github.ref_name) || contains(fromJson('["release, develop"]'), inputs.ref) || github.event_name == 'schedule' }} | |
| uses: slackapi/slack-github-action@v1.23.0 | |
| with: | |
| channel-id: "C05FKJ7DFAP" | |
| payload-file-path: "./payload-slack-content.json" | |
| env: | |
| SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_USER_OAUTH_ACCESS_TOKEN }} | |
| merge-ios-timings: | |
| name: Merge iOS Timing Files | |
| needs: [determine-builds, detox-tests-ios] | |
| runs-on: ubuntu-24.04 | |
| if: ${{ !cancelled() && (needs.detox-tests-ios.result == 'success' || needs.detox-tests-ios.result == 'failure') }} | |
| steps: | |
| - name: Configure AWS credentials | |
| uses: aws-actions/configure-aws-credentials@v4 | |
| id: aws | |
| with: | |
| role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID_PROD }}:role/${{ secrets.AWS_CACHE_ROLE_NAME }} | |
| aws-region: ${{ secrets.AWS_CACHE_REGION }} | |
| - uses: LedgerHQ/ledger-live/tools/actions/composites/merge-e2e-detox-timings@develop | |
| with: | |
| platform: ios | |
| artifacts_dir: e2e/mobile/artifacts | |
| key: ${{ needs.determine-builds.outputs.ios_timing_cache_key }} | |
| aws_access_key_id: ${{ env.AWS_ACCESS_KEY_ID }} | |
| aws_secret_access_key: ${{ env.AWS_SECRET_ACCESS_KEY }} | |
| aws_session_token: ${{ env.AWS_SESSION_TOKEN }} | |
| aws_region: ${{ secrets.AWS_CACHE_REGION }} | |
| cache_bucket: ${{ env.cache-bucket }} | |
| github_ref: ${{ github.ref }} | |
| github_run_id: ${{ github.run_id }} | |
| roleName: ${{ secrets.AWS_CACHE_ROLE_NAME }} | |
| accountId: ${{ secrets.AWS_ACCOUNT_ID_PROD }} | |
| merge-android-timings: | |
| name: Merge Android Timing Files | |
| needs: [determine-builds, detox-tests-android] | |
| runs-on: ubuntu-24.04 | |
| if: ${{ !cancelled() && (needs.detox-tests-android.result == 'success' || needs.detox-tests-android.result == 'failure') }} | |
| steps: | |
| - name: Configure AWS credentials | |
| uses: aws-actions/configure-aws-credentials@v4 | |
| id: aws | |
| with: | |
| role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID_PROD }}:role/${{ secrets.AWS_CACHE_ROLE_NAME }} | |
| aws-region: ${{ secrets.AWS_CACHE_REGION }} | |
| - uses: LedgerHQ/ledger-live/tools/actions/composites/merge-e2e-detox-timings@develop | |
| with: | |
| platform: android | |
| artifacts_dir: e2e/mobile/artifacts | |
| key: ${{ needs.determine-builds.outputs.android_timing_cache_key }} | |
| aws_access_key_id: ${{ env.AWS_ACCESS_KEY_ID }} | |
| aws_secret_access_key: ${{ env.AWS_SECRET_ACCESS_KEY }} | |
| aws_session_token: ${{ env.AWS_SESSION_TOKEN }} | |
| aws_region: ${{ secrets.AWS_CACHE_REGION }} | |
| cache_bucket: ${{ env.cache-bucket }} | |
| github_ref: ${{ github.ref }} | |
| github_run_id: ${{ github.run_id }} | |
| roleName: ${{ secrets.AWS_CACHE_ROLE_NAME }} | |
| accountId: ${{ secrets.AWS_ACCOUNT_ID_PROD }} |