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 7f804317b4.
This commit is contained in:
SatoshiQaziMuhammed authored and GitHub committed 2026-10-18 19:23:15 +04:00
1 parent 5f96f63330
commit 5a6eb62a1b
4 files changed
+118 -82

No files matched your search

+39
View File
@@ -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"
-71
View File
@@ -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...');
+73
View File
@@ -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
+6 -11
View File
@@ -69,14 +69,9 @@ jobs:
run: | run: |
echo "App uploaded to: ${{ steps.s3_upload.outputs.s3_url }}" echo "App uploaded to: ${{ steps.s3_upload.outputs.s3_url }}"
appium-mobile-tests: smoke-test:
needs: [upload-to-s3] needs: build
if: ${{ always() && needs.upload-to-s3.result == 'success' }} permissions:
uses: ./.github/workflows/appium-mobile-tests.yml contents: read
with: actions: read
app_url: ${{ needs.upload-to-s3.outputs.s3_url }} uses: ./.github/workflows/emulator_smoke_test.yml
test_grep: "android"
allure_job_run_id: ""
secrets:
WORKFLOW_TOKEN: ${{ secrets.PAT_TOKEN }}
ALLURE_TOKEN: ${{ secrets.ALLURE_TOKEN }}