@deeeed/metamask-harness 0.34.0 → 0.34.1
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 +16 -0
- package/adapters/manifest.json +0 -8
- package/adapters/mobile/bridge-runtime/cdp-bridge.cjs +14 -3
- package/adapters/mobile/bridge-runtime/lib/target-discovery.cjs +22 -15
- package/adapters/mobile/launch-metro.cjs +6 -4
- package/adapters/mobile/open-device.sh +164 -15
- package/adapters/mobile/start-metro.sh +165 -93
- package/adapters/mobile/stop-metro.sh +1 -0
- package/dist/adapters/mobile/video-recorder.js +12 -10
- package/dist/commands/launch/index.js +9 -0
- package/dist/commands/launch/mobile.js +1 -1
- package/dist/live-adapter-contract.js +7 -0
- package/library/actions/mobile/perps/performance-capture.mjs +83 -1
- package/library/actions/mobile/platform/bridge.mjs +64 -3
- package/library/actions/mobile/wallet/ensure_unlocked.mjs +30 -7
- package/library/manifests/mobile.action-manifest.json +18 -0
- 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/package.json +1 -1
- package/adapters/mobile/metro-config.cjs +0 -93
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.34.1 - 2026-08-09
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Launch Mobile Metro through the product's canonical watcher while preserving slot, build, Sentry, and Segment environment settings, applying cache clears only to the selected Metro instance, and removing the config override that could corrupt cold bundles.
|
|
10
|
+
- Pin native Mobile builds to the exact supported Main or Flask app identity and Android device, rejecting ambiguous physical-device names instead of building or launching on the wrong target.
|
|
11
|
+
- Clean up timed-out Metro process groups safely and require the cold-disk-cache Perps freshness gate to report the expected lifecycle.
|
|
12
|
+
- Allow performance recipes to enforce a first-fresh socket-to-subscriber latency budget for a stream and subscriber throttle, optionally scoped to a lifecycle while always reporting the observed lifecycle.
|
|
13
|
+
- Allow Mobile wallet unlock actions to use their declared target and unlock timeouts without the live-adapter process ending first.
|
|
14
|
+
- Remove Android's 180-second `screenrecord` cap so `--record-video=full-run` covers long recipe runs.
|
|
15
|
+
- Retry empty Metro target lists, derive the Android model pin from `ADB_SERIAL`, and use pin-aware wallet status while Hermes re-registers between recipe actions.
|
|
16
|
+
- Keep compound Mobile actions under one debugger lease so nested bridge calls cannot race another recipe action.
|
|
17
|
+
- Let Android `scroll_into_view` fall back to a scrollable ancestor when the target itself is not a scroll container.
|
|
18
|
+
- Leave Metro's worker count at its native default unless `METRO_MAX_WORKERS` is explicitly configured.
|
|
19
|
+
- Bound each Mobile wallet-status probe by its polling deadline, raise the default unlock verification window to 30 seconds, and give the Android cold-no-cache recipe an explicit cold-start allowance, preventing a successful physical-device unlock from being reported as a timeout.
|
|
20
|
+
|
|
5
21
|
## 0.34.0 - 2026-08-07
|
|
6
22
|
|
|
7
23
|
### Fixed
|
package/adapters/manifest.json
CHANGED
|
@@ -42,14 +42,6 @@
|
|
|
42
42
|
"inputs": "--target --port --log --pid-file [--workers] [--clear]",
|
|
43
43
|
"outputs": "Metro PID/launch metadata and redirected log; exit 0/1/2"
|
|
44
44
|
},
|
|
45
|
-
{
|
|
46
|
-
"id": "mobile/metro-config",
|
|
47
|
-
"entry": "adapters/mobile/metro-config.cjs",
|
|
48
|
-
"kind": "module",
|
|
49
|
-
"purpose": "Preserve the product Metro config while excluding harness-owned checkout temp artifacts.",
|
|
50
|
-
"inputs": "base Metro config; env MM_HARNESS_METRO_PROJECT_ROOT, MM_HARNESS_METRO_BASE_CONFIG",
|
|
51
|
-
"outputs": "merged Metro config with resolver.blockList for <projectRoot>/temp"
|
|
52
|
-
},
|
|
53
45
|
{
|
|
54
46
|
"id": "mobile/stop-metro",
|
|
55
47
|
"entry": "adapters/mobile/stop-metro.sh",
|
|
@@ -322,7 +322,11 @@ const COMMANDS = {
|
|
|
322
322
|
const optsJson = JSON.stringify({ testId, offset, animated, intoView });
|
|
323
323
|
// Try __AGENTIC__ bridge first, fall back to inline fiber walking
|
|
324
324
|
const expr = `(function() {
|
|
325
|
-
|
|
325
|
+
// The app helper implements generic scrolling by searching inside the
|
|
326
|
+
// selector fiber. For into-view requests the scroll container is commonly
|
|
327
|
+
// an ancestor (for example, a Wallet Home section row), so use the
|
|
328
|
+
// ancestor-aware bridge fallback below instead.
|
|
329
|
+
if (!${intoView} && globalThis.__AGENTIC__?.scrollView) return globalThis.__AGENTIC__.scrollView(${optsJson});
|
|
326
330
|
var hook = globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
|
327
331
|
if (!hook) return { ok: false, error: 'No React DevTools hook' };
|
|
328
332
|
var renderers = hook.renderers;
|
|
@@ -681,6 +685,11 @@ const BRIDGE_LOCK_FILE = path.join(
|
|
|
681
685
|
process.env.RECIPE_RUNTIME_DIR || path.join('temp', 'recipe', 'runtime'),
|
|
682
686
|
'cdp-bridge.lock',
|
|
683
687
|
);
|
|
688
|
+
const BRIDGE_LOCK_OWNER_PID = /^\d+$/.test(
|
|
689
|
+
process.env.CDP_BRIDGE_LOCK_OWNER_PID || '',
|
|
690
|
+
)
|
|
691
|
+
? process.env.CDP_BRIDGE_LOCK_OWNER_PID
|
|
692
|
+
: String(process.pid);
|
|
684
693
|
|
|
685
694
|
// Ownership semantics: last writer wins. Each bridge stamps its own pid; the
|
|
686
695
|
// forwarder honors the lock while the pid IN THE FILE is alive. Overlapping
|
|
@@ -691,7 +700,7 @@ let bridgeLockHeld = false;
|
|
|
691
700
|
|
|
692
701
|
function acquireBridgeLock() {
|
|
693
702
|
try {
|
|
694
|
-
fs.writeFileSync(BRIDGE_LOCK_FILE,
|
|
703
|
+
fs.writeFileSync(BRIDGE_LOCK_FILE, BRIDGE_LOCK_OWNER_PID);
|
|
695
704
|
bridgeLockHeld = true;
|
|
696
705
|
} catch {
|
|
697
706
|
// Lock is best-effort: a missing runtime dir must not break bridge commands.
|
|
@@ -765,7 +774,7 @@ Environment:
|
|
|
765
774
|
process.exit(0);
|
|
766
775
|
}
|
|
767
776
|
|
|
768
|
-
const handler = COMMANDS[command];
|
|
777
|
+
const handler = COMMANDS[command === 'status-selected' ? 'status' : command];
|
|
769
778
|
if (!handler) {
|
|
770
779
|
console.error(`Unknown command: ${command}`);
|
|
771
780
|
console.error(`Available: ${Object.keys(COMMANDS).join(', ')}`);
|
|
@@ -780,6 +789,8 @@ Environment:
|
|
|
780
789
|
const timeout = Number.parseInt(process.env.CDP_TIMEOUT || '5000', 10);
|
|
781
790
|
|
|
782
791
|
// `status` probes ALL connected targets so both platforms are visible.
|
|
792
|
+
// `status-selected` falls through to the normal pinned discovery path for
|
|
793
|
+
// action code that must survive one device's Hermes runtime rotation.
|
|
783
794
|
if (command === 'status') {
|
|
784
795
|
const { discoverAllTargets } = require('./lib/target-discovery.cjs');
|
|
785
796
|
const allTargets = await discoverAllTargets(port);
|
|
@@ -6,7 +6,12 @@ const { createWSClient } = require('./ws-client.cjs');
|
|
|
6
6
|
const { BRIDGE_ERROR_CODES, coded } = require('./bridge-errors.cjs');
|
|
7
7
|
|
|
8
8
|
const FETCH_TIMEOUT_MS = Number.parseInt(process.env.CDP_TIMEOUT || '30000', 10);
|
|
9
|
-
const
|
|
9
|
+
const DISCOVERY_RETRY_DELAY_MS = 500;
|
|
10
|
+
const FETCH_RETRIES = Number.parseInt(
|
|
11
|
+
process.env.CDP_DISCOVERY_RETRIES ||
|
|
12
|
+
String(Math.max(3, Math.ceil(FETCH_TIMEOUT_MS / DISCOVERY_RETRY_DELAY_MS))),
|
|
13
|
+
10,
|
|
14
|
+
);
|
|
10
15
|
|
|
11
16
|
/** Fetch JSON from a URL (http only, no external deps) */
|
|
12
17
|
function fetchJSONOnce(url) {
|
|
@@ -43,7 +48,9 @@ async function fetchJSON(url, acceptResponse) {
|
|
|
43
48
|
lastError = error;
|
|
44
49
|
}
|
|
45
50
|
if (attempt === FETCH_RETRIES) break;
|
|
46
|
-
await new Promise((resolve) =>
|
|
51
|
+
await new Promise((resolve) =>
|
|
52
|
+
setTimeout(resolve, DISCOVERY_RETRY_DELAY_MS),
|
|
53
|
+
);
|
|
47
54
|
}
|
|
48
55
|
if (lastError !== undefined) throw lastError;
|
|
49
56
|
if (lastResponse !== undefined) return lastResponse;
|
|
@@ -146,21 +153,21 @@ async function discoverTarget(port) {
|
|
|
146
153
|
let acceptedPinnedCandidate;
|
|
147
154
|
let targets;
|
|
148
155
|
try {
|
|
149
|
-
targets = await fetchJSON(
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
156
|
+
targets = await fetchJSON(listUrl, async (response) => {
|
|
157
|
+
const runtimeCandidates = rankRuntimeCandidates(response);
|
|
158
|
+
if (runtimeCandidates.length === 0) return false;
|
|
159
|
+
if (androidPinned) {
|
|
160
|
+
const pinnedCandidates = runtimeCandidates.filter(matchesAndroidPin);
|
|
161
|
+
for (const candidate of pinnedCandidates) {
|
|
162
|
+
if (await probeTarget(candidate.webSocketDebuggerUrl)) {
|
|
163
|
+
acceptedPinnedCandidate = candidate;
|
|
164
|
+
return true;
|
|
159
165
|
}
|
|
160
|
-
return false;
|
|
161
166
|
}
|
|
162
|
-
|
|
163
|
-
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
return true;
|
|
170
|
+
});
|
|
164
171
|
} catch (e) {
|
|
165
172
|
throw coded(
|
|
166
173
|
new Error(`Cannot reach Metro at ${listUrl}. Is Metro running?\n ${e.message}`),
|
|
@@ -26,10 +26,10 @@ function parseArgs(argv) {
|
|
|
26
26
|
async function main() {
|
|
27
27
|
const args = parseArgs(process.argv.slice(2));
|
|
28
28
|
if (args.help) {
|
|
29
|
-
console.log('Usage: launch-metro.cjs --target <path> --port <port> --log <path> --pid-file <path> [--workers <n>] [--clear]');
|
|
29
|
+
console.log('Usage: launch-metro.cjs --target <path> --port <port> --log <path> --pid-file <path> --build-env <path> [--workers <n>] [--clear]');
|
|
30
30
|
return;
|
|
31
31
|
}
|
|
32
|
-
for (const required of ['target', 'port', 'log', 'pid-file']) {
|
|
32
|
+
for (const required of ['target', 'port', 'log', 'pid-file', 'build-env']) {
|
|
33
33
|
if (!args[required]) throw new Error(`--${required} is required`);
|
|
34
34
|
}
|
|
35
35
|
const target = path.resolve(args.target);
|
|
@@ -38,9 +38,9 @@ async function main() {
|
|
|
38
38
|
fs.mkdirSync(path.dirname(log), { recursive: true });
|
|
39
39
|
fs.mkdirSync(path.dirname(pidFile), { recursive: true });
|
|
40
40
|
const logFd = fs.openSync(log, 'a');
|
|
41
|
-
const childArgs = ['
|
|
42
|
-
if (args.clear) childArgs.push('--clear');
|
|
41
|
+
const childArgs = ['watch'];
|
|
43
42
|
const env = { ...process.env };
|
|
43
|
+
env.BASH_ENV = args['build-env'];
|
|
44
44
|
if (args.workers) env.METRO_MAX_WORKERS = String(args.workers);
|
|
45
45
|
const child = spawn('yarn', childArgs, {
|
|
46
46
|
cwd: target,
|
|
@@ -59,10 +59,12 @@ async function main() {
|
|
|
59
59
|
`${JSON.stringify({
|
|
60
60
|
schemaVersion: 1,
|
|
61
61
|
pid: child.pid,
|
|
62
|
+
pgid: child.pid,
|
|
62
63
|
startedAt: new Date().toISOString(),
|
|
63
64
|
target,
|
|
64
65
|
port: Number(args.port),
|
|
65
66
|
clear: Boolean(args.clear),
|
|
67
|
+
command: `yarn ${childArgs[0]}`,
|
|
66
68
|
}, null, 2)}\n`,
|
|
67
69
|
);
|
|
68
70
|
child.unref();
|
|
@@ -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
|
|
@@ -384,7 +528,12 @@ else
|
|
|
384
528
|
|
|
385
529
|
LAUNCHED=false
|
|
386
530
|
for PKG in "${ANDROID_PKGS[@]}"; do
|
|
387
|
-
ensure_android_app_for_mode "$PKG" "$PREFLIGHT_MODE" "${ADB_ARGS[@]}"
|
|
531
|
+
if ! ensure_android_app_for_mode "$PKG" "$PREFLIGHT_MODE" "${ADB_ARGS[@]}"; then
|
|
532
|
+
case "$PREFLIGHT_MODE" in
|
|
533
|
+
fast) continue ;;
|
|
534
|
+
*) exit 1 ;;
|
|
535
|
+
esac
|
|
536
|
+
fi
|
|
388
537
|
if $RESTART; then
|
|
389
538
|
"$ADB_BIN" "${ADB_ARGS[@]}" shell am force-stop "$PKG" >/dev/null 2>&1 || true
|
|
390
539
|
printf 'Restarting Android dev client %s\n' "$PKG" >&2
|