Repository navigation
Maestro E2E #97
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: Maestro E2E | |
| # Real-backend E2E against the shared test account, so it is not wired to every PR by | |
| # default: run it on demand (choose tags) and nightly (smoke). To gate PRs, add a | |
| # `pull_request:` trigger below — note it needs the test-account secrets, so it won't run | |
| # on fork PRs. | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| tags: | |
| description: "Maestro include-tags (e.g. smoke, tipping, gate)" | |
| default: "smoke" | |
| exclude_tags: | |
| description: "Maestro exclude-tags (side-effecting flows excluded by default)" | |
| default: "spends-funds,creates-account" | |
| minify: | |
| description: "Run against an R8-minified build, an unminified one, or both in turn" | |
| type: choice | |
| default: "true" | |
| options: ["true", "false", "both"] | |
| schedule: | |
| - cron: "37 7 * * *" # nightly, off the top of the hour | |
| concurrency: | |
| group: maestro-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| # `minify: both` has to become a matrix before the job starts, and a matrix cannot be | |
| # built from an input inline. Scheduled runs get no input, so they fall through to the | |
| # minified build — that is the one that catches a broken keep rule. | |
| variants: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| minify: ${{ steps.pick.outputs.minify }} | |
| steps: | |
| - id: pick | |
| run: | | |
| case "${{ github.event.inputs.minify || 'true' }}" in | |
| both) echo 'minify=["true","false"]' >> "$GITHUB_OUTPUT" ;; | |
| false) echo 'minify=["false"]' >> "$GITHUB_OUTPUT" ;; | |
| *) echo 'minify=["true"]' >> "$GITHUB_OUTPUT" ;; | |
| esac | |
| maestro: | |
| name: Maestro E2E (${{ github.event.inputs.tags || 'smoke' }}, minify=${{ matrix.minify }}) | |
| needs: variants | |
| runs-on: ubuntu-24.04 # ruby/setup-ruby has no Ruby 2.7 build for ubuntu-26.04 | |
| timeout-minutes: 60 | |
| strategy: | |
| # One at a time: every flow signs into the same shared test account, so two runners | |
| # would fight over its balance and session. | |
| max-parallel: 1 | |
| fail-fast: false | |
| matrix: | |
| minify: ${{ fromJSON(needs.variants.outputs.minify) }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 1 | |
| - name: Setup Java env | |
| uses: actions/setup-java@v3 | |
| with: | |
| java-version: "21" | |
| distribution: "corretto" | |
| cache: "gradle" | |
| - name: Setup Ruby env | |
| uses: ruby/setup-ruby@v1 | |
| with: | |
| ruby-version: 2.7.2 | |
| bundler-cache: true | |
| # Build prerequisites (same as the unit-test CI job). | |
| - name: Decode Google Services JSON file | |
| uses: timheuer/base64-to-file@v1 | |
| with: | |
| fileName: google-services.json | |
| fileDir: ./apps/flipcash/app/src | |
| encodedString: ${{ secrets.FLIPCASH2_GOOGLE_SERVICES }} | |
| - name: Setup local.properties API keys | |
| run: | | |
| { | |
| echo "BUGSNAG_API_KEY=\"${{ secrets.FLIPCASH_BUGSNAG_API_KEY }}\"" | |
| echo "MIXPANEL_API_KEY=\"${{ secrets.FLIPCASH_MIXPANEL_API_KEY }}\"" | |
| echo "COINBASE_ONRAMP_API_KEY=${{ secrets.COINBASE_ONRAMP_API_KEY }}" | |
| echo "GOOGLE_CLOUD_PROJECT_NUMBER=${{ secrets.GOOGLE_CLOUD_PROJECT_NUMBER }}" | |
| } >> ./local.properties | |
| - name: Install Maestro CLI | |
| run: | | |
| # Pinned: the installer takes the latest release otherwise. 2.11.0 is what the | |
| # nightly has run since 2026-09-29. | |
| export MAESTRO_VERSION=2.11.0 | |
| curl -fsSL "https://get.maestro.mobile.dev" | bash | |
| echo "$HOME/.maestro/bin" >> "$GITHUB_PATH" | |
| # KVM is required for a fast x86_64 emulator on GitHub-hosted Linux runners. | |
| - name: Enable KVM | |
| run: | | |
| echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' \ | |
| | sudo tee /etc/udev/rules.d/99-kvm4all.rules | |
| sudo udevadm control --reload-rules | |
| sudo udevadm trigger --name-match=kvm | |
| # Build the APK before the emulator exists. Compiling and R8-minifying the app takes | |
| # ~10 minutes and several GB of heap; done inside the emulator step it competes with | |
| # the running guest for the runner's memory, and since 2026-10-08 the emulator has | |
| # exited mid-flow (adb: offline, then device not found). Stopping the daemons after | |
| # the build returns that memory before the guest starts; the lane's installDebug | |
| # then finds every task up to date. | |
| - name: Build debug APK | |
| run: | | |
| ./gradlew :apps:flipcash:app:assembleDebug -p . | |
| ./gradlew --stop | |
| env: | |
| DEBUG_MINIFY: ${{ matrix.minify }} | |
| - name: Run Maestro suite on emulator | |
| uses: reactivecircus/android-emulator-runner@v2 | |
| with: | |
| api-level: 34 | |
| arch: x86_64 | |
| profile: pixel_6 | |
| ram-size: 4096M | |
| heap-size: 576M | |
| force-avd-creation: false | |
| emulator-options: -no-window -gpu swangle_indirect -no-snapshot -noaudio -no-boot-anim | |
| disable-animations: true | |
| # logcat runs alongside so the lines before an emulator exit survive in the artifact. | |
| script: | | |
| adb wait-for-device | |
| # The AVD boots with 3-button navigation, whose Back key sits over the bottom row of | |
| # the chat list: a tap on a contact there backgrounded the app. Real devices use | |
| # gestures, and the app is laid out edge-to-edge for them. | |
| adb shell cmd overlay enable com.android.internal.systemui.navbar.gestural | |
| adb shell cmd overlay list | grep -i navbar || true | |
| adb logcat -v threadtime > "$GITHUB_WORKSPACE/logcat.txt" & | |
| bundle exec fastlane android flipcash_maestro | |
| env: | |
| MAESTRO_TAGS: ${{ github.event.inputs.tags || 'smoke' }} | |
| MAESTRO_EXCLUDE_TAGS: ${{ github.event.inputs.exclude_tags || 'spends-funds,creates-account' }} | |
| # Read by tryReadProperty in apps/flipcash/app/build.gradle.kts. local.properties | |
| # wins over the environment there, and the step above never writes this key, so the | |
| # value set here is the one Gradle sees. UI_TESTABLE stays true either way, since it | |
| # is off only for the shipping `release` variant. | |
| DEBUG_MINIFY: ${{ matrix.minify }} | |
| # Test-account credentials (map GitHub secrets -> the env vars run.sh reads). | |
| SEED_PHRASE: ${{ secrets.MAESTRO_SEED_PHRASE }} | |
| LOGIN_DEEPLINK: ${{ secrets.MAESTRO_LOGIN_DEEPLINK }} | |
| TIPCARD_DEEPLINK: ${{ secrets.MAESTRO_TIPCARD_DEEPLINK }} | |
| LOGIN_USERNAME: ${{ secrets.MAESTRO_LOGIN_USERNAME }} | |
| USDF_ONLY_DEEPLINK: ${{ secrets.MAESTRO_USDF_ONLY_DEEPLINK }} | |
| CONTACT_NAME: ${{ secrets.MAESTRO_CONTACT_NAME }} | |
| CONTACT_PHONE: ${{ secrets.MAESTRO_CONTACT_PHONE }} | |
| # An emulator killed by the host leaves no trace in the step log; the kernel log does. | |
| - name: Kernel log (OOM / qemu) | |
| if: failure() | |
| run: sudo dmesg | grep -i -E "out of memory|oom-kill|killed process|qemu" | tail -20 || true | |
| - name: Upload Maestro report | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: maestro-report-minify-${{ matrix.minify }} | |
| path: | | |
| maestro-report.xml | |
| logcat.txt | |
| ~/.maestro/tests/** | |
| if-no-files-found: ignore |