@deeeed/metamask-harness 0.34.0 → 0.34.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -0
- package/adapters/manifest.json +0 -8
- package/adapters/mobile/bridge-runtime/cdp-bridge.cjs +236 -3
- package/adapters/mobile/bridge-runtime/console-forwarder.cjs +22 -1
- package/adapters/mobile/bridge-runtime/lib/target-discovery.cjs +22 -15
- package/adapters/mobile/bridge-runtime/lib/ws-client.cjs +18 -0
- package/adapters/mobile/launch-metro.cjs +6 -4
- package/adapters/mobile/open-device.sh +170 -19
- package/adapters/mobile/start-metro.sh +165 -93
- package/adapters/mobile/stop-metro.sh +1 -0
- package/dist/adapters/mobile/prepare.js +1 -4
- package/dist/adapters/mobile/video-recorder.js +12 -10
- package/dist/adapters.js +134 -6
- package/dist/commands/launch/index.js +9 -0
- package/dist/commands/launch/mobile.js +17 -3
- package/dist/live-adapter-contract.js +10 -3
- package/dist/recipe-security.js +2 -0
- package/dist/runner.js +53 -8
- package/dist/runtime-context.js +1 -0
- package/library/actions/mobile/perps/measure_homepage_visible.mjs +4 -0
- package/library/actions/mobile/perps/performance-capture.mjs +579 -111
- package/library/actions/mobile/perps/perps.mjs +221 -38
- package/library/actions/mobile/perps/prepare_local_snapshot_endpoint.mjs +58 -0
- package/library/actions/mobile/platform/bridge.mjs +102 -8
- package/library/actions/mobile/wallet/ensure_unlocked.mjs +35 -8
- package/library/manifests/mobile.action-manifest.json +243 -511
- package/library/recipes/mobile/perps/performance.homepage.android-cold-disk-cache.recipe.json +20 -2
- package/library/recipes/mobile/perps/performance.homepage.android-cold-no-cache.recipe.json +2 -0
- package/library/recipes/mobile/perps/performance.homepage.cold-position-sample.recipe.json +120 -0
- package/library/recipes/mobile/perps/performance.homepage.ios-background-reconnect.recipe.json +7 -6
- package/library/recipes/mobile/perps/performance.homepage.ios-cold-no-cache.recipe.json +5 -5
- package/package.json +1 -1
- package/adapters/mobile/metro-config.cjs +0 -93
|
@@ -90,19 +90,29 @@ ios_app_installed() {
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
run_ios_native_build() (
|
|
93
|
-
local sim_target="$1"
|
|
94
|
-
|
|
93
|
+
local sim_target="$1" bundle_id="$2" reason="$3" build_env="" build_script="" build_type=""
|
|
94
|
+
case "$bundle_id" in
|
|
95
|
+
io.metamask.MetaMask|io.metamask) build_script="start:ios"; build_type="main" ;;
|
|
96
|
+
io.metamask.MetaMask-Flask) build_script="start:ios:flask"; build_type="flask" ;;
|
|
97
|
+
*)
|
|
98
|
+
printf 'open-device: no supported local build script for iOS bundle %s\n' "$bundle_id" >&2
|
|
99
|
+
return 1
|
|
100
|
+
;;
|
|
101
|
+
esac
|
|
102
|
+
printf '%s; installing with yarn %s (WATCHER_PORT=%s)\n' "$reason" "$build_script" "$PORT" >&2
|
|
95
103
|
build_env="$(mktemp "${TMPDIR:-/tmp}/mm-harness-ios-build-env.XXXXXX")" || return 1
|
|
96
104
|
chmod 600 "$build_env" || { rm -f "$build_env"; return 1; }
|
|
97
105
|
trap 'rm -f "$build_env"' EXIT HUP INT TERM
|
|
98
106
|
{
|
|
99
107
|
printf 'export MM_HARNESS_BUILD_WATCHER_PORT=%q\n' "$PORT"
|
|
100
108
|
printf 'export MM_HARNESS_BUILD_IOS_SIMULATOR=%q\n' "$sim_target"
|
|
109
|
+
printf 'export MM_HARNESS_BUILD_TYPE=%q\n' "$build_type"
|
|
101
110
|
cat <<'BUILD_ENV'
|
|
102
111
|
mm_harness_restore_build_target() {
|
|
103
112
|
export WATCHER_PORT="$MM_HARNESS_BUILD_WATCHER_PORT"
|
|
104
113
|
export METRO_PORT="$MM_HARNESS_BUILD_WATCHER_PORT"
|
|
105
114
|
export IOS_SIMULATOR="$MM_HARNESS_BUILD_IOS_SIMULATOR"
|
|
115
|
+
export METAMASK_BUILD_TYPE="$MM_HARNESS_BUILD_TYPE"
|
|
106
116
|
}
|
|
107
117
|
eval() {
|
|
108
118
|
builtin eval "$@"
|
|
@@ -122,7 +132,7 @@ BUILD_ENV
|
|
|
122
132
|
(
|
|
123
133
|
cd "$TARGET"
|
|
124
134
|
command -v activate_repo_ruby_best_effort >/dev/null 2>&1 && activate_repo_ruby_best_effort "$TARGET"
|
|
125
|
-
BASH_ENV="$build_env" WATCHER_PORT="$PORT" METRO_PORT="$PORT" IOS_SIMULATOR="$sim_target" EXPO_NO_TYPESCRIPT_SETUP=1 yarn
|
|
135
|
+
BASH_ENV="$build_env" WATCHER_PORT="$PORT" METRO_PORT="$PORT" IOS_SIMULATOR="$sim_target" METAMASK_BUILD_TYPE="$build_type" EXPO_NO_TYPESCRIPT_SETUP=1 yarn "$build_script" 2>&1
|
|
126
136
|
)
|
|
127
137
|
)
|
|
128
138
|
|
|
@@ -138,10 +148,16 @@ ensure_ios_app_for_mode() {
|
|
|
138
148
|
;;
|
|
139
149
|
auto|default)
|
|
140
150
|
if ios_app_installed "$sim_target" "$bundle_id"; then return 0; fi
|
|
141
|
-
run_ios_native_build "$sim_target" "iOS dev client ${bundle_id} not installed on ${sim_target}"
|
|
151
|
+
if ! run_ios_native_build "$sim_target" "$bundle_id" "iOS dev client ${bundle_id} not installed on ${sim_target}"; then
|
|
152
|
+
printf 'open-device: iOS native build failed\n' >&2
|
|
153
|
+
return 1
|
|
154
|
+
fi
|
|
142
155
|
;;
|
|
143
156
|
rebuild-native|clean)
|
|
144
|
-
run_ios_native_build "$sim_target" "Rebuilding iOS dev client ${bundle_id} (--preflight-mode ${mode})"
|
|
157
|
+
if ! run_ios_native_build "$sim_target" "$bundle_id" "Rebuilding iOS dev client ${bundle_id} (--preflight-mode ${mode})"; then
|
|
158
|
+
printf 'open-device: iOS native build failed\n' >&2
|
|
159
|
+
return 1
|
|
160
|
+
fi
|
|
145
161
|
;;
|
|
146
162
|
*)
|
|
147
163
|
printf 'open-device: unknown --preflight-mode: %s\n' "$mode" >&2; return 2 ;;
|
|
@@ -264,14 +280,113 @@ android_app_installed() {
|
|
|
264
280
|
"$ADB_BIN" "$@" shell pm list packages 2>/dev/null | grep -q "package:${pkg}"
|
|
265
281
|
}
|
|
266
282
|
|
|
267
|
-
run_android_native_build()
|
|
268
|
-
local
|
|
269
|
-
|
|
270
|
-
|
|
283
|
+
run_android_native_build() (
|
|
284
|
+
local serial="$1" expo_device="$2" pkg="$3" reason="$4" build_env="" build_script=""
|
|
285
|
+
case "$pkg" in
|
|
286
|
+
io.metamask) build_script="start:android" ;;
|
|
287
|
+
io.metamask.flask) build_script="start:android:flask" ;;
|
|
288
|
+
*)
|
|
289
|
+
printf 'open-device: no supported local build script for Android package %s\n' "$pkg" >&2
|
|
290
|
+
return 1
|
|
291
|
+
;;
|
|
292
|
+
esac
|
|
293
|
+
printf '%s; installing with yarn %s (WATCHER_PORT=%s)\n' "$reason" "$build_script" "$PORT" >&2
|
|
294
|
+
build_env="$(mktemp "${TMPDIR:-/tmp}/mm-harness-android-build-env.XXXXXX")" || return 1
|
|
295
|
+
chmod 600 "$build_env" || { rm -f "$build_env"; return 1; }
|
|
296
|
+
trap 'rm -f "$build_env"' EXIT HUP INT TERM
|
|
297
|
+
{
|
|
298
|
+
printf 'export MM_HARNESS_BUILD_ANDROID_SERIAL=%q\n' "$serial"
|
|
299
|
+
printf 'export MM_HARNESS_BUILD_ANDROID_EXPO_DEVICE=%q\n' "$expo_device"
|
|
300
|
+
printf 'export MM_HARNESS_BUILD_WATCHER_PORT=%q\n' "$PORT"
|
|
301
|
+
cat <<'BUILD_ENV'
|
|
302
|
+
mm_harness_restore_android_build_target() {
|
|
303
|
+
export WATCHER_PORT="$MM_HARNESS_BUILD_WATCHER_PORT"
|
|
304
|
+
export METRO_PORT="$MM_HARNESS_BUILD_WATCHER_PORT"
|
|
305
|
+
export ADB_SERIAL="$MM_HARNESS_BUILD_ANDROID_SERIAL"
|
|
306
|
+
export ANDROID_SERIAL="$MM_HARNESS_BUILD_ANDROID_SERIAL"
|
|
307
|
+
}
|
|
308
|
+
eval() {
|
|
309
|
+
builtin eval "$@"
|
|
310
|
+
local status=$?
|
|
311
|
+
mm_harness_restore_android_build_target
|
|
312
|
+
return "$status"
|
|
313
|
+
}
|
|
314
|
+
source() {
|
|
315
|
+
builtin source "$@"
|
|
316
|
+
local status=$?
|
|
317
|
+
mm_harness_restore_android_build_target
|
|
318
|
+
return "$status"
|
|
319
|
+
}
|
|
320
|
+
yarn() {
|
|
321
|
+
if [ "${1:-}" = expo ] && [ "${2:-}" = run:android ]; then
|
|
322
|
+
command yarn "$@" "$MM_HARNESS_BUILD_ANDROID_EXPO_DEVICE"
|
|
323
|
+
else
|
|
324
|
+
command yarn "$@"
|
|
325
|
+
fi
|
|
326
|
+
}
|
|
327
|
+
mm_harness_restore_android_build_target
|
|
328
|
+
BUILD_ENV
|
|
329
|
+
} > "$build_env"
|
|
330
|
+
(
|
|
331
|
+
cd "$TARGET"
|
|
332
|
+
BASH_ENV="$build_env" \
|
|
333
|
+
WATCHER_PORT="$PORT" METRO_PORT="$PORT" \
|
|
334
|
+
ADB_SERIAL="$serial" ANDROID_SERIAL="$serial" \
|
|
335
|
+
EXPO_NO_TYPESCRIPT_SETUP=1 yarn "$build_script" 2>&1
|
|
336
|
+
)
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
resolve_android_expo_device() {
|
|
340
|
+
local serial="$1" rows="" selected_line="" expo_device="" row_serial="" row_state="" row_name="" matches=0
|
|
341
|
+
[ -n "$serial" ] || return 0
|
|
342
|
+
rows="$($ADB_BIN devices -l 2>/dev/null || true)"
|
|
343
|
+
selected_line="$(printf '%s\n' "$rows" | awk -v wanted="$serial" '$1 == wanted { print; exit }')"
|
|
344
|
+
if [ -z "$selected_line" ]; then
|
|
345
|
+
printf 'open-device: configured Android device %s is not attached\n' "$serial" >&2
|
|
346
|
+
return 1
|
|
347
|
+
fi
|
|
348
|
+
row_state="$(printf '%s\n' "$selected_line" | awk '{print $2}')"
|
|
349
|
+
if [ "$row_state" != "device" ]; then
|
|
350
|
+
printf 'open-device: configured Android device %s is not ready (state: %s)\n' "$serial" "$row_state" >&2
|
|
351
|
+
return 1
|
|
352
|
+
fi
|
|
353
|
+
if printf '%s' "$serial" | grep -q '^emulator-'; then
|
|
354
|
+
expo_device="$($ADB_BIN -s "$serial" emu avd name 2>/dev/null | tr -d '\r' | sed '/^OK$/d' | head -1 || true)"
|
|
355
|
+
else
|
|
356
|
+
expo_device="$(printf '%s\n' "$selected_line" | awk '{ for (i = 3; i <= NF; i += 1) if ($i ~ /^model:/) { sub(/^model:/, "", $i); print $i; exit } }')"
|
|
357
|
+
fi
|
|
358
|
+
[ -n "$expo_device" ] || expo_device="${ANDROID_TARGET_DEVICE_NAME:-}"
|
|
359
|
+
expo_device="${expo_device// /_}"
|
|
360
|
+
if [ -z "$expo_device" ]; then
|
|
361
|
+
printf 'open-device: could not resolve Expo device name for Android serial %s\n' "$serial" >&2
|
|
362
|
+
return 1
|
|
363
|
+
fi
|
|
364
|
+
|
|
365
|
+
while IFS= read -r selected_line; do
|
|
366
|
+
row_serial="$(printf '%s\n' "$selected_line" | awk '{print $1}')"
|
|
367
|
+
row_state="$(printf '%s\n' "$selected_line" | awk '{print $2}')"
|
|
368
|
+
[ "$row_state" = "device" ] || continue
|
|
369
|
+
if printf '%s' "$row_serial" | grep -q '^emulator-'; then
|
|
370
|
+
row_name="$($ADB_BIN -s "$row_serial" emu avd name 2>/dev/null | tr -d '\r' | sed '/^OK$/d' | head -1 || true)"
|
|
371
|
+
else
|
|
372
|
+
row_name="$(printf '%s\n' "$selected_line" | awk '{ for (i = 3; i <= NF; i += 1) if ($i ~ /^model:/) { sub(/^model:/, "", $i); print $i; exit } }')"
|
|
373
|
+
fi
|
|
374
|
+
row_name="${row_name// /_}"
|
|
375
|
+
[ "$row_name" = "$expo_device" ] && matches=$((matches + 1))
|
|
376
|
+
done <<EOF
|
|
377
|
+
$rows
|
|
378
|
+
EOF
|
|
379
|
+
if [ "$matches" -ne 1 ]; then
|
|
380
|
+
printf 'open-device: Expo device name %s is ambiguous across %s attached Android devices; Expo cannot select serial %s safely\n' \
|
|
381
|
+
"$expo_device" "$matches" "$serial" >&2
|
|
382
|
+
return 1
|
|
383
|
+
fi
|
|
384
|
+
printf '%s\n' "$expo_device"
|
|
271
385
|
}
|
|
272
386
|
|
|
273
387
|
ensure_android_app_for_mode() {
|
|
274
388
|
local pkg="$1" mode="$2"
|
|
389
|
+
local expo_device=""
|
|
275
390
|
shift 2
|
|
276
391
|
case "$mode" in
|
|
277
392
|
fast)
|
|
@@ -281,10 +396,26 @@ ensure_android_app_for_mode() {
|
|
|
281
396
|
;;
|
|
282
397
|
auto|default)
|
|
283
398
|
if android_app_installed "$pkg" "$@"; then return 0; fi
|
|
284
|
-
|
|
399
|
+
if [ -n "$ADB_SERIAL_ARG" ]; then
|
|
400
|
+
expo_device="$(resolve_android_expo_device "$ADB_SERIAL_ARG")" || return 1
|
|
401
|
+
fi
|
|
402
|
+
[ -n "$expo_device" ] || expo_device="${ANDROID_TARGET_DEVICE_NAME:-}"
|
|
403
|
+
expo_device="${expo_device// /_}"
|
|
404
|
+
if ! run_android_native_build "$ADB_SERIAL_ARG" "$expo_device" "$pkg" "Android dev client ${pkg} not installed"; then
|
|
405
|
+
printf 'open-device: Android native build failed\n' >&2
|
|
406
|
+
return 1
|
|
407
|
+
fi
|
|
285
408
|
;;
|
|
286
409
|
rebuild-native|clean)
|
|
287
|
-
|
|
410
|
+
if [ -n "$ADB_SERIAL_ARG" ]; then
|
|
411
|
+
expo_device="$(resolve_android_expo_device "$ADB_SERIAL_ARG")" || return 1
|
|
412
|
+
fi
|
|
413
|
+
[ -n "$expo_device" ] || expo_device="${ANDROID_TARGET_DEVICE_NAME:-}"
|
|
414
|
+
expo_device="${expo_device// /_}"
|
|
415
|
+
if ! run_android_native_build "$ADB_SERIAL_ARG" "$expo_device" "$pkg" "Rebuilding Android dev client ${pkg} (--preflight-mode ${mode})"; then
|
|
416
|
+
printf 'open-device: Android native build failed\n' >&2
|
|
417
|
+
return 1
|
|
418
|
+
fi
|
|
288
419
|
;;
|
|
289
420
|
*)
|
|
290
421
|
printf 'open-device: unknown --preflight-mode: %s\n' "$mode" >&2; return 2 ;;
|
|
@@ -301,8 +432,16 @@ PY
|
|
|
301
432
|
|
|
302
433
|
# --- dispatch -----------------------------------------------------------------
|
|
303
434
|
|
|
304
|
-
|
|
305
|
-
|
|
435
|
+
if [ -n "${IOS_BUNDLE_ID:-}" ]; then
|
|
436
|
+
BUNDLE_IDS=("$IOS_BUNDLE_ID")
|
|
437
|
+
else
|
|
438
|
+
BUNDLE_IDS=("io.metamask.MetaMask" "io.metamask" "io.metamask.MetaMask-Flask" "io.metamask.qa")
|
|
439
|
+
fi
|
|
440
|
+
if [ -n "${ANDROID_PACKAGE_ID:-}" ]; then
|
|
441
|
+
ANDROID_PKGS=("$ANDROID_PACKAGE_ID")
|
|
442
|
+
else
|
|
443
|
+
ANDROID_PKGS=("io.metamask" "io.metamask.flask" "io.metamask.qa")
|
|
444
|
+
fi
|
|
306
445
|
|
|
307
446
|
if [ "$PLATFORM" = "ios" ]; then
|
|
308
447
|
SIM_TARGET="$(sim_udid_for_target "$SIMULATOR")"
|
|
@@ -330,7 +469,12 @@ if [ "$PLATFORM" = "ios" ]; then
|
|
|
330
469
|
|
|
331
470
|
LAUNCHED=false
|
|
332
471
|
for BUNDLE_ID in "${BUNDLE_IDS[@]}"; do
|
|
333
|
-
ensure_ios_app_for_mode "$SIM_TARGET" "$BUNDLE_ID" "$PREFLIGHT_MODE"
|
|
472
|
+
if ! ensure_ios_app_for_mode "$SIM_TARGET" "$BUNDLE_ID" "$PREFLIGHT_MODE"; then
|
|
473
|
+
case "$PREFLIGHT_MODE" in
|
|
474
|
+
fast) continue ;;
|
|
475
|
+
*) exit 1 ;;
|
|
476
|
+
esac
|
|
477
|
+
fi
|
|
334
478
|
if $RESTART; then
|
|
335
479
|
xcrun simctl terminate "$SIM_TARGET" "$BUNDLE_ID" >/dev/null 2>&1 || true
|
|
336
480
|
printf 'Restarting iOS dev client %s on %s\n' "$BUNDLE_ID" "$SIM_TARGET" >&2
|
|
@@ -378,18 +522,25 @@ else
|
|
|
378
522
|
ADB_ARGS=()
|
|
379
523
|
[ -n "$ADB_SERIAL_ARG" ] && ADB_ARGS+=("-s" "$ADB_SERIAL_ARG")
|
|
380
524
|
|
|
381
|
-
# Reverse-tunnel Metro port so the device can reach localhost:PORT.
|
|
382
|
-
"$ADB_BIN" "${ADB_ARGS[@]}" reverse "tcp:${PORT}" "tcp:${PORT}" >/dev/null 2>&1 || \
|
|
383
|
-
printf 'open-device: adb reverse failed (non-fatal — device may already be reachable)\n' >&2
|
|
384
|
-
|
|
385
525
|
LAUNCHED=false
|
|
386
526
|
for PKG in "${ANDROID_PKGS[@]}"; do
|
|
387
|
-
ensure_android_app_for_mode "$PKG" "$PREFLIGHT_MODE" "${ADB_ARGS[@]}"
|
|
527
|
+
if ! ensure_android_app_for_mode "$PKG" "$PREFLIGHT_MODE" "${ADB_ARGS[@]}"; then
|
|
528
|
+
case "$PREFLIGHT_MODE" in
|
|
529
|
+
fast) continue ;;
|
|
530
|
+
*) exit 1 ;;
|
|
531
|
+
esac
|
|
532
|
+
fi
|
|
388
533
|
if $RESTART; then
|
|
389
534
|
"$ADB_BIN" "${ADB_ARGS[@]}" shell am force-stop "$PKG" >/dev/null 2>&1 || true
|
|
390
535
|
printf 'Restarting Android dev client %s\n' "$PKG" >&2
|
|
391
536
|
fi
|
|
392
537
|
|
|
538
|
+
# Installing/rebuilding a dev client can invalidate adb reverse mappings.
|
|
539
|
+
# Restore Metro reachability only after the app is ensured, immediately
|
|
540
|
+
# before opening the development-client URL.
|
|
541
|
+
"$ADB_BIN" "${ADB_ARGS[@]}" reverse "tcp:${PORT}" "tcp:${PORT}" >/dev/null 2>&1 || \
|
|
542
|
+
printf 'open-device: adb reverse failed (non-fatal — device may already be reachable)\n' >&2
|
|
543
|
+
|
|
393
544
|
ENCODED_URL="$(urlencode "http://localhost:${PORT}?disableOnboarding=1" 2>/dev/null \
|
|
394
545
|
|| printf 'http%%3A%%2F%%2Flocalhost%%3A%s%%3FdisableOnboarding%%3D1' "$PORT")"
|
|
395
546
|
DEEP_LINK="expo-metamask://expo-development-client/?url=${ENCODED_URL}"
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
# start-metro — ensure the Metro bundler is running for this MetaMask Mobile checkout.
|
|
3
3
|
#
|
|
4
4
|
# Checks if Metro is already running and owned by this checkout with required env vars.
|
|
5
|
-
# Clears stale or foreign listeners before starting. Uses
|
|
6
|
-
# yarn
|
|
7
|
-
#
|
|
5
|
+
# Clears stale or foreign listeners before starting. Uses the checkout's canonical
|
|
6
|
+
# yarn watch script and sets EXPO_NO_TYPESCRIPT_SETUP=1. Cache clearing is
|
|
7
|
+
# injected as Expo --clear without invoking Mobile's global watchman cleanup.
|
|
8
|
+
# Metro owns its default worker count; METRO_MAX_WORKERS is an explicit override.
|
|
8
9
|
#
|
|
9
10
|
# Inputs:
|
|
10
11
|
# --target <metamask-mobile dir> (default $PWD)
|
|
@@ -49,7 +50,7 @@ fi
|
|
|
49
50
|
# only ever inspect or signal a Metro listening on $PORT.
|
|
50
51
|
# shellcheck disable=SC1091
|
|
51
52
|
. "$SCRIPT_DIR/lib/metro-listener.sh"
|
|
52
|
-
MOBILE_METRO_REQUIRED_ENV="${MOBILE_METRO_REQUIRED_ENV:-MM_INFURA_PROJECT_ID
|
|
53
|
+
MOBILE_METRO_REQUIRED_ENV="${MOBILE_METRO_REQUIRED_ENV:-MM_INFURA_PROJECT_ID METAMASK_BUILD_TYPE METAMASK_ENVIRONMENT}"
|
|
53
54
|
|
|
54
55
|
# RECIPE_RUNTIME_DIR (relative, validated) makes runtime state per-run so jobs
|
|
55
56
|
# sharing one checkout do not collide on metro.log / metro.pid / metro.tmux.
|
|
@@ -57,39 +58,90 @@ LOG_DIR="$TARGET/$(recipe_runtime_dir)" || exit 1
|
|
|
57
58
|
mkdir -p "$LOG_DIR"
|
|
58
59
|
LOG_FILE="$LOG_DIR/metro.log"
|
|
59
60
|
PID_FILE="$LOG_DIR/metro.pid"
|
|
60
|
-
|
|
61
|
+
METRO_BUILD_ENV_FILE="$LOG_DIR/metro-build-env.sh"
|
|
61
62
|
|
|
62
63
|
# --- helpers ------------------------------------------------------------------
|
|
63
64
|
|
|
64
|
-
|
|
65
|
-
local
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
65
|
+
write_metro_build_env() {
|
|
66
|
+
local temporary name staged_name product_values build_type environment sentry_dsn clear_value=0
|
|
67
|
+
for name in $MOBILE_METRO_REQUIRED_ENV; do
|
|
68
|
+
if ! printf '%s' "$name" | grep -Eq '^[A-Za-z_][A-Za-z0-9_]*$'; then
|
|
69
|
+
printf 'start-metro: invalid required environment variable name: %s\n' "$name" >&2
|
|
70
|
+
return 1
|
|
71
|
+
fi
|
|
72
|
+
done
|
|
73
|
+
temporary="$(mktemp "$LOG_DIR/.metro-build-env.XXXXXX")" || return 1
|
|
74
|
+
chmod 600 "$temporary" || { rm -f "$temporary"; return 1; }
|
|
75
|
+
product_values="$(
|
|
76
|
+
cd "$TARGET" || exit 1
|
|
77
|
+
set -a
|
|
78
|
+
# shellcheck disable=SC1091
|
|
79
|
+
[ ! -f .js.env ] || eval "$(tr -d '\r' < .js.env)"
|
|
80
|
+
set +a
|
|
81
|
+
METAMASK_BUILD_TYPE="${METAMASK_BUILD_TYPE:-main}"
|
|
82
|
+
METAMASK_ENVIRONMENT="${METAMASK_ENVIRONMENT:-dev}"
|
|
83
|
+
printf 'build_type=%q\n' "$METAMASK_BUILD_TYPE"
|
|
84
|
+
printf 'environment=%q\n' "$METAMASK_ENVIRONMENT"
|
|
85
|
+
printf 'sentry_dsn=%q\n' "${MM_SENTRY_DSN:-${MM_SENTRY_DSN_DEV:-}}"
|
|
86
|
+
for name in $MOBILE_METRO_REQUIRED_ENV; do
|
|
87
|
+
printf 'required_%s=%q\n' "$name" "${!name-}"
|
|
88
|
+
done
|
|
89
|
+
)" || { rm -f "$temporary"; return 1; }
|
|
90
|
+
eval "$product_values"
|
|
91
|
+
[ "$CLEAR" = true ] && clear_value=1
|
|
92
|
+
{
|
|
93
|
+
printf 'export MM_HARNESS_METRO_WATCHER_PORT=%q\n' "$PORT"
|
|
94
|
+
printf 'export MM_HARNESS_METRO_BUILD_TYPE=%q\n' "$build_type"
|
|
95
|
+
printf 'export MM_HARNESS_METRO_ENVIRONMENT=%q\n' "$environment"
|
|
96
|
+
printf 'export MM_HARNESS_METRO_SENTRY_DSN=%q\n' "$sentry_dsn"
|
|
97
|
+
printf 'export MM_HARNESS_METRO_CLEAR=%q\n' "$clear_value"
|
|
98
|
+
cat <<'BUILD_ENV'
|
|
99
|
+
mm_harness_restore_metro_env() {
|
|
100
|
+
export WATCHER_PORT="$MM_HARNESS_METRO_WATCHER_PORT"
|
|
101
|
+
export METRO_PORT="$MM_HARNESS_METRO_WATCHER_PORT"
|
|
102
|
+
export METAMASK_BUILD_TYPE="$MM_HARNESS_METRO_BUILD_TYPE"
|
|
103
|
+
export METAMASK_ENVIRONMENT="$MM_HARNESS_METRO_ENVIRONMENT"
|
|
104
|
+
export MM_SENTRY_DSN="$MM_HARNESS_METRO_SENTRY_DSN"
|
|
105
|
+
BUILD_ENV
|
|
106
|
+
for name in $MOBILE_METRO_REQUIRED_ENV; do
|
|
107
|
+
staged_name="required_$name"
|
|
108
|
+
printf ' export %s=%q\n' "$name" "${!staged_name}"
|
|
109
|
+
done
|
|
110
|
+
for name in \
|
|
111
|
+
SEGMENT_PROXY_URL \
|
|
112
|
+
SEGMENT_WRITE_KEY \
|
|
113
|
+
SEGMENT_FLUSH_INTERVAL \
|
|
114
|
+
SEGMENT_FLUSH_EVENT_LIMIT
|
|
115
|
+
do
|
|
116
|
+
if declare -p "$name" >/dev/null 2>&1; then
|
|
117
|
+
printf ' export %s=%q\n' "$name" "${!name}"
|
|
118
|
+
fi
|
|
119
|
+
done
|
|
120
|
+
cat <<'BUILD_ENV'
|
|
121
|
+
}
|
|
122
|
+
eval() {
|
|
123
|
+
builtin eval "$@"
|
|
124
|
+
local status=$?
|
|
125
|
+
mm_harness_restore_metro_env
|
|
126
|
+
return "$status"
|
|
127
|
+
}
|
|
128
|
+
source() {
|
|
129
|
+
builtin source "$@"
|
|
130
|
+
local status=$?
|
|
131
|
+
mm_harness_restore_metro_env
|
|
132
|
+
return "$status"
|
|
133
|
+
}
|
|
134
|
+
yarn() {
|
|
135
|
+
if [ "$MM_HARNESS_METRO_CLEAR" = 1 ] && [ "${1:-}" = expo ] && [ "${2:-}" = start ]; then
|
|
136
|
+
command yarn "$@" --clear
|
|
72
137
|
else
|
|
73
|
-
|
|
138
|
+
command yarn "$@"
|
|
74
139
|
fi
|
|
75
140
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
temporary
|
|
80
|
-
chmod 600 "$temporary" || { rm -f "$temporary"; return 1; }
|
|
81
|
-
for name in \
|
|
82
|
-
SEGMENT_PROXY_URL \
|
|
83
|
-
SEGMENT_WRITE_KEY \
|
|
84
|
-
SEGMENT_FLUSH_INTERVAL \
|
|
85
|
-
SEGMENT_FLUSH_EVENT_LIMIT
|
|
86
|
-
do
|
|
87
|
-
if declare -p "$name" >/dev/null 2>&1; then
|
|
88
|
-
printf 'export %s=%q\n' "$name" "${!name}" >> "$temporary" \
|
|
89
|
-
|| { rm -f "$temporary"; return 1; }
|
|
90
|
-
fi
|
|
91
|
-
done
|
|
92
|
-
mv -f "$temporary" "$INLINE_ENV_FILE"
|
|
141
|
+
mm_harness_restore_metro_env
|
|
142
|
+
BUILD_ENV
|
|
143
|
+
} > "$temporary" || { rm -f "$temporary"; return 1; }
|
|
144
|
+
mv -f "$temporary" "$METRO_BUILD_ENV_FILE"
|
|
93
145
|
}
|
|
94
146
|
|
|
95
147
|
# Log-tail viewer window (session-safe): sourced so its run-owned-session rule is
|
|
@@ -99,37 +151,20 @@ write_inline_metro_env() {
|
|
|
99
151
|
|
|
100
152
|
write_metro_runner() {
|
|
101
153
|
local runner="$1"
|
|
102
|
-
local
|
|
103
|
-
|
|
154
|
+
local watcher_script="watch"
|
|
155
|
+
local worker_env_line='unset METRO_MAX_WORKERS'
|
|
156
|
+
if [ -n "$METRO_WORKERS" ]; then
|
|
157
|
+
worker_env_line="export METRO_MAX_WORKERS=$(printf '%q' "$METRO_WORKERS")"
|
|
158
|
+
fi
|
|
104
159
|
cat > "$runner" <<EOF
|
|
105
160
|
#!/usr/bin/env bash
|
|
106
161
|
set -uo pipefail
|
|
107
162
|
cd "$(printf '%q' "$TARGET")"
|
|
108
163
|
: > "$(printf '%q' "$LOG_FILE")"
|
|
109
|
-
set -a
|
|
110
|
-
[ ! -f .js.env ] || . ./.js.env
|
|
111
|
-
[ ! -f .env ] || . ./.env
|
|
112
|
-
[ ! -f .env.local ] || . ./.env.local
|
|
113
|
-
set +a
|
|
114
|
-
[ -n "\${METAMASK_BUILD_TYPE:-}" ] || export METAMASK_BUILD_TYPE=main
|
|
115
|
-
# build.sh remaps MM_SENTRY_DSN from MM_SENTRY_DSN_DEV for dev builds; quick-launch
|
|
116
|
-
# bypasses it, so without this Sentry never initializes in the dev client.
|
|
117
|
-
[ -n "\${MM_SENTRY_DSN:-}" ] || export MM_SENTRY_DSN="\${MM_SENTRY_DSN_DEV:-}"
|
|
118
|
-
[ ! -f $(printf '%q' "$INLINE_ENV_FILE") ] || {
|
|
119
|
-
. $(printf '%q' "$INLINE_ENV_FILE")
|
|
120
|
-
rm -f $(printf '%q' "$INLINE_ENV_FILE")
|
|
121
|
-
}
|
|
122
164
|
export EXPO_NO_TYPESCRIPT_SETUP=1
|
|
123
165
|
export WATCHER_PORT="$(printf '%q' "$PORT")" METRO_PORT="$(printf '%q' "$PORT")"
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
if [ -n "\${EXPO_OVERRIDE_METRO_CONFIG:-}" ]; then
|
|
127
|
-
export MM_HARNESS_METRO_BASE_CONFIG="\$EXPO_OVERRIDE_METRO_CONFIG"
|
|
128
|
-
else
|
|
129
|
-
export MM_HARNESS_METRO_BASE_CONFIG=$(printf '%q' "$TARGET/metro.config.js")
|
|
130
|
-
fi
|
|
131
|
-
export EXPO_OVERRIDE_METRO_CONFIG="$(printf '%q' "$SCRIPT_DIR/metro-config.cjs")"
|
|
132
|
-
yarn expo start --port "$(printf '%q' "$PORT")"$clear_flag 2>&1 | tee -a "$(printf '%q' "$LOG_FILE")"
|
|
166
|
+
$worker_env_line
|
|
167
|
+
BASH_ENV=$(printf '%q' "$METRO_BUILD_ENV_FILE") command yarn $(printf '%q' "$watcher_script") 2>&1 | tee -a "$(printf '%q' "$LOG_FILE")"
|
|
133
168
|
EOF
|
|
134
169
|
chmod +x "$runner"
|
|
135
170
|
}
|
|
@@ -180,64 +215,61 @@ fi
|
|
|
180
215
|
|
|
181
216
|
stop_metro_listener || exit 1
|
|
182
217
|
|
|
183
|
-
METRO_WORKERS="${METRO_MAX_WORKERS
|
|
218
|
+
METRO_WORKERS="${METRO_MAX_WORKERS:-}"
|
|
219
|
+
if [ -n "$METRO_WORKERS" ] && ! printf '%s' "$METRO_WORKERS" | grep -Eq '^[1-9][0-9]*$'; then
|
|
220
|
+
printf 'start-metro: METRO_MAX_WORKERS must be a positive integer, got %s\n' "$METRO_WORKERS" >&2
|
|
221
|
+
exit 2
|
|
222
|
+
fi
|
|
223
|
+
READY_TIMEOUT="${MOBILE_METRO_READY_TIMEOUT_SECONDS:-180}"
|
|
224
|
+
if ! printf '%s' "$READY_TIMEOUT" | grep -Eq '^[1-9][0-9]*$'; then
|
|
225
|
+
printf 'start-metro: MOBILE_METRO_READY_TIMEOUT_SECONDS must be a positive integer, got %s\n' "$READY_TIMEOUT" >&2
|
|
226
|
+
exit 2
|
|
227
|
+
fi
|
|
184
228
|
CLEAR_LABEL=""
|
|
185
229
|
[ "$CLEAR" = true ] && CLEAR_LABEL=", clear"
|
|
186
230
|
|
|
187
|
-
|
|
188
|
-
|
|
231
|
+
if [ -n "$METRO_WORKERS" ]; then
|
|
232
|
+
printf 'Starting Metro on port %s (workers=%s%s)\n' \
|
|
233
|
+
"$PORT" "$METRO_WORKERS" "$CLEAR_LABEL" >&2
|
|
234
|
+
else
|
|
235
|
+
printf 'Starting Metro on port %s (workers=Metro default%s)\n' \
|
|
236
|
+
"$PORT" "$CLEAR_LABEL" >&2
|
|
237
|
+
fi
|
|
189
238
|
printf '(Full log: %s — mm-harness logs | mm-harness logs --full)\n' "$LOG_FILE" >&2
|
|
190
239
|
|
|
191
|
-
|
|
192
|
-
printf 'start-metro: could not stage
|
|
240
|
+
write_metro_build_env || {
|
|
241
|
+
printf 'start-metro: could not stage the scoped Metro environment\n' >&2
|
|
193
242
|
exit 1
|
|
194
243
|
}
|
|
195
244
|
|
|
196
|
-
|
|
245
|
+
STARTED_METRO_PID=""
|
|
246
|
+
STARTED_METRO_TMUX=""
|
|
247
|
+
if start_metro_tmux; then
|
|
248
|
+
STARTED_METRO_TMUX="$(cat "$LOG_DIR/metro.tmux" 2>/dev/null || true)"
|
|
249
|
+
else
|
|
197
250
|
# Metro runs detached, writing to the log. The tmux window is a read-only tail.
|
|
198
251
|
(
|
|
199
252
|
cd "$TARGET"
|
|
200
253
|
: > "$LOG_FILE"
|
|
201
|
-
# Source product env files (provides MM_INFURA_PROJECT_ID and friends).
|
|
202
|
-
set -a
|
|
203
|
-
# shellcheck disable=SC1091
|
|
204
|
-
[ ! -f .js.env ] || . ./.js.env
|
|
205
|
-
# shellcheck disable=SC1091
|
|
206
|
-
[ ! -f .env ] || . ./.env
|
|
207
|
-
# shellcheck disable=SC1091
|
|
208
|
-
[ ! -f .env.local ] || . ./.env.local
|
|
209
|
-
set +a
|
|
210
|
-
# The quick-launch runs `expo start` directly, bypassing scripts/build.sh which
|
|
211
|
-
# passes the build type as an argument (start:ios = build.sh ios main dev). The
|
|
212
|
-
# fixture .js.env ships METAMASK_BUILD_TYPE="" (empty), which Metro's transform
|
|
213
|
-
# rejects. Default to the main dev client (what runway installs / launch targets)
|
|
214
|
-
# so already-installed slots work without re-syncing fixtures.
|
|
215
|
-
[ -n "${METAMASK_BUILD_TYPE:-}" ] || export METAMASK_BUILD_TYPE=main
|
|
216
|
-
# build.sh remaps MM_SENTRY_DSN from MM_SENTRY_DSN_DEV for dev builds (start:ios
|
|
217
|
-
# = build.sh ios main dev); quick-launch bypasses build.sh, so without this the
|
|
218
|
-
# dev client's MM_SENTRY_DSN stays empty and Sentry never initializes.
|
|
219
|
-
[ -n "${MM_SENTRY_DSN:-}" ] || export MM_SENTRY_DSN="${MM_SENTRY_DSN_DEV:-}"
|
|
220
|
-
[ ! -f "$INLINE_ENV_FILE" ] || {
|
|
221
|
-
# shellcheck disable=SC1090
|
|
222
|
-
. "$INLINE_ENV_FILE"
|
|
223
|
-
rm -f "$INLINE_ENV_FILE"
|
|
224
|
-
}
|
|
225
254
|
export EXPO_NO_TYPESCRIPT_SETUP=1
|
|
226
255
|
export WATCHER_PORT="${PORT}" METRO_PORT="${PORT}"
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
256
|
+
if [ -n "$METRO_WORKERS" ]; then
|
|
257
|
+
export METRO_MAX_WORKERS="${METRO_WORKERS}"
|
|
258
|
+
else
|
|
259
|
+
unset METRO_MAX_WORKERS
|
|
260
|
+
fi
|
|
231
261
|
launcher_args=(
|
|
232
262
|
--target "$TARGET"
|
|
233
263
|
--port "$PORT"
|
|
234
264
|
--log "$LOG_FILE"
|
|
235
265
|
--pid-file "$PID_FILE"
|
|
236
|
-
--
|
|
266
|
+
--build-env "$METRO_BUILD_ENV_FILE"
|
|
237
267
|
)
|
|
268
|
+
[ -z "$METRO_WORKERS" ] || launcher_args+=(--workers "$METRO_WORKERS")
|
|
238
269
|
[ "$CLEAR" = true ] && launcher_args+=(--clear)
|
|
239
270
|
node "$SCRIPT_DIR/launch-metro.cjs" "${launcher_args[@]}"
|
|
240
271
|
) || exit 1
|
|
272
|
+
STARTED_METRO_PID="$(cat "$PID_FILE" 2>/dev/null || true)"
|
|
241
273
|
|
|
242
274
|
start_viewer_window "$LOG_FILE"
|
|
243
275
|
fi
|
|
@@ -256,9 +288,11 @@ if [ -f "$LOG_TUI" ] && command -v node >/dev/null 2>&1 \
|
|
|
256
288
|
TAIL_PID=$!
|
|
257
289
|
fi
|
|
258
290
|
|
|
259
|
-
#
|
|
291
|
+
# Cold main bundles can spend more than 90 seconds initializing Metro before the
|
|
292
|
+
# status listener opens. Keep the wait bounded, but do not trigger a duplicate
|
|
293
|
+
# recovery process while the checkout-owned launch is still making progress.
|
|
260
294
|
READY=false
|
|
261
|
-
for _i in $(seq 1
|
|
295
|
+
for _i in $(seq 1 "$READY_TIMEOUT"); do
|
|
262
296
|
if metro_ready; then
|
|
263
297
|
READY=true
|
|
264
298
|
break
|
|
@@ -277,12 +311,50 @@ if [ "$READY" = true ]; then
|
|
|
277
311
|
exit 0
|
|
278
312
|
fi
|
|
279
313
|
|
|
314
|
+
cleanup_timed_out_metro() {
|
|
315
|
+
local pid pgid i group_alive=false owned_listener_alive=false listener_pid command
|
|
316
|
+
pid="$STARTED_METRO_PID"
|
|
317
|
+
# launch-metro creates a detached group whose PGID equals its initial child
|
|
318
|
+
# PID. The leader can exit before Expo, so probe the group rather than the PID.
|
|
319
|
+
pgid="$pid"
|
|
320
|
+
if [ -n "$pgid" ] && kill -0 -- "-$pgid" 2>/dev/null; then
|
|
321
|
+
printf 'start-metro: stopping timed-out Metro process group %s\n' "$pgid" >&2
|
|
322
|
+
kill -TERM -- "-$pgid" 2>/dev/null || true
|
|
323
|
+
for i in $(seq 1 20); do
|
|
324
|
+
kill -0 -- "-$pgid" 2>/dev/null || break
|
|
325
|
+
sleep 0.25
|
|
326
|
+
done
|
|
327
|
+
if kill -0 -- "-$pgid" 2>/dev/null; then
|
|
328
|
+
kill -KILL -- "-$pgid" 2>/dev/null || true
|
|
329
|
+
for i in $(seq 1 20); do
|
|
330
|
+
kill -0 -- "-$pgid" 2>/dev/null || break
|
|
331
|
+
sleep 0.25
|
|
332
|
+
done
|
|
333
|
+
fi
|
|
334
|
+
kill -0 -- "-$pgid" 2>/dev/null && group_alive=true
|
|
335
|
+
fi
|
|
336
|
+
if [ -n "$STARTED_METRO_TMUX" ] && command -v tmux >/dev/null 2>&1; then
|
|
337
|
+
tmux kill-window -t "$STARTED_METRO_TMUX" >/dev/null 2>&1 || true
|
|
338
|
+
fi
|
|
339
|
+
for listener_pid in $(metro_listener_pids); do
|
|
340
|
+
command="$(ps eww -p "$listener_pid" -o command= 2>/dev/null || true)"
|
|
341
|
+
case "$command" in *"$TARGET"*) owned_listener_alive=true ;; esac
|
|
342
|
+
done
|
|
343
|
+
if [ "$group_alive" = true ] || [ "$owned_listener_alive" = true ]; then
|
|
344
|
+
printf 'start-metro: timed-out checkout-owned process is still alive; retaining launch metadata for recovery\n' >&2
|
|
345
|
+
return 1
|
|
346
|
+
fi
|
|
347
|
+
rm -f "$PID_FILE" "$LOG_DIR/metro-launch.json" "$LOG_DIR/metro-build-env.sh" "$LOG_DIR/metro.tmux"
|
|
348
|
+
return 0
|
|
349
|
+
}
|
|
350
|
+
|
|
280
351
|
# Failure: dump log events for diagnosis.
|
|
281
352
|
if [ -f "$LOG_TUI" ] && command -v node >/dev/null 2>&1; then
|
|
282
353
|
node "$LOG_TUI" events --log "$LOG_FILE" --events 20 >&2 || true
|
|
283
354
|
else
|
|
284
355
|
tail -40 "$LOG_FILE" >&2 || true
|
|
285
356
|
fi
|
|
286
|
-
|
|
357
|
+
cleanup_timed_out_metro || true
|
|
358
|
+
printf 'start-metro: Metro did not become ready on port %s within %ss\n' "$PORT" "$READY_TIMEOUT" >&2
|
|
287
359
|
printf ' Next: check %s for errors, or run mm-harness logs\n' "$LOG_FILE" >&2
|
|
288
360
|
exit 1
|
|
@@ -62,6 +62,7 @@ fi
|
|
|
62
62
|
set +e
|
|
63
63
|
rm -f "$PID_FILE"
|
|
64
64
|
rm -f "$LOG_DIR/metro-launch.json"
|
|
65
|
+
rm -f "$LOG_DIR/metro-build-env.sh"
|
|
65
66
|
|
|
66
67
|
# start-metro runs a per-checkout console-forwarder holding the device debugger
|
|
67
68
|
# slot; Metro going down must take it too, or the orphan keeps polling and later
|
|
@@ -116,10 +116,7 @@ async function prepareMobile(target, opts = {}) {
|
|
|
116
116
|
);
|
|
117
117
|
}
|
|
118
118
|
const launch = await dispatchActionSequence(
|
|
119
|
-
withAppRestart(
|
|
120
|
-
launchActions(path.resolve(target)),
|
|
121
|
-
restartApp || iosAccessibilityChanged
|
|
122
|
-
),
|
|
119
|
+
withAppRestart(launchActions(path.resolve(target)), true),
|
|
123
120
|
target,
|
|
124
121
|
platform,
|
|
125
122
|
json,
|