Skip to content

Commit fbd04c5

Browse files
committed
fix(ci): cold-launch warmup + ios deep-link timeout tolerance
Android side: artifact screenshots from yesterday's gate-run failure showed the AuthView fully rendered with the email field's placeholder text on screen, but Maestro's accessibility-tree query for it timed out after 25s. That's a known cold-emulator quirk — the screen renders before the a11y tree is fully populated, and whichever flow happens to run first eats the cost. Added a one-shot warmup against flows/common/_warmup.yaml before the per-flow xargs loop so the JS bundle and a11y tree are primed when the real flows start. Also added `| sort` to the find pipeline so flow ordering is deterministic across runs. iOS side: `expo run:ios` BUILT and INSTALLED successfully, then tried to deep-link com.<bundle>://expo-development-client/?url=http://<LAN-IP>:8081 to launch the dev launcher. On GitHub-hosted macos-15 runners the LAN IP is unreachable from the simulator and `xcrun simctl openurl` times out at 60s, exiting the expo CLI with code 1 even though the .app is sitting in DerivedData ready to use. We don't need the post-install launch (Maestro re-installs and opens the app cleanly later), so trap the exit code and let the .app-exists check below decide whether to proceed. Caught by today's manual full e2e run where the iOS job failed with "Operation timed out" right after "Build Succeeded".
1 parent d68f458 commit fbd04c5

2 files changed

Lines changed: 51 additions & 3 deletions

File tree

.github/workflows/mobile-e2e.yml

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -563,7 +563,9 @@ jobs:
563563
cd integration/mobile &&
564564
excluded="$EXCLUDE_TAGS,iosOnly,flakyAndroid" &&
565565
pattern="$(echo "$excluded" | sed 's/,/|/g')" &&
566+
( maestro test --flatten-debug-output flows/common/_warmup.yaml || true ) &&
566567
find flows -type f -name '*.yaml' ! -path '*/common/*' ${FLOWS_FILTER:+-path "*$FLOWS_FILTER*"}
568+
| sort
567569
| while read f; do grep -qE "^[[:space:]]*-[[:space:]]*(${pattern})[[:space:]]*$" "$f" || printf '%s\n' "$f"; done
568570
| xargs -n 1 maestro test --env CLERK_TEST_EMAIL="$CLERK_TEST_EMAIL" --env CLERK_TEST_PASSWORD="$CLERK_TEST_PASSWORD" --flatten-debug-output
569571
@@ -854,9 +856,26 @@ jobs:
854856
fi
855857
xcrun simctl boot "$SIM_UDID" 2>/dev/null || true
856858
xcrun simctl bootstatus "$SIM_UDID" -b
859+
# expo run:ios builds + installs + then tries to deep-link the app
860+
# via the dev-launcher URL (com.<bundle>://expo-development-client/
861+
# ?url=http://<LAN-IP>:8081). On GitHub-hosted macos-15 runners the
862+
# LAN IP is unreachable from the simulator and `xcrun simctl openurl`
863+
# times out at 60s, exiting expo run:ios with code 1 — even though
864+
# the build itself succeeded and the .app is sitting in DerivedData.
865+
# We don't need the post-install launch (Maestro re-installs and
866+
# opens the app cleanly later), so swallow the openurl failure and
867+
# let the .app-exists check below decide whether to proceed.
868+
set +e
857869
npx expo run:ios --device "$SIM_UDID" --configuration Release --no-bundler
870+
run_ios_rc=$?
871+
set -e
858872
app=$(find ~/Library/Developer/Xcode/DerivedData -name "clerknativequickstart.app" -path "*/Release-iphonesimulator/*" | head -1)
859-
if [ -z "$app" ]; then echo "::error::No .app found in DerivedData"; exit 1; fi
873+
if [ -z "$app" ]; then
874+
echo "::error::No .app found in DerivedData (expo run:ios exit=$run_ios_rc)"; exit 1
875+
fi
876+
if [ "$run_ios_rc" -ne 0 ]; then
877+
echo "expo run:ios exited with $run_ios_rc but the .app was built ($app); continuing with the cached copy."
878+
fi
860879
rm -rf /tmp/cached-clerknativequickstart.app
861880
cp -R "$app" /tmp/cached-clerknativequickstart.app
862881
ls -la /tmp/cached-clerknativequickstart.app | head
@@ -920,17 +939,26 @@ jobs:
920939
xcrun simctl spawn "$SIM_UDID" defaults write com.apple.keyboard.Prediction -bool NO || true
921940
xcrun simctl install "$SIM_UDID" /tmp/cached-clerknativequickstart.app
922941
cd integration/mobile
942+
# Cold-sim warmup: launch the app once and let the JS bundle parse
943+
# and the accessibility tree populate before the per-flow loop
944+
# runs. Without this, whichever flow happens to run first eats the
945+
# cold-start cost and consistently flakes its in-flow wait — even
946+
# though the screen is visually rendered, Maestro's text matcher
947+
# races the a11y tree on freshly-booted runners.
948+
maestro test --flatten-debug-output flows/common/_warmup.yaml || true
923949
# Maestro's `--exclude-tags` only filters when running a
924950
# DIRECTORY; with explicit file paths Maestro runs every file
925951
# regardless of tag. We want per-flow invocation (so one
926952
# crash/hang can't poison the rest) AND tag filtering, so
927953
# pre-filter the file list. macOS ships bash 3.2 (no mapfile),
928954
# so this uses a portable pipe: skip-messages go to stderr so
929-
# only kept paths flow into xargs.
955+
# only kept paths flow into xargs. The `sort` step keeps the
956+
# alphabetic order stable across runs so any future first-flow
957+
# quirk is easy to reproduce.
930958
excluded="$EXCLUDE_TAGS,androidOnly"
931959
pattern="$(echo "$excluded" | sed 's/,/|/g')"
932960
find flows -type f -name '*.yaml' ! -path '*/common/*' \
933-
${FLOWS_FILTER:+-path "*$FLOWS_FILTER*"} | \
961+
${FLOWS_FILTER:+-path "*$FLOWS_FILTER*"} | sort | \
934962
while IFS= read -r f; do
935963
if grep -qE "^[[:space:]]*-[[:space:]]*(${pattern})[[:space:]]*\$" "$f"; then
936964
echo "::group::Skip $f (matches excluded tag in ${excluded})" >&2
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Cold-emulator warmup. Not a test — just launches the app once and lets the
2+
# JS bundle parse and the accessibility tree populate before the real per-flow
3+
# loop runs. Fresh Android emulators (and to a lesser extent fresh iOS sims)
4+
# can take 40+ seconds on the first cold launch for the Clerk AuthView's text
5+
# fields to become matchable via Maestro. Without a warmup, whichever flow
6+
# happens to be alphabetically first pays that cost and consistently flakes
7+
# its in-flow wait.
8+
#
9+
# Invoked from .github/workflows/mobile-e2e.yml's "Run iOS e2e" / "Run Android
10+
# e2e" steps before the xargs loop. The leading underscore is intentional —
11+
# the workflow's per-flow find excludes flows/common/ anyway, but the prefix
12+
# makes it obvious this isn't a real test if anyone runs maestro test on the
13+
# directory by hand.
14+
appId: com.clerk.clerkexpoquickstart
15+
---
16+
- launchApp:
17+
clearState: true
18+
- extendedWaitUntil:
19+
visible: '(Welcome! Sign in to continue\.?|Sign Out)'
20+
timeout: 90000

0 commit comments

Comments
 (0)