@@ -33,6 +33,16 @@ inputs:
3333 required : false
3434 type : boolean
3535 default : ' true'
36+ preRunHook :
37+ description : Shell script to run in bash immediately before Harness starts
38+ required : false
39+ type : string
40+ default : ' '
41+ afterRunHook :
42+ description : Shell script to run in bash immediately after Harness finishes
43+ required : false
44+ type : string
45+ default : ' '
3646runs :
3747 using : ' composite'
3848 steps :
7686 working-directory : ${{ steps.load-config.outputs.projectRoot }}
7787 run : |
7888 xcrun simctl install booted ${{ inputs.app }}
79-
8089 # ── Android ──────────────────────────────────────────────────────────────
8190 - name : Verify Android config
8291 if : fromJson(steps.load-config.outputs.config).platformId == 'android'
@@ -215,14 +224,105 @@ runs:
215224 echo "runner=npx " >> $GITHUB_OUTPUT
216225 fi
217226 - name : Run E2E tests
227+ id : run-tests
218228 if : fromJson(steps.load-config.outputs.config).platformId != 'android'
219229 shell : bash
220230 working-directory : ${{ steps.load-config.outputs.projectRoot }}
221- run : ${{ steps.detect-pm.outputs.runner }}react-native-harness --harnessRunner ${{ inputs.runner }} ${{ inputs.harnessArgs }}
231+ env :
232+ PRE_RUN_HOOK : ${{ inputs.preRunHook }}
233+ AFTER_RUN_HOOK : ${{ inputs.afterRunHook }}
234+ HARNESS_RUNNER : ${{ inputs.runner }}
235+ run : |
236+ export HARNESS_PROJECT_ROOT="$PWD"
237+
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+
248+ set +e
249+ ${{ steps.detect-pm.outputs.runner }}react-native-harness --harnessRunner ${{ inputs.runner }} ${{ inputs.harnessArgs }}
250+ harness_exit_code=$?
251+ set -e
252+
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+
268+ echo "harness_exit_code=$harness_exit_code" >> "$GITHUB_OUTPUT"
269+
270+ if [ "$harness_exit_code" -ne 0 ]; then
271+ exit "$harness_exit_code"
272+ fi
273+
274+ if [ "$after_run_exit_code" -ne 0 ]; then
275+ exit "$after_run_exit_code"
276+ fi
222277 - name : Run E2E tests
223- id : run-tests
278+ id : run-tests-android
224279 if : fromJson(steps.load-config.outputs.config).platformId == 'android'
225280 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
226326 with :
227327 working-directory : ${{ steps.load-config.outputs.projectRoot }}
228328 api-level : ${{ fromJson(steps.load-config.outputs.config).config.device.avd.apiLevel }}
@@ -231,10 +331,10 @@ runs:
231331 avd-name : ${{ fromJson(steps.load-config.outputs.config).config.device.name }}
232332 disable-animations : true
233333 emulator-options : -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
234- script : |
235- echo $(pwd)
236- adb install -r ${{ inputs.app }}
237- ${{ steps.detect-pm.outputs.runner }}react-native- harness --harnessRunner ${{ inputs.runner }} ${{ inputs.harnessArgs }}
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"
238338 - name : Upload visual test artifacts
239339 if : always() && inputs.uploadVisualTestArtifacts == 'true'
240340 uses : actions/upload-artifact@v4
0 commit comments