name: Android E2E (CDP) # On-device end-to-end tests: boots an x86_64 Android emulator (KVM), installs # a debug APK, and runs the CDP-driven selection lane (pnpm test:android). # Not PR-blocking: runs nightly, on demand, or when a PR is labeled # `e2e-android`. on: workflow_dispatch: schedule: - cron: '30 19 * * *' pull_request: types: [labeled, synchronize] concurrency: group: android-e2e-${{ github.ref }} cancel-in-progress: true permissions: contents: read jobs: android-e2e: if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'e2e-android') runs-on: ubuntu-latest timeout-minutes: 90 steps: - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - name: initialize git submodules run: git submodule update --init --recursive - name: enable KVM for the emulator 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: setup pnpm uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6 - name: setup node uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: node-version: 24 cache: pnpm - name: setup Java uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 with: distribution: 'zulu' java-version: '17' - name: setup Android SDK uses: android-actions/setup-android@40fd30fb8d7440372e1316f5d1809ec01dcd3699 # v4 - name: install NDK run: sdkmanager "ndk;28.2.13676358" - name: install dependencies run: pnpm install --frozen-lockfile --prefer-offline - name: copy pdfjs-dist and simplecc-dist to public directory run: pnpm --filter @readest/readest-app setup-vendors - name: install Rust stable uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable with: targets: x86_64-linux-android - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 with: workspaces: apps/readest-app/src-tauri - name: create .env.local file for Next.js run: | echo "NEXT_PUBLIC_APP_PLATFORM=tauri" >> .env.local cp .env.local apps/readest-app/.env.local - name: build debug APK (x86_64) env: NDK_HOME: ${{ env.ANDROID_HOME }}/ndk/28.2.13676358 run: | cd apps/readest-app # Only the customized files of gen/android are tracked — regenerate # the gradle scaffolding, then restore the tracked customizations # (same flow as the release workflow). rm -rf src-tauri/gen/android pnpm tauri android init pnpm tauri icon ../../data/icons/readest-book.png git checkout . # Debug build: signed with the debug keystore, no release secrets # needed (gradle only loads keystore.properties when it exists). pnpm tauri android build --debug --target x86_64 APK=$(find src-tauri/gen/android/app/build/outputs/apk -name '*-debug.apk' | head -n 1) echo "APK=$PWD/$APK" >> "$GITHUB_ENV" test -n "$APK" - name: cache AVD snapshot uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 id: avd-cache with: path: | ~/.android/avd/* ~/.android/adb* key: avd-api-34 - name: create AVD snapshot for caching if: steps.avd-cache.outputs.cache-hit != 'true' uses: reactivecircus/android-emulator-runner@e89f39f1abbbd05b1113a29cf4db69e7540cae5a # v2 with: api-level: 34 arch: x86_64 target: google_apis force-avd-creation: false emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none disable-animations: false script: echo "AVD snapshot created" - name: run Android e2e lane uses: reactivecircus/android-emulator-runner@e89f39f1abbbd05b1113a29cf4db69e7540cae5a # v2 with: api-level: 34 arch: x86_64 target: google_apis force-avd-creation: false emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none disable-animations: true script: | adb install -r "$APK" cd apps/readest-app && pnpm test:android