Setting the file. One moment.
Snapshot iOS · Expo Skill Eval · expo/skills · Skills Docs
ContentsBack to the top of the page (opens in a new tab)
scripts/ snapshot-ios.sh
Shell · 132 lines · 5 KB
15 set -uo pipefail
16
17 PROJECT_PATH = " ${1 :? usage : snapshot-ios . sh < project-path > < output-png > [port] [settle-seconds] } "
18 OUT = " ${2 :? usage : snapshot-ios . sh < project-path > < output-png > [port] [settle-seconds] } "
19 PORT = " ${3 :- 8081} "
20 SETTLE = " ${4 :- 8} "
21 RUNNER = "${ EXPO_SKILL_EVAL_RUNNER :- expo-go }" # expo-go | dev-build
22 if [[ " $RUNNER " == "dev-build" ]]; then
23 BUNDLE_TIMEOUT = "${ EXPO_SKILL_EVAL_BUNDLE_TIMEOUT :- 900 }" # native compile is slow
24 else
25 BUNDLE_TIMEOUT = "${ EXPO_SKILL_EVAL_BUNDLE_TIMEOUT :- 240 }"
26 fi
27 LOG = "${ OUT % . png }.metro.log"
28
29 mkdir -p "$( dirname " $OUT ")"
30
31 # Free the port up front in case a previous run (or a crash) left Metro on it,
32 # so `expo start` binds the port we expect instead of silently incrementing.
33 # Runs inside this script, so the eval harness never needs ad-hoc lsof/kill.
34 lsof -ti "tcp: $PORT " -sTCP:LISTEN 2> /dev/null | xargs kill -9 2> /dev/null || true
35
36 if ! xcrun simctl list devices booted | grep -q '(Booted)' ; then
37 UDID = "$( xcrun simctl list devices available | grep 'iPhone' | tail -1 | grep -oE '[0-9A-F-]{36}')"
38 if [[ -z " $UDID " ]]; then
39 echo "error: no available iPhone simulator found" >&2
40 exit 1
41 fi
42 echo "booting simulator $UDID ..." >&2
43 xcrun simctl boot " $UDID "
44 open -a Simulator
45 xcrun simctl bootstatus " $UDID "
46 fi
47
48 # expo-go: --ios installs Expo Go when missing; its own open is unreliable if a
49 # previous experience is foregrounded, so we relaunch deterministically below.
50 # dev-build: expo run:ios prebuilds + native-compiles a dev client, installs and
51 # launches it on the simulator, and starts Metro — no Expo Go involved.
52 if [[ " $RUNNER " == "dev-build" ]]; then
53 # Pass --port so Metro binds to $PORT and the health-check curl matches.
54 ( cd " $PROJECT_PATH " && exec env -u CI bunx expo run:ios --port " $PORT " ) < /dev/null > " $LOG " 2>&1 &
55 else
56 ( cd " $PROJECT_PATH " && exec env -u CI REACT_NATIVE_PACKAGER_HOSTNAME= 127.0.0.1 bunx expo start --port " $PORT " --ios ) < /dev/null > " $LOG " 2>&1 &
57 fi
58 METRO_PID = $!
59 cleanup () {
60 kill " $METRO_PID " 2> /dev/null
61 lsof -ti "tcp: $PORT " -sTCP:LISTEN 2> /dev/null | xargs kill -9 2> /dev/null || true
62 wait " $METRO_PID " 2> /dev/null
63 }
64 trap cleanup EXIT
65
66 DEADLINE = $(( SECONDS + BUNDLE_TIMEOUT ))
67 STATUS = 0
68
69 until curl -sf "http://localhost: $PORT /status" > /dev/null ; do
70 if ! kill -0 " $METRO_PID " 2> /dev/null || ((SECONDS > DEADLINE)); then
71 echo "error: Metro did not come up. Log tail:" >&2
72 tail -40 " $LOG " >&2
73 exit 1
74 fi
75 sleep 2
76 done
77 # Expo Go only: wait for Expo Go to install, then relaunch the experience
78 # deterministically. dev-build skips this — expo run:ios launches the dev client.
79 if [[ " $RUNNER " != "dev-build" ]]; then
80 until xcrun simctl get_app_container booted host.exp.Exponent > /dev/null 2>&1 ; do
81 if ((SECONDS > DEADLINE)); then
82 echo "error: Expo Go was not installed on the simulator. Log tail:" >&2
83 tail -40 " $LOG " >&2
84 exit 1
85 fi
86 sleep 2
87 done
88 # Terminate any running Expo Go so a stale experience can't stay foregrounded,
89 # then open the project URL explicitly.
90 xcrun simctl terminate booted host.exp.Exponent 2> /dev/null || true
91 sleep 2
92 xcrun simctl openurl booted "exp://127.0.0.1: $PORT "
93 fi
94
95 until grep -q 'Bundled' " $LOG " ; do
96 if ! kill -0 " $METRO_PID " 2> /dev/null ; then
97 echo "error: Metro exited before bundling. Log tail:" >&2
98 tail -40 " $LOG " >&2
99 STATUS = 1
100 break
101 fi
102 if ((SECONDS > DEADLINE)); then
103 echo "error: timed out after ${ BUNDLE_TIMEOUT }s waiting for bundle. Log tail:" >&2
104 tail -40 " $LOG " >&2
105 STATUS = 1
106 break
107 fi
108 sleep 2
109 done
110
111 # dev-build: after bundling, relaunch the app so it is reliably in the
112 # foreground. Use xcrun simctl launch (not openurl) to avoid the "Open in X?"
113 # confirmation dialog that xcrun simctl openurl triggers on the iOS Simulator.
114 # expo run:ios embeds the Metro URL in the build, so the dev client auto-connects.
115 if [[ " $RUNNER " == "dev-build" ]]; then
116 BUNDLE_ID = "$( node -e "try{var a=require(' $PROJECT_PATH /app.json');console.log((a.expo&&a.expo.ios&&a.expo.ios.bundleIdentifier)||'com.exposkilleval.fixture')}catch(e){console.log('com.exposkilleval.fixture')}" 2> /dev/null || echo 'com.exposkilleval.fixture')"
117 xcrun simctl terminate booted " $BUNDLE_ID " 2> /dev/null || true
118 sleep 2
119 xcrun simctl launch --terminate-running-process booted " $BUNDLE_ID " > /dev/null 2>&1 || true
120 sleep 3
121 fi
122
123 sleep " $SETTLE "
124 xcrun simctl io booted screenshot " $OUT " || STATUS = 1
125 # Raw simulator captures are 3x retina (~2600px tall) and overflow the eval
126 # viewer's window. Downscale to a review/grading-friendly size.
127 if [[ -f " $OUT " ]]; then
128 sips -Z "${ EXPO_SKILL_EVAL_MAX_DIM :- 600 }" " $OUT " > /dev/null 2>&1 || true
129 fi
130 echo "screenshot: $OUT "
131 echo "metro log: $LOG "
132 exit " $STATUS "