Skip to content

Commit 66fd3ce

Browse files
committed
ci: extract local runners into reusable actions
1 parent 26a9642 commit 66fd3ce

3 files changed

Lines changed: 241 additions & 81 deletions

File tree

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
name: 'Appium E2E - Local Android'
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.'
3+
4+
inputs:
5+
app-path:
6+
description: 'Absolute path to the .apk to install on the emulator'
7+
required: true
8+
sdk-type:
9+
description: 'SDK type passed to the tests (flutter, react-native, capacitor, unity, cordova, dotnet, android, etc.)'
10+
required: true
11+
api-level:
12+
description: 'Android API level for the AVD'
13+
required: false
14+
default: '36'
15+
arch:
16+
description: 'AVD architecture'
17+
required: false
18+
default: 'x86_64'
19+
profile:
20+
description: 'AVD device profile (as listed by `avdmanager list device`)'
21+
required: false
22+
default: 'medium_phone'
23+
onesignal-app-id:
24+
description: 'OneSignal App ID for the test app'
25+
required: true
26+
onesignal-api-key:
27+
description: 'OneSignal REST API key for the test app'
28+
required: true
29+
appium-port:
30+
description: 'Port Appium listens on'
31+
required: false
32+
default: '4723'
33+
34+
runs:
35+
using: composite
36+
steps:
37+
- name: Setup Bun
38+
uses: oven-sh/setup-bun@v2
39+
40+
- name: Enable KVM
41+
shell: bash
42+
run: |
43+
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
44+
sudo udevadm control --reload-rules
45+
sudo udevadm trigger --name-match=kvm
46+
47+
- name: Install test dependencies
48+
shell: bash
49+
working-directory: ${{ github.action_path }}/../../../appium
50+
run: bun install --frozen-lockfile
51+
52+
- name: Resolve appium dir
53+
shell: bash
54+
run: |
55+
APPIUM_DIR=$(cd "${{ github.action_path }}/../../../appium" && pwd -P)
56+
echo "APPIUM_DIR=$APPIUM_DIR" >> "$GITHUB_ENV"
57+
58+
- name: Run Appium tests on emulator
59+
uses: reactivecircus/android-emulator-runner@v2
60+
env:
61+
SDK_TYPE: ${{ inputs.sdk-type }}
62+
PLATFORM: android
63+
APP_PATH: ${{ inputs.app-path }}
64+
ONESIGNAL_APP_ID: ${{ inputs.onesignal-app-id }}
65+
ONESIGNAL_API_KEY: ${{ inputs.onesignal-api-key }}
66+
APPIUM_PORT: ${{ inputs.appium-port }}
67+
with:
68+
api-level: ${{ inputs.api-level }}
69+
target: google_apis
70+
arch: ${{ inputs.arch }}
71+
profile: ${{ inputs.profile }}
72+
force-avd-creation: false
73+
disable-animations: true
74+
ram-size: 4096M
75+
heap-size: 1024M
76+
emulator-boot-timeout: 900
77+
emulator-options: -no-window -no-boot-anim -gpu swiftshader_indirect
78+
working-directory: ${{ env.APPIUM_DIR }}
79+
script: |
80+
bun add -g appium@3
81+
appium driver install uiautomator2
82+
appium --port "$APPIUM_PORT" --log-level error > appium.log 2>&1 &
83+
for i in {1..30}; do
84+
if curl -sf "http://localhost:$APPIUM_PORT/status" >/dev/null; then
85+
break
86+
fi
87+
sleep 1
88+
done
89+
if ! curl -sf "http://localhost:$APPIUM_PORT/status" >/dev/null; then
90+
echo "::error::Appium did not become ready on port $APPIUM_PORT"
91+
cat appium.log || true
92+
exit 1
93+
fi
94+
bunx wdio run wdio.android.conf.ts
95+
96+
- name: Upload Appium + test results
97+
if: always()
98+
uses: actions/upload-artifact@v5
99+
with:
100+
name: appium-local-android-${{ inputs.sdk-type }}
101+
path: |
102+
${{ env.APPIUM_DIR }}/results/
103+
${{ env.APPIUM_DIR }}/appium.log
104+
if-no-files-found: ignore
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
name: 'Appium E2E - Local iOS'
2+
description: 'Boot an iPhone simulator, start Appium, and run the shared OneSignal Appium E2E tests against a local .app bundle.'
3+
4+
inputs:
5+
app-path:
6+
description: 'Absolute path to the simulator .app bundle or a .app.zip containing it'
7+
required: true
8+
sdk-type:
9+
description: 'SDK type passed to the tests (flutter, react-native, capacitor, unity, cordova, dotnet, ios, etc.)'
10+
required: true
11+
device:
12+
description: 'Simulator device name'
13+
required: false
14+
default: 'iPhone 17'
15+
os-version:
16+
description: 'Simulator iOS major version (matched against iOS-<version> in simctl runtimes)'
17+
required: false
18+
default: '26'
19+
onesignal-app-id:
20+
description: 'OneSignal App ID for the test app'
21+
required: true
22+
onesignal-api-key:
23+
description: 'OneSignal REST API key for the test app'
24+
required: true
25+
appium-port:
26+
description: 'Port Appium listens on'
27+
required: false
28+
default: '4723'
29+
30+
runs:
31+
using: composite
32+
steps:
33+
- name: Setup Bun
34+
uses: oven-sh/setup-bun@v2
35+
36+
- name: Resolve appium dir
37+
shell: bash
38+
run: |
39+
APPIUM_DIR=$(cd "${{ github.action_path }}/../../../appium" && pwd -P)
40+
echo "APPIUM_DIR=$APPIUM_DIR" >> "$GITHUB_ENV"
41+
42+
- name: Unpack .app bundle if zipped
43+
shell: bash
44+
env:
45+
APP_PATH_IN: ${{ inputs.app-path }}
46+
run: |
47+
if [[ "$APP_PATH_IN" == *.zip ]]; then
48+
DEST_DIR=$(dirname "$APP_PATH_IN")
49+
unzip -o -q "$APP_PATH_IN" -d "$DEST_DIR"
50+
RESOLVED="${APP_PATH_IN%.zip}"
51+
else
52+
RESOLVED="$APP_PATH_IN"
53+
fi
54+
if [ ! -d "$RESOLVED" ]; then
55+
echo "::error::Expected .app bundle directory at $RESOLVED"
56+
exit 1
57+
fi
58+
echo "APP_PATH=$RESOLVED" >> "$GITHUB_ENV"
59+
60+
- name: Boot iOS simulator
61+
shell: bash
62+
env:
63+
DEVICE: ${{ inputs.device }}
64+
OS_MAJOR: ${{ inputs.os-version }}
65+
run: |
66+
UDID=$(xcrun simctl list devices available -j | jq -r --arg dev "$DEVICE" --arg os "iOS-$OS_MAJOR" '
67+
.devices | to_entries[]
68+
| select(.key | contains($os))
69+
| .value[] | select(.name == $dev) | .udid' | head -1)
70+
if [ -z "$UDID" ]; then
71+
echo "::error::Simulator '$DEVICE' (iOS $OS_MAJOR) not found on this runner"
72+
xcrun simctl list devices available
73+
exit 1
74+
fi
75+
xcrun simctl boot "$UDID" 2>/dev/null || true
76+
xcrun simctl bootstatus "$UDID" -b
77+
78+
- name: Install Appium and driver
79+
shell: bash
80+
run: |
81+
bun add -g appium@3
82+
appium driver install xcuitest
83+
84+
- name: Start Appium
85+
shell: bash
86+
env:
87+
APPIUM_PORT: ${{ inputs.appium-port }}
88+
run: |
89+
appium --port "$APPIUM_PORT" --log-level error > appium.log 2>&1 &
90+
echo "APPIUM_PID=$!" >> "$GITHUB_ENV"
91+
for i in {1..30}; do
92+
if curl -sf "http://localhost:$APPIUM_PORT/status" >/dev/null; then
93+
echo "Appium ready"
94+
exit 0
95+
fi
96+
sleep 1
97+
done
98+
echo "::error::Appium did not become ready on port $APPIUM_PORT"
99+
cat appium.log || true
100+
exit 1
101+
102+
- name: Install test dependencies
103+
shell: bash
104+
working-directory: ${{ env.APPIUM_DIR }}
105+
run: bun install --frozen-lockfile
106+
107+
- name: Run Appium tests
108+
shell: bash
109+
working-directory: ${{ env.APPIUM_DIR }}
110+
env:
111+
SDK_TYPE: ${{ inputs.sdk-type }}
112+
PLATFORM: ios
113+
DEVICE: ${{ inputs.device }}
114+
OS_VERSION: ${{ inputs.os-version }}
115+
ONESIGNAL_APP_ID: ${{ inputs.onesignal-app-id }}
116+
ONESIGNAL_API_KEY: ${{ inputs.onesignal-api-key }}
117+
run: bunx wdio run wdio.ios.conf.ts
118+
119+
- name: Stop Appium
120+
if: always()
121+
shell: bash
122+
run: |
123+
if [ -n "${APPIUM_PID:-}" ]; then
124+
kill "$APPIUM_PID" 2>/dev/null || true
125+
fi
126+
127+
- name: Upload Appium + test results
128+
if: always()
129+
uses: actions/upload-artifact@v5
130+
with:
131+
name: appium-local-ios-${{ inputs.sdk-type }}
132+
path: |
133+
${{ env.APPIUM_DIR }}/results/
134+
appium.log
135+
if-no-files-found: ignore

.github/workflows/appium-e2e.yml

Lines changed: 2 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,6 @@ on:
1919
description: 'SDK type: react-native, flutter, capacitor, unity, cordova, dotnet, ios, or android'
2020
required: true
2121
type: string
22-
runner:
23-
description: 'Where to run: browserstack or local'
24-
required: false
25-
default: 'browserstack'
26-
type: string
2722
device:
2823
description: 'BrowserStack device name'
2924
required: false
@@ -39,9 +34,7 @@ on:
3934

4035
jobs:
4136
e2e:
42-
runs-on: >-
43-
${{ (inputs.runner == 'local' && inputs.platform == 'ios') && 'macos-26'
44-
|| 'ubuntu-latest' }}
37+
runs-on: ubuntu-latest
4538
steps:
4639
- name: Download app artifact
4740
uses: actions/download-artifact@v4
@@ -58,7 +51,6 @@ jobs:
5851
uses: oven-sh/setup-bun@v2
5952

6053
- name: Upload app to BrowserStack
61-
if: inputs.runner == 'browserstack'
6254
id: bs-upload
6355
env:
6456
BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }}
@@ -80,8 +72,7 @@ jobs:
8072
working-directory: _sdk-shared/appium
8173
run: bun install --frozen-lockfile
8274

