Skip to content

@Mobile E2E • Scheduled • All • develop • liviuciulinaru #3880

@Mobile E2E • Scheduled • All • develop • liviuciulinaru

@Mobile E2E • Scheduled • All • develop • liviuciulinaru #3880

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: 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: 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 }}
- 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 }}