5252 env :
5353 INPUT_RUNNER : ${{ inputs.runner }}
5454 INPUT_PROJECTROOT : ${{ inputs.projectRoot }}
55+ HARNESS_AVD_CACHING : ${{ inputs.cacheAvd }}
5556 run : |
5657 node ${{ github.action_path }}/actions/shared/index.cjs
5758 - name : Verify native app input
@@ -72,34 +73,20 @@ runs:
7273 ${{ runner.os }}-metro-cache-
7374
7475 # ── iOS ──────────────────────────────────────────────────────────────────
75- - uses : futureware-tech/simulator-action@v4
76- if : fromJson(steps.load-config.outputs.config).platformId == 'ios'
77- with :
78- model : ${{ fromJson(steps.load-config.outputs.config).config.device.name }}
79- os : iOS
80- os_version : ${{ fromJson(steps.load-config.outputs.config).config.device.systemVersion }}
81- wait_for_boot : true
82- erase_before_boot : false
83- - name : Install app
84- if : fromJson(steps.load-config.outputs.config).platformId == 'ios'
85- shell : bash
86- working-directory : ${{ steps.load-config.outputs.projectRoot }}
87- run : |
88- xcrun simctl install booted ${{ inputs.app }}
76+ # iOS simulator boot and app installation are handled by Harness itself.
8977 # ── Android ──────────────────────────────────────────────────────────────
9078 - name : Verify Android config
91- if : fromJson(steps.load-config.outputs.config).platformId == 'android'
79+ if : ${{ fromJson(steps.load-config.outputs.config).platformId == 'android' && fromJson(steps.load-config.outputs.config).config.device.type == 'emulator' }}
9280 shell : bash
9381 run : |
94- CONFIG='${{ steps.load-config.outputs.config }}'
95- if [ -z "$CONFIG.config.device.avd" ] || [ "$CONFIG.config.device.avd" = "null" ]; then
82+ if [ '${{ fromJson(steps.load-config.outputs.config).config.device.avd }}' = 'null' ]; then
9683 echo "Error: AVD config is required for Android emulators"
9784 echo "Please define the 'avd' property in the runner config"
9885 exit 1
9986 fi
10087 - name : Get architecture of the runner
10188 id : arch
102- if : fromJson(steps.load-config.outputs.config).platformId == 'android'
89+ if : ${{ fromJson(steps.load-config.outputs.config).platformId == 'android' && fromJson(steps.load-config.outputs.config).config.device.type == 'emulator' }}
10390 shell : bash
10491 run : |
10592 case "${{ runner.arch }}" in
@@ -117,7 +104,7 @@ runs:
117104 ;;
118105 esac
119106 - name : Enable KVM group perms
120- if : fromJson(steps.load-config.outputs.config).platformId == 'android'
107+ if : ${{ fromJson(steps.load-config.outputs.config).platformId == 'android' && fromJson(steps.load-config.outputs.config).config.device.type == 'emulator' }}
121108 shell : bash
122109 run : |
123110 echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
@@ -126,47 +113,24 @@ runs:
126113 ls /dev/kvm
127114 - name : Compute AVD cache key
128115 id : avd-key
129- if : ${{ fromJson(steps.load-config.outputs.config).platformId == 'android' && fromJSON(inputs.cacheAvd) }}
116+ if : ${{ fromJson(steps.load-config.outputs.config).platformId == 'android' && fromJson(steps.load-config.outputs.config).config.device.type == 'emulator' && fromJson(steps.load-config.outputs.config).action.avdCachingEnabled }}
130117 shell : bash
131118 run : |
132- CONFIG ='${{ steps.load-config.outputs.config }}'
133- AVD_CONFIG =$(echo "$CONFIG " | jq -c '.config.device.avd' )
134- AVD_CONFIG_HASH=$(echo "$AVD_CONFIG" | sha256sum | cut -d' ' -f1)
119+ CACHE_CONFIG ='${{ toJson(fromJson( steps.load-config.outputs.config).action.avdCacheConfig) }}'
120+ AVD_CONFIG_HASH =$(printf '%s' "$CACHE_CONFIG " | sha256sum | cut -d' ' -f1 )
121+ AVD_NAME='${{ fromJson(steps.load-config.outputs.config).config.device.name }}'
135122 ARCH="${{ steps.arch.outputs.arch }}"
136- CACHE_KEY="avd-$ARCH-$AVD_CONFIG_HASH"
123+ CACHE_KEY="avd-$AVD_NAME-$ ARCH-$AVD_CONFIG_HASH"
137124 echo "key=$CACHE_KEY" >> $GITHUB_OUTPUT
138125 - name : Restore AVD cache
139126 uses : actions/cache/restore@v4
140127 id : avd-cache
141- if : ${{ fromJson(steps.load-config.outputs.config).platformId == 'android' && fromJSON(inputs.cacheAvd) }}
128+ if : ${{ fromJson(steps.load-config.outputs.config).platformId == 'android' && fromJson(steps.load-config.outputs.config).config.device.type == 'emulator' && fromJson(steps.load-config.outputs.config).action.avdCachingEnabled }}
142129 with :
143130 path : |
144131 ~/.android/avd
145132 ~/.android/adb*
146133 key : ${{ steps.avd-key.outputs.key }}
147- - name : Create AVD and generate snapshot for caching
148- if : ${{ fromJson(steps.load-config.outputs.config).platformId == 'android' && fromJSON(inputs.cacheAvd) && steps.avd-cache.outputs.cache-hit != 'true' }}
149- uses : reactivecircus/android-emulator-runner@v2
150- with :
151- api-level : ${{ fromJson(steps.load-config.outputs.config).config.device.avd.apiLevel }}
152- arch : ${{ steps.arch.outputs.arch }}
153- profile : ${{ fromJson(steps.load-config.outputs.config).config.device.avd.profile }}
154- disk-size : ${{ fromJson(steps.load-config.outputs.config).config.device.avd.diskSize }}
155- heap-size : ${{ fromJson(steps.load-config.outputs.config).config.device.avd.heapSize }}
156- force-avd-creation : false
157- avd-name : ${{ fromJson(steps.load-config.outputs.config).config.device.name }}
158- disable-animations : true
159- emulator-options : -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
160- script : echo "Generated AVD snapshot for caching."
161- - name : Save AVD cache
162- if : ${{ fromJson(steps.load-config.outputs.config).platformId == 'android' && fromJSON(inputs.cacheAvd) && steps.avd-cache.outputs.cache-hit != 'true' }}
163- uses : actions/cache/save@v4
164- with :
165- path : |
166- ~/.android/avd
167- ~/.android/adb*
168- key : ${{ steps.avd-key.outputs.key }}
169-
170134 # ── Web ──────────────────────────────────────────────────────────────────
171135 - name : Install Playwright Browsers
172136 if : fromJson(steps.load-config.outputs.config).platformId == 'web'
@@ -225,116 +189,27 @@ runs:
225189 fi
226190 - name : Run E2E tests
227191 id : run-tests
228- if : fromJson(steps.load-config.outputs.config).platformId != 'android'
229192 shell : bash
230193 working-directory : ${{ steps.load-config.outputs.projectRoot }}
231194 env :
232195 PRE_RUN_HOOK : ${{ inputs.preRunHook }}
233196 AFTER_RUN_HOOK : ${{ inputs.afterRunHook }}
234197 HARNESS_RUNNER : ${{ inputs.runner }}
198+ HARNESS_APP_PATH : ${{ inputs.app }}
199+ HARNESS_AVD_CACHING : ${{ inputs.cacheAvd }}
235200 run : |
236201 export HARNESS_PROJECT_ROOT="$PWD"
237202
238- if [ -n "$PRE_RUN_HOOK" ]; then
239- pre_hook_file="$(mktemp "${RUNNER_TEMP:-/tmp}/harness-pre-run.XXXXXX.sh")"
240- trap 'rm -f "$pre_hook_file"' EXIT
241- printf '%s\n' "$PRE_RUN_HOOK" > "$pre_hook_file"
242- chmod +x "$pre_hook_file"
243- bash "$pre_hook_file"
244- rm -f "$pre_hook_file"
245- trap - EXIT
246- fi
247-
248203 set +e
249204 ${{ steps.detect-pm.outputs.runner }}react-native-harness --harnessRunner ${{ inputs.runner }} ${{ inputs.harnessArgs }}
250205 harness_exit_code=$?
251206 set -e
252207
253- export HARNESS_EXIT_CODE="$harness_exit_code"
254- after_run_exit_code=0
255- if [ -n "$AFTER_RUN_HOOK" ]; then
256- after_hook_file="$(mktemp "${RUNNER_TEMP:-/tmp}/harness-after-run.XXXXXX.sh")"
257- trap 'rm -f "$after_hook_file"' EXIT
258- printf '%s\n' "$AFTER_RUN_HOOK" > "$after_hook_file"
259- chmod +x "$after_hook_file"
260- set +e
261- bash "$after_hook_file"
262- after_run_exit_code=$?
263- set -e
264- rm -f "$after_hook_file"
265- trap - EXIT
266- fi
267-
268208 echo "harness_exit_code=$harness_exit_code" >> "$GITHUB_OUTPUT"
269209
270210 if [ "$harness_exit_code" -ne 0 ]; then
271211 exit "$harness_exit_code"
272212 fi
273-
274- if [ "$after_run_exit_code" -ne 0 ]; then
275- exit "$after_run_exit_code"
276- fi
277- - name : Run E2E tests
278- id : run-tests-android
279- if : fromJson(steps.load-config.outputs.config).platformId == 'android'
280- uses : reactivecircus/android-emulator-runner@v2
281- env :
282- PRE_RUN_HOOK : ${{ inputs.preRunHook }}
283- AFTER_RUN_HOOK : ${{ inputs.afterRunHook }}
284- HARNESS_RUNNER : ${{ inputs.runner }}
285- # android-emulator-runner executes each script line via `sh -c`, so multi-line
286- # shell control flow must live in a separate bash script instead of `with.script`.
287- HARNESS_ANDROID_SESSION_SCRIPT : |-
288- export HARNESS_PROJECT_ROOT="$PWD"
289-
290- adb install -r "${{ inputs.app }}"
291-
292- if [ -n "$PRE_RUN_HOOK" ]; then
293- pre_hook_file="$(mktemp "${RUNNER_TEMP:-/tmp}/harness-pre-run.XXXXXX.sh")"
294- printf "%s\n" "$PRE_RUN_HOOK" > "$pre_hook_file"
295- chmod +x "$pre_hook_file"
296- bash "$pre_hook_file"
297- rm -f "$pre_hook_file"
298- fi
299-
300- set +e
301- ${{ steps.detect-pm.outputs.runner }}react-native-harness --harnessRunner "${{ inputs.runner }}" ${{ inputs.harnessArgs }}
302- harness_exit_code=$?
303- echo "harness_exit_code=$harness_exit_code" >> "$GITHUB_OUTPUT"
304- set -e
305-
306- export HARNESS_EXIT_CODE="$harness_exit_code"
307- after_run_exit_code=0
308- if [ -n "$AFTER_RUN_HOOK" ]; then
309- after_hook_file="$(mktemp "${RUNNER_TEMP:-/tmp}/harness-after-run.XXXXXX.sh")"
310- printf "%s\n" "$AFTER_RUN_HOOK" > "$after_hook_file"
311- chmod +x "$after_hook_file"
312- set +e
313- bash "$after_hook_file"
314- after_run_exit_code=$?
315- set -e
316- rm -f "$after_hook_file"
317- fi
318-
319- if [ "$harness_exit_code" -ne 0 ]; then
320- exit "$harness_exit_code"
321- fi
322-
323- if [ "$after_run_exit_code" -ne 0 ]; then
324- exit "$after_run_exit_code"
325- fi
326- with :
327- working-directory : ${{ steps.load-config.outputs.projectRoot }}
328- api-level : ${{ fromJson(steps.load-config.outputs.config).config.device.avd.apiLevel }}
329- arch : ${{ steps.arch.outputs.arch }}
330- force-avd-creation : false
331- avd-name : ${{ fromJson(steps.load-config.outputs.config).config.device.name }}
332- disable-animations : true
333- emulator-options : -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
334- # Keep `script` to a single line so the emulator action does not split our bash
335- # session apart before the hooks and Harness command run.
336- script : >-
337- harness_script_file="$(mktemp "${RUNNER_TEMP:-/tmp}/harness-android-run.XXXXXX.sh")"; printf '%s\n' "$HARNESS_ANDROID_SESSION_SCRIPT" > "$harness_script_file"; chmod +x "$harness_script_file"; bash "$harness_script_file"; status=$?; rm -f "$harness_script_file"; exit "$status"
338213 - name : Upload visual test artifacts
339214 if : always() && inputs.uploadVisualTestArtifacts == 'true'
340215 uses : actions/upload-artifact@v4
@@ -344,6 +219,14 @@ runs:
344219 ${{ steps.load-config.outputs.projectRoot }}/**/__image_snapshots__/**/*-diff.png
345220 ${{ steps.load-config.outputs.projectRoot }}/**/__image_snapshots__/**/*-actual.png
346221 if-no-files-found : ignore
222+ - name : Save AVD cache
223+ if : ${{ always() && fromJson(steps.load-config.outputs.config).platformId == 'android' && fromJson(steps.load-config.outputs.config).config.device.type == 'emulator' && fromJson(steps.load-config.outputs.config).action.avdCachingEnabled && steps.avd-cache.outputs.cache-hit != 'true' }}
224+ uses : actions/cache/save@v4
225+ with :
226+ path : |
227+ ~/.android/avd
228+ ~/.android/adb*
229+ key : ${{ steps.avd-key.outputs.key }}
347230 - name : Upload crash report artifacts
348231 if : always()
349232 uses : actions/upload-artifact@v4
0 commit comments