From 5a6eb62a1bf1328437b94a023e287d4fede618c1 Mon Sep 17 00:00:00 2001 From: SatoshiQaziMuhammed Date: Sun, 18 Oct 2026 19:23:15 +0400 Subject: [PATCH] ci(release): smoke-test the release APK on an emulator, drop the BrowserStack trigger (#25) * ci(release): smoke-test the release APK on an emulator, drop the BrowserStack trigger The release workflow ended by triggering BrowserStack tests in appium-mobile-tests with a personal token. The token expired on 2026-10-17, so every release build since is red, and the BrowserStack run behind it had never produced a result (it fails uploading the app, KeyError 'app_url', since 2026-08-03). Its tests mostly asserted nothing. In its place, every release build now installs the APK on an x86_64 API 34 emulator on a GitHub-hosted runner, launches it, and fails unless the first-run welcome screen appears with both its create and restore buttons and the app has neither crashed nor exited. The screen is matched by view id, so it holds in any locale. A screenshot, the view tree and logcat are kept as an artifact. It needs no account and no stored secret, and it can be run by hand against the APK of an earlier run. * ci: run the emulator smoke test on pull requests that change it, against main's latest release APK * TEMP: nail check, expect red * Revert "TEMP: nail check, expect red" This reverts commit 7f804317b4612fec832b55638cddf3d3ea5aba98. --- .github/scripts/smoke-test.sh | 39 ++++++++++++ .github/workflows/appium-mobile-tests.yml | 71 ---------------------- .github/workflows/emulator_smoke_test.yml | 73 +++++++++++++++++++++++ .github/workflows/push_develop.yml | 17 ++---- 4 files changed, 118 insertions(+), 82 deletions(-) create mode 100755 .github/scripts/smoke-test.sh delete mode 100644 .github/workflows/appium-mobile-tests.yml create mode 100644 .github/workflows/emulator_smoke_test.yml diff --git a/.github/scripts/smoke-test.sh b/.github/scripts/smoke-test.sh new file mode 100755 index 00000000..2a2da4d1 --- /dev/null +++ b/.github/scripts/smoke-test.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +# Installs a release APK on the running emulator, launches it and waits for the +# first-run welcome screen. Fails if the app crashes, dies, or never shows the +# screen. Matches the screen by view id, not text, so it holds in any locale. +set -euo pipefail + +APK="$1" +PKG="io.pezkuwichain.wallet" +OUT="${SMOKE_OUT:-smoke-out}" +TIMEOUT=120 +mkdir -p "$OUT" + +fail() { + echo "::error::$1" + adb exec-out screencap -p > "$OUT/screen.png" || true + adb shell uiautomator dump /sdcard/ui.xml >/dev/null 2>&1 && adb pull /sdcard/ui.xml "$OUT/ui.xml" >/dev/null || true + adb logcat -d > "$OUT/logcat.txt" || true + exit 1 +} + +adb install -r "$APK" >/dev/null || fail "install failed" +adb logcat -c +adb shell monkey -p "$PKG" -c android.intent.category.LAUNCHER 1 >/dev/null 2>&1 || fail "launch failed" + +for _ in $(seq 1 $((TIMEOUT / 3))); do + sleep 3 + if adb logcat -d -b crash | grep -q "Process: $PKG"; then + fail "the app crashed on launch" + fi + adb shell pidof "$PKG" >/dev/null || fail "the app process is gone" + adb shell uiautomator dump /sdcard/ui.xml >/dev/null 2>&1 || continue + if adb exec-out cat /sdcard/ui.xml | grep -q "$PKG:id/welcomeCreateWalletButton" && + adb exec-out cat /sdcard/ui.xml | grep -q "$PKG:id/welcomeRestoreWalletButton"; then + echo "welcome screen shown, both create and restore buttons present" + adb exec-out screencap -p > "$OUT/screen.png" || true + exit 0 + fi +done +fail "the welcome screen did not appear within ${TIMEOUT}s" diff --git a/.github/workflows/appium-mobile-tests.yml b/.github/workflows/appium-mobile-tests.yml deleted file mode 100644 index 7a564e0e..00000000 --- a/.github/workflows/appium-mobile-tests.yml +++ /dev/null @@ -1,71 +0,0 @@ -name: Appium Mobile Tests - -on: - workflow_call: - inputs: - app_url: - type: string - description: URL to download the app from - required: true - test_grep: - type: string - description: Test pattern to run (pytest marker or test name) - required: false - default: "android" - allure_job_run_id: - type: string - description: ALLURE_JOB_RUN_ID service parameter. Leave blank. - required: false - default: "" - allure_username: - type: string - description: ALLURE_USERNAME service parameter. Leave blank. - required: false - default: "" - secrets: - WORKFLOW_TOKEN: - required: true - ALLURE_TOKEN: - required: false - -env: - PYTHON_VERSION: '3.9' - CI: true - ALLURE_ENDPOINT: https://pezkuwi.testops.cloud/ - ALLURE_PROJECT_ID: 103 - -permissions: - contents: read - -jobs: - trigger-tests: - runs-on: ubuntu-latest - steps: - - name: Trigger mobile tests in test repository - uses: actions/github-script@v7 - with: - github-token: ${{ secrets.WORKFLOW_TOKEN }} - script: | - const response = await github.rest.actions.createWorkflowDispatch({ - owner: 'pezkuwichain', - repo: 'appium-mobile-tests', - workflow_id: 'browserstack-tests.yml', - ref: 'master', - inputs: { - app_url: '${{ inputs.app_url }}', - ALLURE_JOB_RUN_ID: '${{ inputs.allure_job_run_id }}', - ALLURE_USERNAME: '${{ inputs.allure_username }}' - } - }); - - console.log('Mobile tests triggered successfully'); - console.log('App URL:', '${{ inputs.app_url }}'); - - - name: Wait for test completion (optional) - if: false - uses: actions/github-script@v7 - with: - github-token: ${{ secrets.WORKFLOW_TOKEN }} - script: | - console.log('Waiting for test completion...'); - diff --git a/.github/workflows/emulator_smoke_test.yml b/.github/workflows/emulator_smoke_test.yml new file mode 100644 index 00000000..89455d91 --- /dev/null +++ b/.github/workflows/emulator_smoke_test.yml @@ -0,0 +1,73 @@ +name: Emulator smoke test + +# Installs the release APK on an Android emulator and checks it starts and +# reaches the welcome screen. Called by the release workflow on every build. +# A pull request that changes the test runs it against the latest release APK +# from main; it can also be run by hand against the APK of a given run. + +on: + workflow_call: + workflow_dispatch: + inputs: + run-id: + description: "Run whose 'apk' artifact to test" + required: true + pull_request: + paths: + - .github/workflows/emulator_smoke_test.yml + - .github/scripts/smoke-test.sh + +permissions: + contents: read + actions: read + +jobs: + smoke-test: + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + + - name: Pick the APK to test + id: apk + env: + GH_TOKEN: ${{ github.token }} + RUN_ID: ${{ inputs.run-id }} + run: | + if [ "${{ github.event_name }}" = "pull_request" ]; then + RUN_ID=$(gh api "repos/${{ github.repository }}/actions/artifacts?name=apk&per_page=20" \ + --jq '[.artifacts[] | select(.expired | not) | select(.workflow_run.head_branch == "main")][0].workflow_run.id') + [ -n "$RUN_ID" ] && [ "$RUN_ID" != "null" ] || { echo "::error::no release APK from main to test"; exit 1; } + fi + echo "run-id=${RUN_ID:-${{ github.run_id }}}" >> "$GITHUB_OUTPUT" + + - name: Download built artifact + uses: actions/download-artifact@v4 + with: + name: apk + path: app + run-id: ${{ steps.apk.outputs.run-id }} + github-token: ${{ github.token }} + + - 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 + + - name: Launch the app on an emulator + uses: reactivecircus/android-emulator-runner@a421e43855164a8197daf9d8d40fe71c6996bb0d # v2.38.0 + with: + api-level: 34 + target: google_apis + arch: x86_64 + disable-animations: true + script: .github/scripts/smoke-test.sh app/releaseMarket/app-releaseMarket.apk + + - name: Upload screen, view tree and logcat + if: always() + uses: actions/upload-artifact@v4 + with: + name: smoke-test-output + path: smoke-out/ + if-no-files-found: ignore diff --git a/.github/workflows/push_develop.yml b/.github/workflows/push_develop.yml index 912981b9..072cfa30 100644 --- a/.github/workflows/push_develop.yml +++ b/.github/workflows/push_develop.yml @@ -69,14 +69,9 @@ jobs: run: | echo "App uploaded to: ${{ steps.s3_upload.outputs.s3_url }}" - appium-mobile-tests: - needs: [upload-to-s3] - if: ${{ always() && needs.upload-to-s3.result == 'success' }} - uses: ./.github/workflows/appium-mobile-tests.yml - with: - app_url: ${{ needs.upload-to-s3.outputs.s3_url }} - test_grep: "android" - allure_job_run_id: "" - secrets: - WORKFLOW_TOKEN: ${{ secrets.PAT_TOKEN }} - ALLURE_TOKEN: ${{ secrets.ALLURE_TOKEN }} + smoke-test: + needs: build + permissions: + contents: read + actions: read + uses: ./.github/workflows/emulator_smoke_test.yml