11name : ' Appium E2E - Local Android'
2- description : ' Boot an Android emulator, start Appium, and run the shared OneSignal Appium E2E tests against a local .apk. Uses raw sdkmanager/avdmanager/emulator commands so each phase is a separate, cacheable, debuggable step .'
2+ description : ' Boot an Android emulator via reactivecircus/android-emulator-runner , start Appium, and run the shared OneSignal Appium E2E tests against a local .apk.'
33
44inputs :
55 app-path :
@@ -34,7 +34,7 @@ inputs:
3434 description : ' Port Appium listens on'
3535 required : false
3636 default : ' 4723'
37- boot-timeout-seconds :
37+ emulator- boot-timeout :
3838 description : ' Max seconds to wait for emulator boot completion'
3939 required : false
4040 default : ' 900'
@@ -45,172 +45,115 @@ runs:
4545 - name : Setup Bun
4646 uses : oven-sh/setup-bun@v2
4747
48- - name : Resolve paths
48+ - name : Resolve appium dir
4949 shell : bash
5050 run : |
5151 APPIUM_DIR=$(cd "${{ github.action_path }}/../../../appium" && pwd -P)
5252 echo "APPIUM_DIR=$APPIUM_DIR" >> "$GITHUB_ENV"
5353
54- if [ -z "${ANDROID_HOME:-}" ]; then
55- if [ -n "${ANDROID_SDK_ROOT:-}" ]; then
56- ANDROID_HOME="$ANDROID_SDK_ROOT"
57- else
58- ANDROID_HOME="/usr/local/lib/android/sdk"
59- fi
60- echo "ANDROID_HOME=$ANDROID_HOME" >> "$GITHUB_ENV"
61- fi
62-
63- CMDLINE_BIN=$(ls -d "$ANDROID_HOME"/cmdline-tools/*/bin 2>/dev/null | tail -1 || true)
64- if [ -z "$CMDLINE_BIN" ]; then
65- echo "::error::No Android cmdline-tools found under $ANDROID_HOME/cmdline-tools/"
66- ls -la "$ANDROID_HOME/cmdline-tools" 2>/dev/null || true
67- exit 1
68- fi
69- echo "$CMDLINE_BIN" >> "$GITHUB_PATH"
70- echo "$ANDROID_HOME/emulator" >> "$GITHUB_PATH"
71- echo "$ANDROID_HOME/platform-tools" >> "$GITHUB_PATH"
72- echo "Using cmdline-tools at: $CMDLINE_BIN"
73-
7454 - name : Enable KVM
7555 shell : bash
7656 run : |
7757 echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
7858 sudo udevadm control --reload-rules
7959 sudo udevadm trigger --name-match=kvm
8060
81- - name : Cache Android system image
61+ - name : AVD cache
8262 uses : actions/cache@v4
63+ id : avd-cache
8364 with :
84- path : ${{ env.ANDROID_HOME }}/system-images/android-${{ inputs.api-level }}/${{ inputs.target }}/${{ inputs.arch }}
85- key : android-sysimg-${{ inputs.api-level }}-${{ inputs.target }}-${{ inputs.arch }}
86-
87- - name : Install SDK packages
88- shell : bash
89- env :
90- API_LEVEL : ${{ inputs.api-level }}
91- TARGET : ${{ inputs.target }}
92- ARCH : ${{ inputs.arch }}
93- run : |
94- yes | sdkmanager --licenses >/dev/null 2>&1 || true
95- sdkmanager --install \
96- "platforms;android-$API_LEVEL" \
97- "system-images;android-$API_LEVEL;$TARGET;$ARCH" \
98- "emulator" \
99- "platform-tools" > /tmp/sdkmanager.log 2>&1 || {
100- echo "::error::sdkmanager install failed"
101- tail -40 /tmp/sdkmanager.log
102- exit 1
103- }
104-
105- - name : Create AVD
106- shell : bash
107- env :
108- API_LEVEL : ${{ inputs.api-level }}
109- TARGET : ${{ inputs.target }}
110- ARCH : ${{ inputs.arch }}
111- PROFILE : ${{ inputs.profile }}
112- run : |
113- echo "no" | avdmanager create avd \
114- -n test \
115- -k "system-images;android-$API_LEVEL;$TARGET;$ARCH" \
116- -d "$PROFILE" \
117- --force
118-
119- - name : Boot emulator
120- shell : bash
121- env :
122- BOOT_TIMEOUT : ${{ inputs.boot-timeout-seconds }}
123- run : |
124- nohup "$ANDROID_HOME/emulator/emulator" \
125- -avd test \
126- -no-window -no-audio -no-boot-anim \
127- -gpu swiftshader_indirect \
128- -no-snapshot-save \
129- -memory 4096 \
130- > emulator.log 2>&1 &
131- echo "EMULATOR_PID=$!" >> "$GITHUB_ENV"
132-
133- adb wait-for-device
134-
135- echo "Waiting for boot completion (timeout ${BOOT_TIMEOUT}s)..."
136- deadline=$(( $(date +%s) + BOOT_TIMEOUT ))
137- while :; do
138- boot=$(adb shell getprop sys.boot_completed 2>/dev/null | tr -d '\r' || true)
139- if [ "$boot" = "1" ]; then
140- break
141- fi
142- if [ "$(date +%s)" -ge "$deadline" ]; then
143- echo "::error::Emulator boot timed out"
144- echo "--- emulator.log (last 100 lines) ---"
145- tail -100 emulator.log || true
146- exit 1
147- fi
148- sleep 2
149- done
150- adb shell input keyevent 82 >/dev/null 2>&1 || true
151- echo "Emulator booted"
152-
153- - name : Install Appium and driver
154- shell : bash
155- run : |
156- bun add -g appium@3
157- appium driver install uiautomator2
158-
159- - name : Start Appium
160- shell : bash
161- env :
162- APPIUM_PORT : ${{ inputs.appium-port }}
163- run : |
164- appium --port "$APPIUM_PORT" --log-level error > appium.log 2>&1 &
165- echo "APPIUM_PID=$!" >> "$GITHUB_ENV"
166- i=0
167- while [ "$i" -lt 30 ]; do
168- if curl -sf "http://localhost:$APPIUM_PORT/status" >/dev/null; then
169- echo "Appium ready"
170- exit 0
171- fi
172- sleep 1
173- i=$((i + 1))
174- done
175- echo "::error::Appium did not become ready on port $APPIUM_PORT"
176- cat appium.log || true
177- exit 1
65+ path : |
66+ ~/.android/avd/*
67+ ~/.android/adb*
68+ key : avd-${{ inputs.api-level }}-${{ inputs.target }}-${{ inputs.arch }}-${{ inputs.profile }}
17869
179- - name : Install test dependencies
180- shell : bash
181- working-directory : ${{ env.APPIUM_DIR }}
182- run : bun install --frozen-lockfile
70+ - name : Create AVD snapshot
71+ if : steps.avd-cache.outputs.cache-hit != 'true'
72+ uses : reactivecircus/android-emulator-runner@v2
73+ with :
74+ api-level : ${{ inputs.api-level }}
75+ target : ${{ inputs.target }}
76+ arch : ${{ inputs.arch }}
77+ profile : ${{ inputs.profile }}
78+ ram-size : 4096M
79+ heap-size : 1024M
80+ disable-animations : false
81+ force-avd-creation : false
82+ emulator-options : -no-window -gpu swiftshader_indirect -no-boot-anim -no-snapshot-save
83+ emulator-boot-timeout : ${{ inputs.emulator-boot-timeout }}
84+ script : echo "Generated AVD snapshot for caching"
18385
18486 - name : Run Appium tests
185- shell : bash
186- working-directory : ${{ env.APPIUM_DIR }}
87+ uses : reactivecircus/android-emulator-runner@v2
18788 env :
188- SDK_TYPE : ${{ inputs.sdk-type }}
189- PLATFORM : android
89+ APPIUM_PORT : ${{ inputs.appium-port }}
19090 APP_PATH : ${{ inputs.app-path }}
91+ SDK_TYPE : ${{ inputs.sdk-type }}
19192 ONESIGNAL_APP_ID : ${{ inputs.onesignal-app-id }}
19293 ONESIGNAL_API_KEY : ${{ inputs.onesignal-api-key }}
193- run : bunx wdio run wdio.android.conf.ts
94+ with :
95+ api-level : ${{ inputs.api-level }}
96+ target : ${{ inputs.target }}
97+ arch : ${{ inputs.arch }}
98+ profile : ${{ inputs.profile }}
99+ ram-size : 4096M
100+ heap-size : 1024M
101+ disable-animations : true
102+ force-avd-creation : false
103+ emulator-options : -no-snapshot-save -no-window -gpu swiftshader_indirect -no-boot-anim
104+ emulator-boot-timeout : ${{ inputs.emulator-boot-timeout }}
105+ script : |
106+ set -e
107+ echo "::group::Install Appium + uiautomator2 driver"
108+ bun add -g appium@3
109+ appium driver install uiautomator2
110+ echo "::endgroup::"
111+
112+ echo "::group::Start Appium"
113+ appium --port "$APPIUM_PORT" --log-level error > "$GITHUB_WORKSPACE/appium.log" 2>&1 &
114+ APPIUM_PID=$!
115+ echo "APPIUM_PID=$APPIUM_PID"
116+
117+ i=0
118+ while [ "$i" -lt 60 ]; do
119+ if curl -sf "http://localhost:$APPIUM_PORT/status" >/dev/null; then
120+ echo "Appium ready"
121+ break
122+ fi
123+ sleep 1
124+ i=$((i + 1))
125+ done
126+ if ! curl -sf "http://localhost:$APPIUM_PORT/status" >/dev/null; then
127+ echo "::error::Appium did not become ready on port $APPIUM_PORT"
128+ tail -100 "$GITHUB_WORKSPACE/appium.log" || true
129+ exit 1
130+ fi
131+ echo "::endgroup::"
194132
195- - name : Shut down emulator and Appium
196- if : always()
197- shell : bash
198- run : |
199- adb emu kill 2>/dev/null || true
200- if [ -n "${APPIUM_PID:-}" ]; then
133+ echo "::group::Install test dependencies"
134+ cd "$APPIUM_DIR"
135+ bun install --frozen-lockfile
136+ echo "::endgroup::"
137+
138+ echo "::group::Run WDIO tests"
139+ set +e
140+ bunx wdio run wdio.android.conf.ts
141+ WDIO_EXIT=$?
142+ set -e
143+ echo "::endgroup::"
144+
145+ echo "::group::Stop Appium"
201146 kill "$APPIUM_PID" 2>/dev/null || true
202- fi
203- if [ -n "${EMULATOR_PID:-}" ]; then
204- kill "$EMULATOR_PID" 2>/dev/null || true
205- fi
147+ echo "::endgroup::"
148+
149+ exit "$WDIO_EXIT"
206150
207- - name : Upload Appium + emulator logs
151+ - name : Upload Appium + test results
208152 if : always()
209153 uses : actions/upload-artifact@v5
210154 with :
211155 name : appium-local-android-${{ inputs.sdk-type }}
212156 path : |
213157 ${{ env.APPIUM_DIR }}/results/
214- ${{ github.workspace }}/emulator.log
215158 ${{ github.workspace }}/appium.log
216159 if-no-files-found : ignore
0 commit comments