83-
- name: Run Appium tests (BrowserStack)
84-
if: inputs.runner == 'browserstack'
75+
- name: Run Appium tests
8576
working-directory: _sdk-shared/appium
8677
env:
8778
BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }}
@@ -96,76 +87,6 @@ jobs:
9687
ONESIGNAL_API_KEY: ${{ secrets.APPIUM_ONESIGNAL_API_KEY }}
9788
run: bunx wdio run "wdio.${PLATFORM}.conf.ts"
9889

99-
- name: Enable KVM
100-
if: inputs.runner == 'local' && inputs.platform == 'android'
101-
run: |
102-
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
103-
sudo udevadm control --reload-rules
104-
sudo udevadm trigger --name-match=kvm
105-
106-
- name: Run Appium tests (local Android)
107-
if: inputs.runner == 'local' && inputs.platform == 'android'
108-
uses: reactivecircus/android-emulator-runner@v2
109-
env:
110-
SDK_TYPE: ${{ inputs.sdk-type }}
111-
PLATFORM: android
112-
APP_PATH: ${{ github.workspace }}/${{ inputs.app-filename }}
113-
ONESIGNAL_APP_ID: ${{ vars.APPIUM_ONESIGNAL_APP_ID }}
114-
ONESIGNAL_API_KEY: ${{ secrets.APPIUM_ONESIGNAL_API_KEY }}
115-
with:
116-
api-level: 36
117-
arch: x86_64
118-
profile: medium_phone
119-
force-avd-creation: false
120-
emulator-options: -no-window -no-boot-anim -gpu swiftshader_indirect
121-
working-directory: _sdk-shared/appium
122-
script: |
123-
bun add -g appium@3
124-
appium driver install uiautomator2
125-
appium --port 4723 --log-level error &
126-
for i in {1..30}; do
127-
curl -sf http://localhost:4723/status && break
128-
sleep 1
129-
done
130-
bunx wdio run wdio.android.conf.ts
131-
132-
- name: Boot iPhone 17 simulator
133-
if: inputs.runner == 'local' && inputs.platform == 'ios'
134-
run: |
135-
UDID=$(xcrun simctl list devices available -j | jq -r '
136-
.devices | to_entries[]
137-
| select(.key | contains("iOS-26"))
138-
| .value[] | select(.name == "iPhone 17") | .udid' | head -1)
139-
if [ -z "$UDID" ]; then
140-
echo "::error::iPhone 17 (iOS 26) simulator not found on this runner"
141-
xcrun simctl list devices available
142-
exit 1
143-
fi
144-
xcrun simctl boot "$UDID"
145-
xcrun simctl bootstatus "$UDID" -b
146-
147-
- name: Start Appium (local iOS)
148-
if: inputs.runner == 'local' && inputs.platform == 'ios'
149-
run: |
150-
bun add -g appium@3
151-
appium driver install xcuitest
152-
appium --port 4723 --log-level error &
153-
for i in {1..30}; do
154-
curl -sf http://localhost:4723/status && break
155-
sleep 1
156-
done
157-
158-
- name: Run Appium tests (local iOS)
159-
if: inputs.runner == 'local' && inputs.platform == 'ios'
160-
working-directory: _sdk-shared/appium
161-
env:
162-
SDK_TYPE: ${{ inputs.sdk-type }}
163-
PLATFORM: ios
164-
APP_PATH: ${{ github.workspace }}/${{ inputs.app-filename }}
165-
ONESIGNAL_APP_ID: ${{ vars.APPIUM_ONESIGNAL_APP_ID }}
166-
ONESIGNAL_API_KEY: ${{ secrets.APPIUM_ONESIGNAL_API_KEY }}
167-
run: bunx wdio run wdio.ios.conf.ts
168-
16990
- name: Upload test results
17091
if: always()
17192
uses: actions/upload-artifact@v4

0 commit comments

Comments
 (0)