From a49a463acfdfe6a0cd57fa5a971761df7d450993 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Sat, 20 Jun 2026 14:27:11 +0800 Subject: [PATCH] [Fix] (ci): split APK retrace AVD lifecycle - expose AVD create, launch, retrace, diagnostics, and stop as separate workflow steps - add bounded waits for emulator adb connection, boot, and trace replay execution - collect emulator diagnostics for APK retrace artifacts --- .github/workflows/apk.yml | 82 +++++++++---- android-plugin/run-avd-ci.sh | 222 +++++++++++++++++++++++------------ 2 files changed, 208 insertions(+), 96 deletions(-) diff --git a/.github/workflows/apk.yml b/.github/workflows/apk.yml index 14106c9e..b4f264ee 100644 --- a/.github/workflows/apk.yml +++ b/.github/workflows/apk.yml @@ -96,6 +96,9 @@ jobs: name: retrace (${{ matrix.backend.name }}, ${{ matrix.case.name }}) runs-on: ubuntu-latest needs: build + timeout-minutes: 75 + env: + AVD_NAME: mobilegl-${{ matrix.backend.id }} strategy: fail-fast: false max-parallel: 4 @@ -376,6 +379,8 @@ jobs: run: | echo "date_today=$(date +'%Y-%m-%d')" >> "$GITHUB_ENV" echo "short_sha=${GITHUB_SHA::7}" >> "$GITHUB_ENV" + echo "EMULATOR_LOG=${RUNNER_TEMP}/mobilegl-emulator.log" >> "$GITHUB_ENV" + echo "EMULATOR_PID_FILE=${RUNNER_TEMP}/mobilegl-emulator.pid" >> "$GITHUB_ENV" - name: Setup Android SDK uses: android-actions/setup-android@v3 @@ -393,36 +398,65 @@ jobs: sudo udevadm control --reload-rules sudo udevadm trigger --name-match=kvm - - name: Retrace and validate + - name: Create AVD run: | - apk_file="$(find android-retrace-apks -name '${{ matrix.backend.apk }}' -print -quit)" - sh android-plugin/run-avd-ci.sh \ + sh android-plugin/run-avd-ci.sh create \ --api-level 35 \ --target google_apis \ --arch x86_64 \ --profile pixel_6 \ + --avd-name "${AVD_NAME}" + + - name: Launch Emulator + run: | + sh android-plugin/run-avd-ci.sh start \ + --avd-name "${AVD_NAME}" \ --gpu "${{ matrix.backend.gpu }}" \ - --avd-name "mobilegl-${{ matrix.backend.id }}" \ - -- sh android-plugin/trace-replay-ci.sh \ - --apk-file "${apk_file}" \ - --package "${{ matrix.backend.package }}" \ - --backend "${{ matrix.backend.name }}" \ - --result-root android-retrace-result \ - --fixture-root android-retrace-fixture \ - --case "${{ matrix.case.name }}" \ - --trace-archive "${{ matrix.case.trace_archive }}" \ - --trace-file "${{ matrix.case.trace_file }}" \ - --golden "${{ matrix.case.golden }}" \ - --target-call "${{ matrix.case.target_call }}" \ - --width "${{ matrix.case.width }}" \ - --height "${{ matrix.case.height }}" \ - --tolerance "${{ matrix.case.tolerance }}" \ - --crop-x "${{ matrix.case.crop_x }}" \ - --crop-y "${{ matrix.case.crop_y }}" \ - --crop-width "${{ matrix.case.crop_width }}" \ - --crop-height "${{ matrix.case.crop_height }}" \ - --fuzz-percent "${{ matrix.case.fuzz_percent }}" \ - --timeout-seconds "${{ matrix.case.timeout_seconds }}" + --emulator-log "${EMULATOR_LOG}" \ + --pid-file "${EMULATOR_PID_FILE}" \ + --boot-timeout 300 + + - name: Retrace and validate + run: | + apk_file="$(find android-retrace-apks -name '${{ matrix.backend.apk }}' -print -quit)" + timeout "$(( ${{ matrix.case.timeout_seconds }} + 300 ))" sh android-plugin/trace-replay-ci.sh \ + --apk-file "${apk_file}" \ + --package "${{ matrix.backend.package }}" \ + --backend "${{ matrix.backend.name }}" \ + --result-root android-retrace-result \ + --fixture-root android-retrace-fixture \ + --case "${{ matrix.case.name }}" \ + --trace-archive "${{ matrix.case.trace_archive }}" \ + --trace-file "${{ matrix.case.trace_file }}" \ + --golden "${{ matrix.case.golden }}" \ + --target-call "${{ matrix.case.target_call }}" \ + --width "${{ matrix.case.width }}" \ + --height "${{ matrix.case.height }}" \ + --tolerance "${{ matrix.case.tolerance }}" \ + --crop-x "${{ matrix.case.crop_x }}" \ + --crop-y "${{ matrix.case.crop_y }}" \ + --crop-width "${{ matrix.case.crop_width }}" \ + --crop-height "${{ matrix.case.crop_height }}" \ + --fuzz-percent "${{ matrix.case.fuzz_percent }}" \ + --timeout-seconds "${{ matrix.case.timeout_seconds }}" + + - name: Collect emulator diagnostics + if: always() + run: | + mkdir -p android-retrace-result/diagnostics + adb devices -l > android-retrace-result/diagnostics/adb-devices.txt || true + timeout 30 adb logcat -d -t 1000 > android-retrace-result/diagnostics/logcat.txt || true + if [ -f "${EMULATOR_LOG}" ]; then + cp "${EMULATOR_LOG}" android-retrace-result/diagnostics/emulator.log + fi + + - name: Stop Emulator + if: always() + run: | + sh android-plugin/run-avd-ci.sh stop \ + --avd-name "${AVD_NAME}" \ + --emulator-log "${EMULATOR_LOG}" \ + --pid-file "${EMULATOR_PID_FILE}" - name: Upload Android retrace result if: always() diff --git a/android-plugin/run-avd-ci.sh b/android-plugin/run-avd-ci.sh index 7d61c79c..de928f19 100644 --- a/android-plugin/run-avd-ci.sh +++ b/android-plugin/run-avd-ci.sh @@ -4,14 +4,24 @@ set -eu usage() { cat <<'EOF' Usage: - sh android-plugin/run-avd-ci.sh \ + sh android-plugin/run-avd-ci.sh create \ --api-level N \ --target TARGET \ --arch ARCH \ --profile PROFILE \ - --gpu GPU \ + --avd-name NAME + + sh android-plugin/run-avd-ci.sh start \ --avd-name NAME \ - -- COMMAND [ARGS ...] + --gpu GPU \ + --emulator-log FILE \ + --pid-file FILE \ + --boot-timeout SECONDS + + sh android-plugin/run-avd-ci.sh stop \ + --avd-name NAME \ + --emulator-log FILE \ + --pid-file FILE EOF } @@ -36,80 +46,148 @@ require_value() { fi } -api_level="" -target="" -arch="" -profile="" -gpu="" -avd_name="" +print_diagnostics() { + emulator_log="$1" + adb devices -l >&2 || true + if [ -f "${emulator_log}" ]; then + echo "----- emulator log -----" >&2 + cat "${emulator_log}" >&2 || true + echo "----- end emulator log -----" >&2 + fi +} -while [ "$#" -gt 0 ]; do - case "$1" in - --api-level) api_level="$(next_arg "$@")"; shift 2 ;; - --target) target="$(next_arg "$@")"; shift 2 ;; - --arch) arch="$(next_arg "$@")"; shift 2 ;; - --profile) profile="$(next_arg "$@")"; shift 2 ;; - --gpu) gpu="$(next_arg "$@")"; shift 2 ;; - --avd-name) avd_name="$(next_arg "$@")"; shift 2 ;; - --) shift; break ;; - -h|--help) usage; exit 0 ;; - *) die "unknown argument: $1" ;; - esac -done +create_avd() { + api_level="" + target="" + arch="" + profile="" + avd_name="" -require_value "${api_level}" "--api-level" -require_value "${target}" "--target" -require_value "${arch}" "--arch" -require_value "${profile}" "--profile" -require_value "${gpu}" "--gpu" -require_value "${avd_name}" "--avd-name" + while [ "$#" -gt 0 ]; do + case "$1" in + --api-level) api_level="$(next_arg "$@")"; shift 2 ;; + --target) target="$(next_arg "$@")"; shift 2 ;; + --arch) arch="$(next_arg "$@")"; shift 2 ;; + --profile) profile="$(next_arg "$@")"; shift 2 ;; + --avd-name) avd_name="$(next_arg "$@")"; shift 2 ;; + -h|--help) usage; exit 0 ;; + *) die "unknown create argument: $1" ;; + esac + done + + require_value "${api_level}" "--api-level" + require_value "${target}" "--target" + require_value "${arch}" "--arch" + require_value "${profile}" "--profile" + require_value "${avd_name}" "--avd-name" + + system_image="system-images;android-${api_level};${target};${arch}" + sdkmanager "platform-tools" "emulator" "platforms;android-${api_level}" "${system_image}" + echo no | avdmanager create avd --force --name "${avd_name}" --package "${system_image}" --device "${profile}" +} + +start_avd() { + avd_name="" + gpu="" + emulator_log="" + pid_file="" + boot_timeout="" + + while [ "$#" -gt 0 ]; do + case "$1" in + --avd-name) avd_name="$(next_arg "$@")"; shift 2 ;; + --gpu) gpu="$(next_arg "$@")"; shift 2 ;; + --emulator-log) emulator_log="$(next_arg "$@")"; shift 2 ;; + --pid-file) pid_file="$(next_arg "$@")"; shift 2 ;; + --boot-timeout) boot_timeout="$(next_arg "$@")"; shift 2 ;; + -h|--help) usage; exit 0 ;; + *) die "unknown start argument: $1" ;; + esac + done + + require_value "${avd_name}" "--avd-name" + require_value "${gpu}" "--gpu" + require_value "${emulator_log}" "--emulator-log" + require_value "${pid_file}" "--pid-file" + require_value "${boot_timeout}" "--boot-timeout" + + mkdir -p "$(dirname "${emulator_log}")" "$(dirname "${pid_file}")" + emulator -avd "${avd_name}" \ + -no-window \ + -gpu "${gpu}" \ + -no-snapshot \ + -noaudio \ + -no-boot-anim \ + -camera-back none \ + -camera-front none \ + > "${emulator_log}" 2>&1 & + emulator_pid="$!" + echo "${emulator_pid}" > "${pid_file}" + + if ! timeout "${boot_timeout}" adb wait-for-device; then + print_diagnostics "${emulator_log}" + die "emulator did not connect to adb within ${boot_timeout}s" + fi + + booted="" + for _ in $(seq 1 "${boot_timeout}"); do + booted="$(adb shell getprop sys.boot_completed 2>/dev/null | tr -d '\r' || true)" + if [ "${booted}" = "1" ]; then + break + fi + sleep 1 + done + + if [ "${booted}" != "1" ]; then + print_diagnostics "${emulator_log}" + die "emulator did not boot within ${boot_timeout}s" + fi + + adb shell settings put global window_animation_scale 0 + adb shell settings put global transition_animation_scale 0 + adb shell settings put global animator_duration_scale 0 + adb shell input keyevent 82 || true +} + +stop_avd() { + avd_name="" + emulator_log="" + pid_file="" + + while [ "$#" -gt 0 ]; do + case "$1" in + --avd-name) avd_name="$(next_arg "$@")"; shift 2 ;; + --emulator-log) emulator_log="$(next_arg "$@")"; shift 2 ;; + --pid-file) pid_file="$(next_arg "$@")"; shift 2 ;; + -h|--help) usage; exit 0 ;; + *) die "unknown stop argument: $1" ;; + esac + done + + require_value "${avd_name}" "--avd-name" + require_value "${emulator_log}" "--emulator-log" + require_value "${pid_file}" "--pid-file" + + adb emu kill >/dev/null 2>&1 || true + if [ -f "${pid_file}" ]; then + emulator_pid="$(cat "${pid_file}")" + if [ -n "${emulator_pid}" ] && kill -0 "${emulator_pid}" 2>/dev/null; then + kill "${emulator_pid}" >/dev/null 2>&1 || true + fi + fi +} if [ "$#" -eq 0 ]; then die "missing command" fi -system_image="system-images;android-${api_level};${target};${arch}" -sdkmanager "platform-tools" "emulator" "platforms;android-${api_level}" "${system_image}" -echo no | avdmanager create avd --force --name "${avd_name}" --package "${system_image}" --device "${profile}" +command="$1" +shift -emulator_log="${RUNNER_TEMP:-.}/${avd_name}-emulator.log" -emulator -avd "${avd_name}" \ - -no-window \ - -gpu "${gpu}" \ - -no-snapshot \ - -noaudio \ - -no-boot-anim \ - -camera-back none \ - -camera-front none \ - > "${emulator_log}" 2>&1 & -emulator_pid="$!" - -cleanup() { - if kill -0 "${emulator_pid}" 2>/dev/null; then - adb -s emulator-5554 emu kill >/dev/null 2>&1 || kill "${emulator_pid}" >/dev/null 2>&1 || true - fi -} -trap cleanup EXIT INT TERM - -adb wait-for-device - -booted="" -for _ in $(seq 1 300); do - booted="$(adb shell getprop sys.boot_completed 2>/dev/null | tr -d '\r' || true)" - if [ "${booted}" = "1" ]; then - break - fi - sleep 1 -done - -if [ "${booted}" != "1" ]; then - cat "${emulator_log}" >&2 || true - die "emulator did not boot" -fi - -adb shell settings put global window_animation_scale 0 -adb shell settings put global transition_animation_scale 0 -adb shell settings put global animator_duration_scale 0 -adb shell input keyevent 82 || true - -"$@" +case "${command}" in + create) create_avd "$@" ;; + start) start_avd "$@" ;; + stop) stop_avd "$@" ;; + -h|--help) usage; exit 0 ;; + *) die "unknown command: ${command}" ;; +esac