stim 1.1.0 → 1.3.0
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/README.md +37 -0
- package/dist/{android-CC5bUer7.mjs → android-BW8i_YxU.mjs} +1 -1
- package/dist/android-BqVIJoG7.mjs +1368 -0
- package/dist/{android-DnZlT3aU.mjs → android-CqcW1hrP.mjs} +205 -58
- package/dist/{android-cas-DGlTStWu.mjs → android-cas-BR5YJBYv.mjs} +6 -6
- package/dist/android-cas-compiler.mjs +1 -1
- package/dist/{app-install-oZIQRbdD.mjs → app-install-DIp9riNx.mjs} +29 -21
- package/dist/{build-slots-CgacA9zQ.mjs → build-slots-C3-R4XQr.mjs} +3 -3
- package/dist/{cache-manifest-oeH18ERr.mjs → cache-manifest-CXR-Y8pH.mjs} +1 -1
- package/dist/cache-manifest.mjs +1 -1
- package/dist/cli.mjs +13 -13
- package/dist/collector-run.d.mts +4 -1
- package/dist/collector-run.mjs +36 -11
- package/dist/{config-D7uu8Gbq.mjs → config-CcefMY2Q.mjs} +94 -13
- package/dist/{deps-64wVnpZc.mjs → deps-Spz8ozgo.mjs} +2 -2
- package/dist/{dev-client-Bvky1hya.mjs → dev-client-BjisHDB5.mjs} +250 -10
- package/dist/{device-DJKv5Qej.mjs → device-BkiW-gkk.mjs} +19 -11
- package/dist/{device-lease-D-G3SgVe.mjs → device-lease-BJYybF-2.mjs} +107 -83
- package/dist/{device-pool-BlHXnYYs.mjs → device-pool-CPbLGfrB.mjs} +19 -13
- package/dist/{device-remote-CCgkG82J.mjs → device-remote-AkYCKS8U.mjs} +8 -8
- package/dist/{doctor-DlVxTtMq.mjs → doctor-Bmv86UUW.mjs} +18 -26
- package/dist/{doctor-BRF9aAJ3.mjs → doctor-fAMXC4vl.mjs} +10 -10
- package/dist/{error-diagnostics-B5IJwv3-.mjs → error-diagnostics-DHC31rXA.mjs} +65 -55
- package/dist/{exec-bsN9MJXb.mjs → exec-CyylIdq9.mjs} +4 -2
- package/dist/{gc-DhXdgSdt.mjs → gc-GGu-8ULN.mjs} +90 -67
- package/dist/{guide-DrhrDBo5.mjs → guide-92CPA21s.mjs} +414 -47
- package/dist/{ios-CK1Vrk6Q.mjs → ios-BSXFeO5H.mjs} +200 -59
- package/dist/{ios-BZxKPuqP.mjs → ios-Deh5js5V.mjs} +147 -40
- package/dist/{ios-device-CdSm0GDw.mjs → ios-device-CWOM_S44.mjs} +2 -2
- package/dist/{ios-device-CYbuRNXR.mjs → ios-device-D3pzTKQE.mjs} +1 -1
- package/dist/{logs-Dxoy16Qv.mjs → logs-DPNF4C9s.mjs} +9 -6
- package/dist/{logs-query-oqn47Ffx.mjs → logs-query-R-Cqc6Qv.mjs} +22 -5
- package/dist/{metro-B01H3EuD.mjs → metro-CORqXyTL.mjs} +3 -3
- package/dist/{ndjson-DcAtEx_K.mjs → ndjson-BZwI1NBv.mjs} +5 -2
- package/dist/{ownership-D8HmSv2E.mjs → ownership-8O073GIC.mjs} +3 -3
- package/dist/{ownership-B9QIdQOw.mjs → ownership-BpCKiRlQ.mjs} +4 -4
- package/dist/{ownership-claim-CeJLEpjD.mjs → ownership-claim-Bf2CAfw3.mjs} +4 -3
- package/dist/{project-COj6nzzh.mjs → project-fKmuIaMI.mjs} +2 -2
- package/dist/{reclaim-BPvN2HlP.mjs → reclaim-iW9Cad7k.mjs} +80 -73
- package/dist/{reload-DYZEBbZy.mjs → reload-DazHDSFA.mjs} +32 -24
- package/dist/{server-bare-BYHqEeyw.mjs → server-bare-DEzPZLvf.mjs} +7 -2
- package/dist/server-expo-44em5Pmg.mjs +2 -0
- package/dist/{server-expo-BEMr_m6p.mjs → server-expo-DizT4OMk.mjs} +5 -5
- package/dist/{settings-BSwrY4FZ.mjs → settings-DEJf6_kJ.mjs} +26 -5
- package/dist/slot-launch-cci0xvmC.mjs +19 -0
- package/dist/{start-ByCylYtB.mjs → start-mObjQH2W.mjs} +12 -12
- package/dist/{state-IYEY-zi8.mjs → state-B3XA2wp2.mjs} +9 -9
- package/dist/{stats-wqxPm7Tv.mjs → stats-BR-kwj0B.mjs} +1 -1
- package/dist/{stats-DZ4Q5J-7.mjs → stats-CaaQr4yN.mjs} +3 -3
- package/dist/{status-Dia0XTqG.mjs → status-BxQNqu9t.mjs} +86 -25
- package/dist/{stop-D6o5zAdm.mjs → stop-CrHGcjrV.mjs} +2 -2
- package/dist/{stop-BApyaF21.mjs → stop-DimGXEKY.mjs} +93 -42
- package/dist/supervisor-run.d.mts +1 -2
- package/dist/supervisor-run.mjs +5 -5
- package/dist/{remote-cache-DOi5-zLj.mjs → teardown-ClFp1Aax.mjs} +889 -830
- package/dist/{workspace-process-lock-CxOO9-pf.mjs → workspace-process-lock-3NcQnZ0X.mjs} +1 -1
- package/dist/{worktree-DxEmUI_n.mjs → worktree-DxTgN1CJ.mjs} +16 -16
- package/dist/{worktree-Dx3ZC5lS.mjs → worktree-Eq6TLH8N.mjs} +7 -3
- package/dist/{xcode-CDB6fytw.mjs → xcode-D7sAp-Zy.mjs} +256 -103
- package/package.json +5 -5
- package/shim/bundle-response.cjs +5 -0
- package/dist/android-xmyGBNy5.mjs +0 -665
- package/dist/build-lock-vE45ZzJZ.mjs +0 -645
- package/dist/server-expo-rb0k-0w7.mjs +0 -2
|
@@ -1,34 +1,35 @@
|
|
|
1
|
-
import { t as getExecutor } from "./exec-
|
|
2
|
-
import { C as withConfigLock, S as upsertProject, f as getProject, g as loadConfig, l as getConcurrencyLimits, y as saveConfig } from "./config-
|
|
3
|
-
import { r as createNdjsonWriter } from "./ndjson-
|
|
4
|
-
import { D as workspaceLogsDir, T as workspaceDir, _ as writeWorkspaceState, g as writeWorkspaceLaunch, n as MODE_EXPO, p as readWorkspaceState, t as MODE_BARE, v as emulatorLogFile } from "./state-
|
|
5
|
-
import { C as removeTemporaryEntry, S as makeTemporaryDirectory, g as repoRoot, s as gitCommonDir } from "./worktree-
|
|
6
|
-
import { a as findProjectRoot, c as projectShortcut, i as detectIsExpo, n as detectAndroidPackage, r as detectBundleId, t as appProjectProblem } from "./project-
|
|
1
|
+
import { t as getExecutor } from "./exec-CyylIdq9.mjs";
|
|
2
|
+
import { C as withConfigLock, I as deviceSlotKey, L as deviceSlotPlatforms, S as upsertProject, V as validateDeviceSlot, b as setDevice, f as getProject, g as loadConfig, l as getConcurrencyLimits, y as saveConfig } from "./config-CcefMY2Q.mjs";
|
|
3
|
+
import { r as createNdjsonWriter } from "./ndjson-BZwI1NBv.mjs";
|
|
4
|
+
import { D as workspaceLogsDir, T as workspaceDir, _ as writeWorkspaceState, g as writeWorkspaceLaunch, n as MODE_EXPO, p as readWorkspaceState, t as MODE_BARE, v as emulatorLogFile } from "./state-B3XA2wp2.mjs";
|
|
5
|
+
import { C as removeTemporaryEntry, S as makeTemporaryDirectory, g as repoRoot, s as gitCommonDir } from "./worktree-Eq6TLH8N.mjs";
|
|
6
|
+
import { a as findProjectRoot, c as projectShortcut, i as detectIsExpo, n as detectAndroidPackage, r as detectBundleId, t as appProjectProblem } from "./project-fKmuIaMI.mjs";
|
|
7
7
|
import { r as waitForChild, t as createLineReader } from "./process-output-C7IlIzd7.mjs";
|
|
8
|
-
import { i as resolveProjectMetro, r as pidExists } from "./metro-
|
|
9
|
-
import { n as verifyCollectorOwnership, r as readCollectors } from "./ownership-
|
|
10
|
-
import { a as easAuthNote, c as loadProjectProvider, d as resolveEasCliBin, f as resolveRemote, i as checkEasAuth, n as UPLOAD_TIMEOUT_MS, o as exitAfterFlush, p as uploadRemote, r as cacheLevel, s as isEasAuthFailureText, t as RESOLVE_TIMEOUT_MS, x as parkedMaxSetting } from "./remote-cache-DOi5-zLj.mjs";
|
|
11
|
-
import { A as settingFile, B as compilerCacheFallbackMessage, E as remoteDeviceSettingError, F as unknownSettingKeys, H as resolveOptimizations, L as detectProviders, M as settingShapeErrors, O as resolveCacheProviderConfig, P as tunnelModeSetting, T as remoteAndroidSetting, V as optimizationBuildProfile, a as SETTING_SHAPE_REMEDY, d as cacheProviderSettingError, k as resolveSettings, r as REMOTE_DEVICE_BACKENDS, s as androidAvdConfigSettingError, u as androidDataPartitionSizeGbSettingError, w as publicUrlSetting, z as artifactCachePolicy } from "./settings-BSwrY4FZ.mjs";
|
|
8
|
+
import { i as resolveProjectMetro, r as pidExists } from "./metro-CORqXyTL.mjs";
|
|
9
|
+
import { n as verifyCollectorOwnership, r as readCollectors } from "./ownership-8O073GIC.mjs";
|
|
12
10
|
import { _ as launchErrorPreview, c as launchErrorReport, g as stepTimer, h as stepClock, i as formatDuration, l as phaseLine, n as appReadinessMessage, p as shortHash } from "./command-output-8wHMEde0.mjs";
|
|
13
|
-
import {
|
|
14
|
-
import { C as
|
|
15
|
-
import { r as
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import { c as ensureWorkspaceStorageSafely, d as
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import { t as
|
|
24
|
-
import {
|
|
11
|
+
import { r as claimFailure } from "./ownership-claim-Bf2CAfw3.mjs";
|
|
12
|
+
import { $ as providerDownloadPath, C as memoizeEmulatorProbe, G as buildCacheKey, J as filesystemBuildCapability, K as describeFingerprintMiss, L as waitForBoot, M as resetAdoptedAvd, N as resolveOwnedAvdSerial, P as resolvePhysicalDevice, Q as prepareProviderDownloadDir, R as WAIT_CEILING_MS, U as takeoverLine, V as releaseBuildLock, W as waitForBuild, X as fingerprintDiffSuffix, Y as fingerprintDiffRecord, Z as fingerprintProject, _ as findBuildTool, a as androidSystemImageAbi, at as untrackedMissLine, ct as captureAssetManifest, et as providerUploadOutcome, g as extractEmulatorFailure, h as emulatorFailureRemedy, i as androidPoolNoCandidatesRefusal, it as storedAssetManifest, j as probeEmulatorSerial, k as physicalDeviceModel, lt as compareAssetManifests, m as emulatorDiskSpaceRemedy, n as androidHome, nt as resolveBuild, ot as untrackedNativeFiles, r as androidPoolCandidates, rt as storeBuild, st as assetDiffReason, t as androidDeviceAbi, tt as refingerprintAfterMutation, ut as readAssetManifest, x as listInstalledSystemImages, y as listAdbDevices, z as acquireBuildLock } from "./android-BqVIJoG7.mjs";
|
|
13
|
+
import { A as resolveSettings, B as artifactCachePolicy, D as remoteDeviceSettingError, E as remoteAndroidSetting, F as tunnelModeSetting, H as optimizationBuildProfile, I as unknownSettingKeys, N as settingShapeErrors, R as detectProviders, T as publicUrlSetting, U as resolveOptimizations, V as compilerCacheFallbackMessage, a as SETTING_SHAPE_REMEDY, d as cacheProviderSettingError, j as settingFile, k as resolveCacheProviderConfig, r as REMOTE_DEVICE_BACKENDS, s as androidAvdConfigSettingError, u as androidDataPartitionSizeGbSettingError, x as metroWarmupUrlSetting } from "./settings-DEJf6_kJ.mjs";
|
|
14
|
+
import { t as cleanLine } from "./server-expo-DizT4OMk.mjs";
|
|
15
|
+
import { C as uploadRemote, S as resolveRemote, _ as isEasAuthFailureText, c as parkedMaxSetting, d as RESOLVE_TIMEOUT_MS, f as UPLOAD_TIMEOUT_MS, g as exitAfterFlush, h as easAuthNote, m as checkEasAuth, p as cacheLevel, v as loadProjectProvider, x as resolveEasCliBin } from "./teardown-ClFp1Aax.mjs";
|
|
16
|
+
import { c as ensureWorkspaceStorageSafely, d as launchOutcomeRecord, f as noMetroMessage, g as warmMetro, i as detectEntryFile, m as resolveMetroWithRetry, n as pickDevClientScheme, o as needsPrebuild, p as noMetroRemedy, s as runPrebuild, t as devClientScheme, u as isPhysicalDeviceRequest, v as isEasBuildFailure, y as resolveEasDevelopmentBuild } from "./dev-client-BjisHDB5.mjs";
|
|
17
|
+
import { t as withWorkspaceProcessLock } from "./workspace-process-lock-3NcQnZ0X.mjs";
|
|
18
|
+
import { D as verifyAndroidReleaseLaunch, E as unverifiedLaunchLines, O as verifyLaunch, S as launchAndroidReleaseApp, _ as installConflictKind, c as RELEASE_VERIFY_WAIT_MS, d as androidDevClientUrl, g as installAndroidApp, h as deviceShellArg, i as LAUNCH_BUNDLING, l as VERIFY_TIMEOUT_MS, n as DEFAULT_METRO_PORT, o as LAUNCH_FATAL, s as LAUNCH_UNVERIFIED, t as ADB_INSTALL_TIMEOUT_MS, u as androidAppProcess, x as launchAndroidApp } from "./app-install-DIp9riNx.mjs";
|
|
19
|
+
import { a as lostLine, c as releaseLeaseOnSignal, i as leaseExpiryText, l as runLease, n as DEBUG_VERIFY_STEP_MS, o as lostRefusal, r as acquireRunLease, s as parseDeviceWait, t as selectFromPool, u as waitFlagConflict } from "./device-pool-CPbLGfrB.mjs";
|
|
20
|
+
import { r as releaseBuildSlot, t as acquireBuildSlot } from "./build-slots-C3-R4XQr.mjs";
|
|
21
|
+
import { a as statsProjectKey, i as recordRunStats, n as readRunEstimates, t as createRunRecorder } from "./stats-BR-kwj0B.mjs";
|
|
22
|
+
import { C as unknownAndroidSystemImageRefusal, _ as AvdRecoveryError, b as ensureBooted, f as startBuildHeartbeat, p as tailLines, r as HEARTBEAT_INTERVAL_MS, v as checkDeviceCapacity, x as ensureOwnedDevice } from "./xcode-D7sAp-Zy.mjs";
|
|
23
|
+
import { a as ensureMetroReachable, b as ownedSessionName, l as resolveRemoteContext, n as binOnPath, o as ensureRemoteBootOwned, s as remoteAndroidDeps } from "./device-remote-AkYCKS8U.mjs";
|
|
24
|
+
import { a as buildAndroid, c as CCACHE_NOT_RUN, f as resolveCcache, l as CCACHE_UNAVAILABLE, n as resolveAndroidCas, o as productFlavorRefusal, p as formatDiagnostic, r as resolveAndroidCompilerCache, s as readProductFlavors, u as ccacheActivityLine } from "./android-cas-BR5YJBYv.mjs";
|
|
25
|
+
import { n as nativeRunCommand, t as launchSlotScope } from "./slot-launch-cci0xvmC.mjs";
|
|
25
26
|
import { t as spawnEntry } from "./spawn-entry-nCE6m770.mjs";
|
|
26
|
-
import { a as printNativeCrashReport, r as captureNativeCrashes, t as errorDiagnostics } from "./error-diagnostics-
|
|
27
|
+
import { a as printNativeCrashReport, r as captureNativeCrashes, t as errorDiagnostics } from "./error-diagnostics-DHC31rXA.mjs";
|
|
27
28
|
import { existsSync, mkdirSync, openSync, readFileSync, readdirSync, rmSync } from "node:fs";
|
|
28
29
|
import { InvalidArgumentError } from "commander";
|
|
29
30
|
import { basename, dirname, join, relative } from "node:path";
|
|
30
|
-
import chalk from "chalk";
|
|
31
31
|
import { createWarnOnce, loadCacheProvider, resolveTieredBuild, storeTieredBuild } from "@stim-cli/cache";
|
|
32
|
+
import chalk from "chalk";
|
|
32
33
|
//#region src/engine/apk-swap.ts
|
|
33
34
|
const ANDROID_BUNDLE_NAME = "index.android.bundle";
|
|
34
35
|
const ANDROID_BUNDLE_ENTRY = `assets/${ANDROID_BUNDLE_NAME}`;
|
|
@@ -399,7 +400,12 @@ function androidSystemImageSetting(settings) {
|
|
|
399
400
|
function resolveSystemImage(flag, settings) {
|
|
400
401
|
return (typeof flag === "string" && flag.trim() !== "" ? flag.trim() : null) || androidSystemImageSetting(settings);
|
|
401
402
|
}
|
|
402
|
-
function systemImageRefusal({ flag, resolved, physical, remoteBackend, listImages }) {
|
|
403
|
+
function systemImageRefusal({ slot, flag, resolved, physical, remoteBackend, listImages }) {
|
|
404
|
+
if (slot && slot !== "default" && remoteBackend) return {
|
|
405
|
+
code: "STIM_BAD_ARG",
|
|
406
|
+
message: "Named slots currently support local simulators and physical devices.",
|
|
407
|
+
remedy: "Use the default slot for a remote session."
|
|
408
|
+
};
|
|
403
409
|
if (typeof flag === "string" && flag.trim() === "") return {
|
|
404
410
|
code: "STIM_BAD_ARG",
|
|
405
411
|
message: "--system-image was given an empty id.",
|
|
@@ -624,9 +630,10 @@ function androidBuildOptions({ release, physical, device, variant, deviceAbi, co
|
|
|
624
630
|
}
|
|
625
631
|
//#endregion
|
|
626
632
|
//#region src/commands/android/result.ts
|
|
627
|
-
function androidFacts({ serial, avdName = null, deviceName = null, systemImage = null, fingerprint, cacheKey = null, variant = null, metroPort = null, cacheHit, cacheSkipped = false, waitedForBuild = null, appPath, bundleId, installSkipped = false, launched, ccache = CCACHE_UNAVAILABLE, logs, debugHttpHost = null, debugHttpHostNote = null, devClientUrl = null, durationMs, lease }) {
|
|
633
|
+
function androidFacts({ slot, serial, avdName = null, deviceName = null, systemImage = null, fingerprint, cacheKey = null, variant = null, metroPort = null, cacheHit, cacheSkipped = false, waitedForBuild = null, appPath, bundleId, installSkipped = false, launched, ccache = CCACHE_UNAVAILABLE, logs, debugHttpHost = null, debugHttpHostNote = null, devClientUrl = null, durationMs, lease }) {
|
|
628
634
|
return {
|
|
629
635
|
platform: PLATFORM,
|
|
636
|
+
...slot && slot !== "default" ? { slot } : {},
|
|
630
637
|
serial: serial ?? null,
|
|
631
638
|
avdName: avdName ?? null,
|
|
632
639
|
deviceName: deviceName ?? avdName ?? null,
|
|
@@ -689,7 +696,7 @@ async function finishAndroidUpload(uploadPending, remote, phase) {
|
|
|
689
696
|
}
|
|
690
697
|
return false;
|
|
691
698
|
}
|
|
692
|
-
function reportAndroidResult({ json, useBuildCache, variant, release, metroCheck, metroPort, logsDir, serial, apkPath, androidPackage, installSkipped, record, waitedForBuild, remote, providerName, launchState, launchWarning, launched, ccache, durationMs, writer, emit, lease, recordRun }) {
|
|
699
|
+
function reportAndroidResult({ slot, json, useBuildCache, variant, release, metroCheck, metroPort, logsDir, serial, apkPath, androidPackage, installSkipped, record, waitedForBuild, remote, providerName, launchState, launchWarning, launched, ccache, durationMs, writer, emit, lease, recordRun }) {
|
|
693
700
|
recordRun({
|
|
694
701
|
failed: false,
|
|
695
702
|
cacheHit: cacheLevel(record.cacheHit),
|
|
@@ -697,6 +704,7 @@ function reportAndroidResult({ json, useBuildCache, variant, release, metroCheck
|
|
|
697
704
|
durationMs
|
|
698
705
|
});
|
|
699
706
|
const facts = androidFacts({
|
|
707
|
+
slot,
|
|
700
708
|
serial,
|
|
701
709
|
avdName: record.avdName,
|
|
702
710
|
deviceName: record.deviceName,
|
|
@@ -765,18 +773,18 @@ function persistLastBuild({ writeState, root, record, startedAt, durationMs, sta
|
|
|
765
773
|
function collectorEntry() {
|
|
766
774
|
return spawnEntry("collector-run");
|
|
767
775
|
}
|
|
768
|
-
function collectorLogFile(root) {
|
|
769
|
-
return join(workspaceLogsDir(root), `collector-${PLATFORM}.log`);
|
|
776
|
+
function collectorLogFile(root, slot = "default") {
|
|
777
|
+
return join(workspaceLogsDir(root), `collector-${deviceSlotKey(PLATFORM, slot)}.log`);
|
|
770
778
|
}
|
|
771
779
|
const COLLECTOR_EXIT_WAIT_MS = 2e3;
|
|
772
780
|
const COLLECTOR_POLL_MS = 25;
|
|
773
|
-
function killPreviousCollector(root, { platform = PLATFORM, kill = (pid, signal) => process.kill(pid, signal), collectors = null, verify = verifyCollectorOwnership, isAlive = pidExists, note = (_line) => {} } = {}) {
|
|
774
|
-
const record = (collectors ?? readCollectors(root))?.[platform];
|
|
781
|
+
function killPreviousCollector(root, { platform = PLATFORM, slot = "default", kill = (pid, signal) => process.kill(pid, signal), collectors = null, verify = verifyCollectorOwnership, isAlive = pidExists, note = (_line) => {} } = {}) {
|
|
782
|
+
const record = (collectors ?? readCollectors(root))?.[deviceSlotKey(platform, slot)];
|
|
775
783
|
const pid = Number(record?.pid);
|
|
776
784
|
if (!Number.isFinite(pid) || pid <= 0 || pid === process.pid) return null;
|
|
777
785
|
const ownership = verify({
|
|
778
786
|
pid,
|
|
779
|
-
platform,
|
|
787
|
+
platform: deviceSlotKey(platform, slot),
|
|
780
788
|
root,
|
|
781
789
|
isAlive,
|
|
782
790
|
expected: record
|
|
@@ -793,8 +801,9 @@ function killPreviousCollector(root, { platform = PLATFORM, kill = (pid, signal)
|
|
|
793
801
|
return null;
|
|
794
802
|
}
|
|
795
803
|
}
|
|
796
|
-
async function startCollector({ root, serial, packageName, spawn, kill, alive = pidExists, verify = verifyCollectorOwnership, waitMs = COLLECTOR_EXIT_WAIT_MS, sleep = (ms) => new Promise((r) => setTimeout(r, ms)), out }) {
|
|
804
|
+
async function startCollector({ root, slot = "default", serial, packageName, spawn, kill, alive = pidExists, verify = verifyCollectorOwnership, waitMs = COLLECTOR_EXIT_WAIT_MS, sleep = (ms) => new Promise((r) => setTimeout(r, ms)), out }) {
|
|
797
805
|
const previousPid = killPreviousCollector(root, {
|
|
806
|
+
slot,
|
|
798
807
|
kill,
|
|
799
808
|
isAlive: alive,
|
|
800
809
|
verify,
|
|
@@ -807,7 +816,7 @@ async function startCollector({ root, serial, packageName, spawn, kill, alive =
|
|
|
807
816
|
let stdio = "ignore";
|
|
808
817
|
try {
|
|
809
818
|
mkdirSync(workspaceLogsDir(root), { recursive: true });
|
|
810
|
-
const fd = openSync(collectorLogFile(root), "a");
|
|
819
|
+
const fd = openSync(collectorLogFile(root, slot), "a");
|
|
811
820
|
stdio = [
|
|
812
821
|
"ignore",
|
|
813
822
|
fd,
|
|
@@ -821,6 +830,7 @@ async function startCollector({ root, serial, packageName, spawn, kill, alive =
|
|
|
821
830
|
PLATFORM,
|
|
822
831
|
"--root",
|
|
823
832
|
root,
|
|
833
|
+
...slot === "default" ? [] : ["--slot", slot],
|
|
824
834
|
"--serial",
|
|
825
835
|
serial,
|
|
826
836
|
"--package",
|
|
@@ -840,9 +850,14 @@ async function startCollector({ root, serial, packageName, spawn, kill, alive =
|
|
|
840
850
|
}
|
|
841
851
|
//#endregion
|
|
842
852
|
//#region src/commands/android/launch.ts
|
|
843
|
-
async function verifyAndroidRun({ root, release, remoteRelease, remoteDevice, verifyReleaseLaunched, verifyLaunched, serial, androidPackage, variant, metroCheck, logsDir, launchedAt, metroPort, isExpo, physical, scheme, component = null, phase }) {
|
|
853
|
+
async function verifyAndroidRun({ root, slot, release, remoteRelease, remoteDevice, verifyReleaseLaunched, verifyLaunched, serial, androidPackage, variant, metroCheck, logsDir, launchedAt, metroPort, isExpo, physical, device, scheme, component = null, phase }) {
|
|
854
|
+
const runCommand = nativeRunCommand("android", slot, {
|
|
855
|
+
physical,
|
|
856
|
+
deviceId: serial
|
|
857
|
+
});
|
|
844
858
|
const readNativeCrashes = () => remoteDevice ? [] : captureNativeCrashes({
|
|
845
859
|
root,
|
|
860
|
+
slot,
|
|
846
861
|
platform: "android",
|
|
847
862
|
deviceId: serial,
|
|
848
863
|
appId: androidPackage,
|
|
@@ -872,7 +887,13 @@ async function verifyAndroidRun({ root, release, remoteRelease, remoteDevice, ve
|
|
|
872
887
|
phase("", chalk.yellow("A release process exited before readiness. Run `stim logs --errors` for captured crash reports, or `stim logs --source device` for the full device output."));
|
|
873
888
|
return { state: LAUNCH_FATAL };
|
|
874
889
|
}
|
|
890
|
+
const newEmulator = Boolean(device.created && device.owned && !remoteDevice);
|
|
891
|
+
const timeoutMs = newEmulator ? 6e4 : VERIFY_TIMEOUT_MS;
|
|
892
|
+
if (metroCheck && newEmulator) phase("verify", "waiting up to 60s for bundle load (new emulator)");
|
|
875
893
|
const verification = metroCheck ? await verifyLaunched({
|
|
894
|
+
timeoutMs,
|
|
895
|
+
requireBundleResponse: true,
|
|
896
|
+
slot: launchSlotScope(root, slot),
|
|
876
897
|
onReadinessPending: () => phase("readiness", "waiting for app readiness (up to 30s after bundle load)"),
|
|
877
898
|
logsDir,
|
|
878
899
|
since: launchedAt,
|
|
@@ -904,10 +925,10 @@ async function verifyAndroidRun({ root, release, remoteRelease, remoteDevice, ve
|
|
|
904
925
|
phase("verify", chalk.red(`FATAL after ${formatDuration(verification.waitedMs ?? 0)}: ${reason}`));
|
|
905
926
|
for (const line of launchErrorPreview(verification.errors ?? [], root)) phase("", chalk.red(line));
|
|
906
927
|
if (verification.processAlive === false && !verification.errors?.some((record) => record.event === "native_crash")) phase("logs", "No attributable native crash report captured yet. Run `stim logs --errors` again for delayed reports, or `stim logs --source device` for available output.");
|
|
907
|
-
if (nativeFatal || verification.processAlive === false) phase("remedy", chalk.yellow(`Fix the crash, then restart the app: \`adb -s ${deviceShellArg(serial)} shell am force-stop ${deviceShellArg(androidPackage)}\`, then run \`
|
|
928
|
+
if (nativeFatal || verification.processAlive === false) phase("remedy", chalk.yellow(`Fix the crash, then restart the app: \`adb -s ${deviceShellArg(serial)} shell am force-stop ${deviceShellArg(androidPackage)}\`, then run \`${runCommand}\` again. A crashed Android process can remain alive behind the system crash dialog; Metro reload cannot recover it.`));
|
|
908
929
|
else if (verification.processAlive === true && metroPort !== null) {
|
|
909
930
|
const reloadRemedy = physical ? `run \`agent-device metro reload --metro-port ${metroPort}\`.` : "run `stim reload android`.";
|
|
910
|
-
phase("remedy", chalk.yellow(`The native app is still running. ${deliveryFailed ? "Check the Metro logs and device connection, then" : "Fix the JavaScript or TypeScript error, then"} ${reloadRemedy} Do not run \`
|
|
931
|
+
phase("remedy", chalk.yellow(`The native app is still running. ${deliveryFailed ? "Check the Metro logs and device connection, then" : "Fix the JavaScript or TypeScript error, then"} ${reloadRemedy} Do not run \`${runCommand}\` unless native inputs changed or the app process exits.`));
|
|
911
932
|
}
|
|
912
933
|
return { state: LAUNCH_FATAL };
|
|
913
934
|
}
|
|
@@ -918,7 +939,7 @@ async function verifyAndroidRun({ root, release, remoteRelease, remoteDevice, ve
|
|
|
918
939
|
for (const line of report.lines) phase("launch", chalk.yellow(line));
|
|
919
940
|
if (report.lines.length > 0 && verification.processAlive === true && metroPort !== null) {
|
|
920
941
|
const reloadRemedy = physical ? `run \`agent-device metro reload --metro-port ${metroPort}\`.` : "run `stim reload android`.";
|
|
921
|
-
phase("remedy", chalk.yellow(`The native app is still running. Fix the JavaScript or TypeScript error; Fast Refresh should apply the edit. If the error screen remains, ${reloadRemedy} Do not run \`
|
|
942
|
+
phase("remedy", chalk.yellow(`The native app is still running. Fix the JavaScript or TypeScript error; Fast Refresh should apply the edit. If the error screen remains, ${reloadRemedy} Do not run \`${runCommand}\` unless native inputs changed or the app process exits.`));
|
|
922
943
|
}
|
|
923
944
|
return {
|
|
924
945
|
state: true,
|
|
@@ -947,7 +968,29 @@ async function verifyAndroidRun({ root, release, remoteRelease, remoteDevice, ve
|
|
|
947
968
|
})) phase("", chalk.yellow(line));
|
|
948
969
|
return { state: LAUNCH_UNVERIFIED };
|
|
949
970
|
}
|
|
950
|
-
async function
|
|
971
|
+
async function resolveInstallSerial({ root, slot, device, physical, remoteDevice, serial, resolveAvdSerial, waitForDeviceBoot, phase }) {
|
|
972
|
+
if (physical || remoteDevice || !device.owned || !device.avdName) return serial;
|
|
973
|
+
const resolved = resolveAvdSerial(device.avdName, { timeoutMs: 5e3 });
|
|
974
|
+
if (!resolved.serial) throw new Error(`Could not verify a running owned AVD ${device.avdName} before installation.`);
|
|
975
|
+
if (resolved.serial !== serial) {
|
|
976
|
+
phase("device", `${device.avdName} changed serial (${serial} -> ${resolved.serial}); checking readiness before installation`);
|
|
977
|
+
const ready = await waitForDeviceBoot(resolved.serial, 6e4, { commandTimeoutMs: 5e3 });
|
|
978
|
+
if (!ready.ok) throw new Error(`Emulator ${resolved.serial} never reported boot completion. Diagnostic: ${JSON.stringify(ready.diagnostic)}`);
|
|
979
|
+
if (resolveAvdSerial(device.avdName, { timeoutMs: 5e3 }).serial !== resolved.serial) throw new Error(`Could not verify AVD ${device.avdName} still runs on ${resolved.serial} after checking readiness.`);
|
|
980
|
+
phase("device", `reopen agent-device on ${resolved.serial}`);
|
|
981
|
+
}
|
|
982
|
+
const consolePort = Number(resolved.serial.replace(/^emulator-/, ""));
|
|
983
|
+
withConfigLock(() => {
|
|
984
|
+
const current = deviceSlotPlatforms(loadConfig()?.projects?.[root], slot)?.android;
|
|
985
|
+
if (!current?.owned || current.avdName !== device.avdName) throw new Error("The owned emulator assignment changed before installation.");
|
|
986
|
+
if (current.consolePort !== consolePort) setDevice(root, PLATFORM, {
|
|
987
|
+
...current,
|
|
988
|
+
consolePort
|
|
989
|
+
}, slot);
|
|
990
|
+
});
|
|
991
|
+
return resolved.serial;
|
|
992
|
+
}
|
|
993
|
+
async function finishAndroidRun({ lease, releaseLease, root, slot, json, metroCheck, useBuildCache, variant, release, isExpo, metroPort, logsDir, emuLog, device, physical, remoteDevice, bootPromise, resolveAvdSerial, waitForDeviceBoot, bootDuration, apkPath, androidPackage: initialPackage, swapDir, record, waitedForBuild, ccache, uploadPending, providerUpload, providerName, remote, abandonedRemote: remoteWasAbandoned, started, startedAt, writer, phase, fail, readApkPackage, install, launch, launchRelease, resolveDevClientScheme, verifyLaunched, verifyReleaseLaunched, spawn, kill, pidAlive, verifyCollector, writeLaunch, writeState, now, out, emit, recordRun }) {
|
|
951
994
|
let androidPackage = initialPackage;
|
|
952
995
|
let leaseWarned = false;
|
|
953
996
|
const raiseLeaseFor = (boundMs, beforeInstall) => {
|
|
@@ -964,11 +1007,15 @@ async function finishAndroidRun({ lease, releaseLease, root, json, metroCheck, u
|
|
|
964
1007
|
return null;
|
|
965
1008
|
};
|
|
966
1009
|
const booted = await bootPromise;
|
|
1010
|
+
const runCommand = nativeRunCommand("android", slot, {
|
|
1011
|
+
physical,
|
|
1012
|
+
deviceId: booted.serial
|
|
1013
|
+
});
|
|
967
1014
|
if (booted.failed) {
|
|
968
1015
|
const diag = noDeviceDiagnostic({
|
|
969
1016
|
reason: booted.reason ?? "The emulator did not boot.",
|
|
970
1017
|
logFile: emuLog,
|
|
971
|
-
remedy:
|
|
1018
|
+
remedy: `Run \`stim status\` to see what Stim thinks it owns; re-running \`${runCommand}\` creates a fresh owned AVD.`,
|
|
972
1019
|
localEmulator: !physical
|
|
973
1020
|
});
|
|
974
1021
|
return fail(NO_DEVICE, diag.message, diag.remedy, {
|
|
@@ -976,7 +1023,22 @@ async function finishAndroidRun({ lease, releaseLease, root, json, metroCheck, u
|
|
|
976
1023
|
logPath: diag.logPath ? displayPath(root, diag.logPath) : null
|
|
977
1024
|
});
|
|
978
1025
|
}
|
|
979
|
-
|
|
1026
|
+
let serial;
|
|
1027
|
+
try {
|
|
1028
|
+
serial = await resolveInstallSerial({
|
|
1029
|
+
root,
|
|
1030
|
+
slot,
|
|
1031
|
+
device,
|
|
1032
|
+
physical,
|
|
1033
|
+
remoteDevice,
|
|
1034
|
+
serial: booted.serial,
|
|
1035
|
+
resolveAvdSerial,
|
|
1036
|
+
waitForDeviceBoot,
|
|
1037
|
+
phase
|
|
1038
|
+
});
|
|
1039
|
+
} catch (error) {
|
|
1040
|
+
return fail(NO_DEVICE, error instanceof Error ? error.message : String(error), `Run \`stim status\` to inspect the owned AVD, then retry \`${runCommand}\`; the APK was not installed.`);
|
|
1041
|
+
}
|
|
980
1042
|
phase("device", physical ? `${device.deviceName || serial} (${serial}) connected, not owned by Stim` : `${device.avdName || serial} (${serial}) ${device.adopted || device.adoptionPending ? "adopted" : "booted"} ${bootDuration()}`);
|
|
981
1043
|
const packageFromApk = readApkPackage(apkPath);
|
|
982
1044
|
if (packageFromApk && androidPackage && packageFromApk !== androidPackage) phase("install", chalk.dim(`applicationId ${packageFromApk} (from the APK; project files say ${androidPackage})`));
|
|
@@ -1008,7 +1070,7 @@ async function finishAndroidRun({ lease, releaseLease, root, json, metroCheck, u
|
|
|
1008
1070
|
if (adopting) try {
|
|
1009
1071
|
withConfigLock(() => {
|
|
1010
1072
|
const config = loadConfig();
|
|
1011
|
-
const current = config?.projects?.[root]?.
|
|
1073
|
+
const current = deviceSlotPlatforms(config?.projects?.[root], slot)?.android;
|
|
1012
1074
|
if (!config || !current || current.avdName !== device.avdName) throw new Error("The adopted emulator assignment changed during installation.");
|
|
1013
1075
|
delete current.adoptionPending;
|
|
1014
1076
|
saveConfig(config);
|
|
@@ -1070,6 +1132,7 @@ async function finishAndroidRun({ lease, releaseLease, root, json, metroCheck, u
|
|
|
1070
1132
|
if (launched.failed) {
|
|
1071
1133
|
printNativeCrashReport({
|
|
1072
1134
|
root,
|
|
1135
|
+
slot,
|
|
1073
1136
|
platform: "android",
|
|
1074
1137
|
deviceId: serial,
|
|
1075
1138
|
appId: androidPackage,
|
|
@@ -1132,6 +1195,7 @@ async function finishAndroidRun({ lease, releaseLease, root, json, metroCheck, u
|
|
|
1132
1195
|
if (physical) raiseLeaseFor(0, false);
|
|
1133
1196
|
const collectorPid = await startCollector({
|
|
1134
1197
|
root,
|
|
1198
|
+
slot,
|
|
1135
1199
|
serial,
|
|
1136
1200
|
packageName: androidPackage,
|
|
1137
1201
|
spawn,
|
|
@@ -1145,6 +1209,7 @@ async function finishAndroidRun({ lease, releaseLease, root, json, metroCheck, u
|
|
|
1145
1209
|
if (physical) raiseLeaseFor(release ? RELEASE_VERIFY_WAIT_MS : DEBUG_VERIFY_STEP_MS, false);
|
|
1146
1210
|
const { state: launchState, warning: launchWarning } = await verifyAndroidRun({
|
|
1147
1211
|
root,
|
|
1212
|
+
slot,
|
|
1148
1213
|
release,
|
|
1149
1214
|
remoteRelease,
|
|
1150
1215
|
remoteDevice: Boolean(remoteDevice),
|
|
@@ -1159,6 +1224,7 @@ async function finishAndroidRun({ lease, releaseLease, root, json, metroCheck, u
|
|
|
1159
1224
|
metroPort,
|
|
1160
1225
|
isExpo,
|
|
1161
1226
|
physical,
|
|
1227
|
+
device,
|
|
1162
1228
|
scheme,
|
|
1163
1229
|
component: launched.component ?? null,
|
|
1164
1230
|
phase
|
|
@@ -1177,6 +1243,7 @@ async function finishAndroidRun({ lease, releaseLease, root, json, metroCheck, u
|
|
|
1177
1243
|
const leaseFacts = lease?.facts() ?? null;
|
|
1178
1244
|
releaseLease();
|
|
1179
1245
|
const facts = reportAndroidResult({
|
|
1246
|
+
slot,
|
|
1180
1247
|
json,
|
|
1181
1248
|
useBuildCache,
|
|
1182
1249
|
variant,
|
|
@@ -1239,7 +1306,7 @@ function androidCommand(program) {
|
|
|
1239
1306
|
registerAndroid(program);
|
|
1240
1307
|
}
|
|
1241
1308
|
function registerAndroid(program) {
|
|
1242
|
-
program.command("android").description("Build (or install from the shared cache), install and launch this workspace's Android app on its owned emulator, wired to the reserved Metro port. Never starts the bundler -- run `stim start` first.").option("--json", "Emit the facts as a single JSON line on stdout; every other line goes to stderr").option("--no-metro-check", "Skip the reserved-port Metro health check (the app will load no bundle unless something else serves it)").option("--no-build-cache", "Build fresh, ignoring cached artifacts (local and the project's build-cache provider); the fresh build still replaces the cache entry").option("--variant <name>", "Gradle variant to assemble and install (e.g. productionDebug on a flavored project); overrides the android.variant setting. A variant ending in Release embeds the JS bundle and skips Metro entirely. Default: debug").option("--system-image <id>", "Android system image to create this workspace's owned AVD from, as the sdkmanager package id (e.g. \"system-images;android-36;google_apis;arm64-v8a\"). Overrides the android.systemImage setting for this invocation. An unknown id refuses with STIM_BAD_ARG and prints the installed images.").option("--device [serial]", "Install and launch on a connected physical device instead of this workspace's owned emulator. With no serial, the first connected device this workspace can lease is used. Stim never creates, boots, or deletes a physical device.").option("--remote <backend>", "Install and launch on a remote device with proxy or EAS.
|
|
1309
|
+
program.command("android").description("Build (or install from the shared cache), install and launch this workspace's Android app on its owned emulator, wired to the reserved Metro port. Never starts the bundler -- run `stim start` first.").option("--eas-profile <name>", "Download a matching EAS development build; on a miss, print the build command without running it").option("--slot <name>", "Reusable device slot within this workspace (default: default)", validateDeviceSlot).option("--json", "Emit the facts as a single JSON line on stdout; every other line goes to stderr").option("--no-metro-check", "Skip the reserved-port Metro health check (the app will load no bundle unless something else serves it)").option("--no-build-cache", "Build fresh, ignoring cached artifacts (local and the project's build-cache provider); the fresh build still replaces the cache entry").option("--variant <name>", "Gradle variant to assemble and install (e.g. productionDebug on a flavored project); overrides the android.variant setting. A variant ending in Release embeds the JS bundle and skips Metro entirely. Default: debug").option("--system-image <id>", "Android system image to create this workspace's owned AVD from, as the sdkmanager package id (e.g. \"system-images;android-36;google_apis;arm64-v8a\"). Overrides the android.systemImage setting for this invocation. An unknown id refuses with STIM_BAD_ARG and prints the installed images.").option("--device [serial]", "Install and launch on a connected physical device instead of this workspace's owned emulator. With no serial, the first connected device this workspace can lease is used. Stim never creates, boots, or deletes a physical device.").option("--remote <backend>", "Install and launch on a remote device with proxy or EAS. Builds are local unless --eas-profile selects an existing EAS build.", (value) => {
|
|
1243
1310
|
if (REMOTE_DEVICE_BACKENDS.includes(value)) return value;
|
|
1244
1311
|
throw new InvalidArgumentError(`expected one of: ${REMOTE_DEVICE_BACKENDS.join(", ")}`);
|
|
1245
1312
|
}).option("--wait <seconds>", "How long to wait for another workspace to release the device it leases, before refusing with STIM_DEVICE_BUSY (default 60, 0 refuses at once). Only with --device.").option("--no-wait", "Install on a device another workspace leases instead of waiting: this run takes no lease and, when both workspaces build the same app id, the install terminates the holder's running app. Only with --device.").action(async (opts) => {
|
|
@@ -1249,8 +1316,10 @@ function registerAndroid(program) {
|
|
|
1249
1316
|
process.exit(1);
|
|
1250
1317
|
return;
|
|
1251
1318
|
}
|
|
1252
|
-
if (!(await runAndroid({
|
|
1319
|
+
if (!(await withWorkspaceProcessLock(workspaceDir(root), "native-run", () => runAndroid({
|
|
1253
1320
|
root,
|
|
1321
|
+
slot: opts.slot,
|
|
1322
|
+
easProfile: opts.easProfile,
|
|
1254
1323
|
json: Boolean(opts.json),
|
|
1255
1324
|
metroCheck: opts.metroCheck !== false,
|
|
1256
1325
|
useBuildCache: opts.buildCache !== false,
|
|
@@ -1260,10 +1329,13 @@ function registerAndroid(program) {
|
|
|
1260
1329
|
device: opts.device ?? null,
|
|
1261
1330
|
wait: opts.wait,
|
|
1262
1331
|
waitConflict: waitFlagConflict(process.argv)
|
|
1332
|
+
}), {
|
|
1333
|
+
external: true,
|
|
1334
|
+
waitMs: 18e5
|
|
1263
1335
|
})).ok) process.exit(1);
|
|
1264
1336
|
});
|
|
1265
1337
|
}
|
|
1266
|
-
function resolveRunAndroidOptions({ root, json = false, remoteDevice: commandRemoteBackend = null, resolveSettingsFor = resolveSettings, resolveRemoteDeviceContext = resolveRemoteContext, remoteDeviceDeps: makeRemoteDeviceDeps = remoteAndroidDeps, resolveEasBin = resolveEasCliBin, ensureMetroReachable: ensureRemoteMetro = ensureMetroReachable, ensureRemoteBootOwned: ensureRemoteOwned = ensureRemoteBootOwned, detectRemoteProviders = detectProviders, metroCheck = true, useBuildCache = true, variant: variantFlag = null, systemImage: systemImageFlag = null, device: deviceFlag = null, wait: waitFlag = void 0, waitConflict = false, acquireLease = acquireRunLease, makeRunLease = runLease, selectPool = selectFromPool, onLeaseSignal = releaseLeaseOnSignal, listDevices = listAdbDevices, deviceModel = physicalDeviceModel, deviceAbi = androidDeviceAbi, isEmulatorDevice = probeEmulatorSerial, readApkPackage = (apkPath) => apkPackage(dumpApkManifest(apkPath)), getLimits = getConcurrencyLimits, checkCapacity = checkDeviceCapacity, acquireSlot = acquireBuildSlot, releaseSlot = releaseBuildSlot, ensureDevice = ensureOwnedDevice, listSystemImages = listInstalledSystemImages, ensureDeviceBooted = ensureBooted, resolveMetro = resolveProjectMetro, resolveMetroRetrying = resolveMetroWithRetry, readState = readWorkspaceState, pidAlive = pidExists, verifyCollector = verifyCollectorOwnership, verifyLaunched = verifyLaunch, ensureStorage = ensureWorkspaceStorageSafely, fingerprint = fingerprintProject, untracked = untrackedNativeFiles, resolveCached = resolveBuild, storeCached = storeBuild, storedAssets = storedAssetManifest, captureAssets = captureAssetManifest, acquireLock = acquireBuildLock, releaseLock = releaseBuildLock, waitForBuild: waitForBuild$1 = waitForBuild, loadProvider = loadProjectProvider, easAuth = checkEasAuth, resolveRemoteBuild = resolveRemote, uploadRemoteBuild = uploadRemote, resolveCacheProvider = resolveCacheProviderConfig, loadCacheProviderModule = loadCacheProvider, needsPrebuildFor = needsPrebuild, prebuild = runPrebuild, build = buildAndroid, ccacheFor = resolveCcache, install = installAndroidApp, launch = launchAndroidApp, launchRelease = launchAndroidReleaseApp, verifyReleaseLaunched = verifyAndroidReleaseLaunch, swapApk = swapApkBundle, resolveDevClientScheme = androidDevClientScheme, spawn = (cmd, args, opts) => getExecutor().spawn(cmd, args, opts), kill = (pid, signal) => process.kill(pid, signal), createWriter = createNdjsonWriter, writeLaunch = writeWorkspaceLaunch, writeState = writeWorkspaceState, recordStats = recordRunStats, readEstimates = readRunEstimates, now = Date.now, out = (line) => console.error(line), emit = (line) => console.log(line) } = {}) {
|
|
1338
|
+
function resolveRunAndroidOptions({ root, json = false, remoteDevice: commandRemoteBackend = null, resolveSettingsFor = resolveSettings, resolveRemoteDeviceContext = resolveRemoteContext, remoteDeviceDeps: makeRemoteDeviceDeps = remoteAndroidDeps, resolveEasBin = resolveEasCliBin, ensureMetroReachable: ensureRemoteMetro = ensureMetroReachable, ensureRemoteBootOwned: ensureRemoteOwned = ensureRemoteBootOwned, detectRemoteProviders = detectProviders, metroCheck = true, useBuildCache = true, easProfile, resolveEasDevelopmentBuild: resolveEasBuild = resolveEasDevelopmentBuild, variant: variantFlag = null, systemImage: systemImageFlag = null, device: deviceFlag = null, wait: waitFlag = void 0, waitConflict = false, acquireLease = acquireRunLease, makeRunLease = runLease, selectPool = selectFromPool, onLeaseSignal = releaseLeaseOnSignal, listDevices = listAdbDevices, deviceModel = physicalDeviceModel, deviceAbi = androidDeviceAbi, isEmulatorDevice = probeEmulatorSerial, readApkPackage = (apkPath) => apkPackage(dumpApkManifest(apkPath)), getLimits = getConcurrencyLimits, checkCapacity = checkDeviceCapacity, acquireSlot = acquireBuildSlot, releaseSlot = releaseBuildSlot, ensureDevice = ensureOwnedDevice, listSystemImages = listInstalledSystemImages, ensureDeviceBooted = ensureBooted, resolveAvdSerial = resolveOwnedAvdSerial, waitForDeviceBoot = waitForBoot, resolveMetro = resolveProjectMetro, warmMetro: prewarmMetro = warmMetro, resolveMetroRetrying = resolveMetroWithRetry, readState = readWorkspaceState, pidAlive = pidExists, verifyCollector = verifyCollectorOwnership, verifyLaunched = verifyLaunch, ensureStorage = ensureWorkspaceStorageSafely, fingerprint = fingerprintProject, untracked = untrackedNativeFiles, resolveCached = resolveBuild, storeCached = storeBuild, storedAssets = storedAssetManifest, captureAssets = captureAssetManifest, acquireLock = acquireBuildLock, releaseLock = releaseBuildLock, waitForBuild: waitForBuild$1 = waitForBuild, loadProvider = loadProjectProvider, easAuth = checkEasAuth, resolveRemoteBuild = resolveRemote, uploadRemoteBuild = uploadRemote, resolveCacheProvider = resolveCacheProviderConfig, loadCacheProviderModule = loadCacheProvider, needsPrebuildFor = needsPrebuild, prebuild = runPrebuild, build = buildAndroid, ccacheFor = resolveCcache, install = installAndroidApp, launch = launchAndroidApp, launchRelease = launchAndroidReleaseApp, verifyReleaseLaunched = verifyAndroidReleaseLaunch, swapApk = swapApkBundle, resolveDevClientScheme = androidDevClientScheme, spawn = (cmd, args, opts) => getExecutor().spawn(cmd, args, opts), kill = (pid, signal) => process.kill(pid, signal), createWriter = createNdjsonWriter, writeLaunch = writeWorkspaceLaunch, writeState = writeWorkspaceState, recordStats = recordRunStats, readEstimates = readRunEstimates, now = Date.now, out = (line) => console.error(line), emit = (line) => console.log(line) } = {}) {
|
|
1267
1339
|
return {
|
|
1268
1340
|
root,
|
|
1269
1341
|
json,
|
|
@@ -1277,6 +1349,8 @@ function resolveRunAndroidOptions({ root, json = false, remoteDevice: commandRem
|
|
|
1277
1349
|
detectRemoteProviders,
|
|
1278
1350
|
metroCheck,
|
|
1279
1351
|
useBuildCache,
|
|
1352
|
+
easProfile,
|
|
1353
|
+
resolveEasBuild,
|
|
1280
1354
|
variantFlag,
|
|
1281
1355
|
systemImageFlag,
|
|
1282
1356
|
deviceFlag,
|
|
@@ -1298,7 +1372,10 @@ function resolveRunAndroidOptions({ root, json = false, remoteDevice: commandRem
|
|
|
1298
1372
|
ensureDevice,
|
|
1299
1373
|
listSystemImages,
|
|
1300
1374
|
ensureDeviceBooted,
|
|
1375
|
+
resolveAvdSerial,
|
|
1376
|
+
waitForDeviceBoot,
|
|
1301
1377
|
resolveMetro,
|
|
1378
|
+
prewarmMetro,
|
|
1302
1379
|
resolveMetroRetrying,
|
|
1303
1380
|
readState,
|
|
1304
1381
|
pidAlive,
|
|
@@ -1342,8 +1419,38 @@ function resolveRunAndroidOptions({ root, json = false, remoteDevice: commandRem
|
|
|
1342
1419
|
emit
|
|
1343
1420
|
};
|
|
1344
1421
|
}
|
|
1422
|
+
function androidSlotOptions(options) {
|
|
1423
|
+
const base = resolveRunAndroidOptions(options);
|
|
1424
|
+
const slot = validateDeviceSlot(options.slot);
|
|
1425
|
+
if (slot === "default") return base;
|
|
1426
|
+
return {
|
|
1427
|
+
...base,
|
|
1428
|
+
ensureDevice: (args) => base.ensureDevice({
|
|
1429
|
+
...args,
|
|
1430
|
+
slot
|
|
1431
|
+
}),
|
|
1432
|
+
checkCapacity: (args) => base.checkCapacity({
|
|
1433
|
+
...args,
|
|
1434
|
+
slot
|
|
1435
|
+
}),
|
|
1436
|
+
selectPool: (args) => base.selectPool({
|
|
1437
|
+
...args,
|
|
1438
|
+
slot
|
|
1439
|
+
}),
|
|
1440
|
+
acquireLease: (args) => base.acquireLease({
|
|
1441
|
+
...args,
|
|
1442
|
+
slot
|
|
1443
|
+
}),
|
|
1444
|
+
makeRunLease: (args) => base.makeRunLease({
|
|
1445
|
+
...args,
|
|
1446
|
+
slot
|
|
1447
|
+
}),
|
|
1448
|
+
writeLaunch: ((projectRoot, platform, record) => base.writeLaunch(projectRoot, platform, record, slot))
|
|
1449
|
+
};
|
|
1450
|
+
}
|
|
1345
1451
|
async function runAndroid(options = {}) {
|
|
1346
|
-
let { root, json, commandRemoteBackend, resolveSettingsFor, resolveRemoteDeviceContext, makeRemoteDeviceDeps, resolveEasBin, ensureRemoteMetro, ensureRemoteOwned, detectRemoteProviders, metroCheck, useBuildCache: requestedBuildCache, variantFlag, systemImageFlag, deviceFlag, waitFlag, waitConflict, acquireLease, makeRunLease, selectPool, onLeaseSignal, listDevices, deviceModel, deviceAbi, isEmulatorDevice, readApkPackage, getLimits, checkCapacity, acquireSlot, releaseSlot, ensureDevice, listSystemImages, ensureDeviceBooted, resolveMetro, resolveMetroRetrying, readState, pidAlive, verifyCollector, verifyLaunched, ensureStorage, fingerprint, untracked, resolveCached, storeCached, storedAssets, captureAssets, acquireLock, releaseLock, waitForBuild, loadProvider, easAuth, resolveRemoteBuild, uploadRemoteBuild, resolveCacheProvider, loadCacheProviderModule, needsPrebuildFor, prebuild, build, ccacheFor, install, launch, launchRelease, verifyReleaseLaunched, swapApk, resolveDevClientScheme, spawn, kill, createWriter, writeLaunch, writeState, recordStats, readEstimates, now, out, emit } =
|
|
1452
|
+
let { root, json, commandRemoteBackend, resolveSettingsFor, resolveRemoteDeviceContext, makeRemoteDeviceDeps, resolveEasBin, ensureRemoteMetro, ensureRemoteOwned, detectRemoteProviders, metroCheck, useBuildCache: requestedBuildCache, easProfile, resolveEasBuild, variantFlag, systemImageFlag, deviceFlag, waitFlag, waitConflict, acquireLease, makeRunLease, selectPool, onLeaseSignal, listDevices, deviceModel, deviceAbi, isEmulatorDevice, readApkPackage, getLimits, checkCapacity, acquireSlot, releaseSlot, ensureDevice, listSystemImages, ensureDeviceBooted, resolveAvdSerial, waitForDeviceBoot, resolveMetro, prewarmMetro, resolveMetroRetrying, readState, pidAlive, verifyCollector, verifyLaunched, ensureStorage, fingerprint, untracked, resolveCached, storeCached, storedAssets, captureAssets, acquireLock, releaseLock, waitForBuild, loadProvider, easAuth, resolveRemoteBuild, uploadRemoteBuild, resolveCacheProvider, loadCacheProviderModule, needsPrebuildFor, prebuild, build, ccacheFor, install, launch, launchRelease, verifyReleaseLaunched, swapApk, resolveDevClientScheme, spawn, kill, createWriter, writeLaunch, writeState, recordStats, readEstimates, now, out, emit } = androidSlotOptions(options);
|
|
1453
|
+
const slot = validateDeviceSlot(options.slot);
|
|
1347
1454
|
const started = now();
|
|
1348
1455
|
const startedAt = new Date(started).toISOString();
|
|
1349
1456
|
const projectProblem = appProjectProblem(root);
|
|
@@ -1388,8 +1495,11 @@ async function runAndroid(options = {}) {
|
|
|
1388
1495
|
};
|
|
1389
1496
|
}
|
|
1390
1497
|
const logsDir = workspaceLogsDir(root);
|
|
1391
|
-
const buildLog = join(logsDir, "
|
|
1392
|
-
const writer = createWriter(buildLog, {
|
|
1498
|
+
const buildLog = join(logsDir, `build-${deviceSlotKey("android", slot)}.ndjson`);
|
|
1499
|
+
const writer = createWriter(buildLog, {
|
|
1500
|
+
truncate: true,
|
|
1501
|
+
fields: { slot }
|
|
1502
|
+
});
|
|
1393
1503
|
const record = {
|
|
1394
1504
|
fingerprint: null,
|
|
1395
1505
|
cacheKey: null,
|
|
@@ -1514,7 +1624,7 @@ async function runAndroid(options = {}) {
|
|
|
1514
1624
|
const remoteSettingError = remoteDeviceSettingError(settings);
|
|
1515
1625
|
if (remoteSettingError) return fail("STIM_BAD_ARG", remoteSettingError, `Set ios.remote and android.remote to one of: ${REMOTE_DEVICE_BACKENDS.join(", ")}.`);
|
|
1516
1626
|
const systemImage = resolveSystemImage(systemImageFlag, settings);
|
|
1517
|
-
const variant = resolveVariant(variantFlag, settings);
|
|
1627
|
+
const variant = easProfile !== void 0 ? "debug" : resolveVariant(variantFlag, settings);
|
|
1518
1628
|
const flavorRefusal = productFlavorRefusal({
|
|
1519
1629
|
flavors: readProductFlavors(root),
|
|
1520
1630
|
variant
|
|
@@ -1524,7 +1634,7 @@ async function runAndroid(options = {}) {
|
|
|
1524
1634
|
const cachePolicy = artifactCachePolicy(optimizations, requestedBuildCache, release);
|
|
1525
1635
|
const useBuildCache = cachePolicy.read;
|
|
1526
1636
|
const isExpo = detectIsExpo(root);
|
|
1527
|
-
const physical = deviceFlag
|
|
1637
|
+
const physical = isPhysicalDeviceRequest(deviceFlag);
|
|
1528
1638
|
if (physical && deviceFlag === "") return fail("STIM_BAD_ARG", "--device was given an empty serial.", "Pass `--device` on its own to take the first connected device this workspace can lease, or `--device <serial>` to name one.");
|
|
1529
1639
|
if (physical && commandRemoteBackend) return fail("STIM_BAD_ARG", "--device installs on a device connected to this machine, and --remote installs on a remote one.", "Pass only one of --device and --remote.");
|
|
1530
1640
|
const noWait = waitFlag === false;
|
|
@@ -1536,6 +1646,7 @@ async function runAndroid(options = {}) {
|
|
|
1536
1646
|
const waitSeconds = waitParsed.seconds;
|
|
1537
1647
|
const remoteBackend = physical ? null : commandRemoteBackend ?? remoteAndroidSetting(settings);
|
|
1538
1648
|
const imageRefusal = systemImageRefusal({
|
|
1649
|
+
slot,
|
|
1539
1650
|
flag: systemImageFlag,
|
|
1540
1651
|
resolved: systemImage,
|
|
1541
1652
|
physical,
|
|
@@ -1543,6 +1654,17 @@ async function runAndroid(options = {}) {
|
|
|
1543
1654
|
listImages: listSystemImages
|
|
1544
1655
|
});
|
|
1545
1656
|
if (imageRefusal) return fail(imageRefusal.code, imageRefusal.message, imageRefusal.remedy);
|
|
1657
|
+
const easBuild = await resolveEasBuild({
|
|
1658
|
+
root,
|
|
1659
|
+
platform: PLATFORM,
|
|
1660
|
+
profile: easProfile,
|
|
1661
|
+
note: out,
|
|
1662
|
+
isExpo,
|
|
1663
|
+
physical,
|
|
1664
|
+
selectors: [variantFlag],
|
|
1665
|
+
buildCache: requestedBuildCache
|
|
1666
|
+
});
|
|
1667
|
+
if (isEasBuildFailure(easBuild)) return fail(easBuild.code, easBuild.message, easBuild.remedy);
|
|
1546
1668
|
const requestedSerial = typeof deviceFlag === "string" ? deviceFlag : null;
|
|
1547
1669
|
let androidPackage = detectAndroidPackage(root);
|
|
1548
1670
|
record.bundleId = androidPackage;
|
|
@@ -1673,7 +1795,8 @@ async function runAndroid(options = {}) {
|
|
|
1673
1795
|
const diag = noDeviceDiagnostic({
|
|
1674
1796
|
reason: `Could not ensure an owned Android emulator: ${err?.message || err}`,
|
|
1675
1797
|
logFile: emuLog,
|
|
1676
|
-
|
|
1798
|
+
localEmulator: !(err instanceof AvdRecoveryError),
|
|
1799
|
+
remedy: err instanceof AvdRecoveryError ? err.remedy : "Check that JAVA_HOME and ANDROID_HOME are set correctly, and that an arm64 system image is installed (`sdkmanager \"system-images;android-36;google_apis;arm64-v8a\"`)."
|
|
1677
1800
|
});
|
|
1678
1801
|
return fail(NO_DEVICE, diag.message, diag.remedy, {
|
|
1679
1802
|
lines: diag.lines,
|
|
@@ -1753,6 +1876,18 @@ async function runAndroid(options = {}) {
|
|
|
1753
1876
|
let storeSources = [];
|
|
1754
1877
|
let apkPath = null;
|
|
1755
1878
|
async function resolveInitialFingerprint() {
|
|
1879
|
+
if (easBuild?.ok) {
|
|
1880
|
+
hash = storeHash = easBuild.fingerprint;
|
|
1881
|
+
cacheKey = storeKey = easBuild.cacheKey;
|
|
1882
|
+
apkPath = easBuild.path;
|
|
1883
|
+
record.fingerprint = hash;
|
|
1884
|
+
record.cacheKey = cacheKey;
|
|
1885
|
+
record.cacheHit = easBuild.cacheHit;
|
|
1886
|
+
record.cacheSkipped = !useBuildCache;
|
|
1887
|
+
providerName = "eas";
|
|
1888
|
+
stats.setCacheKey(cacheKey);
|
|
1889
|
+
return true;
|
|
1890
|
+
}
|
|
1756
1891
|
const fingerprintTimer = stepTimer(now);
|
|
1757
1892
|
try {
|
|
1758
1893
|
const computed = await fingerprint(root, { platform: PLATFORM });
|
|
@@ -1825,6 +1960,13 @@ async function runAndroid(options = {}) {
|
|
|
1825
1960
|
return true;
|
|
1826
1961
|
}
|
|
1827
1962
|
const runFromFingerprint = async () => {
|
|
1963
|
+
if (metroCheck && metroPort !== null && optimizations.metroWarmup) prewarmMetro({
|
|
1964
|
+
port: metroPort,
|
|
1965
|
+
platform: "android",
|
|
1966
|
+
isExpo,
|
|
1967
|
+
appId: androidPackage,
|
|
1968
|
+
bundleUrl: metroWarmupUrlSetting(settings, "android")
|
|
1969
|
+
});
|
|
1828
1970
|
if (!await resolveInitialFingerprint()) return phaseFailure;
|
|
1829
1971
|
let remote = null;
|
|
1830
1972
|
let abandonedRemote = false;
|
|
@@ -1877,7 +2019,7 @@ async function runAndroid(options = {}) {
|
|
|
1877
2019
|
} else phase("cache", `remote miss (${remote.name}) ${remoteTimer()}`);
|
|
1878
2020
|
}
|
|
1879
2021
|
}
|
|
1880
|
-
await resolveRemoteArtifact();
|
|
2022
|
+
if (!easBuild) await resolveRemoteArtifact();
|
|
1881
2023
|
let waitedForBuild = null;
|
|
1882
2024
|
let releasedWait = null;
|
|
1883
2025
|
async function waitForSharedBuild() {
|
|
@@ -1910,7 +2052,7 @@ async function runAndroid(options = {}) {
|
|
|
1910
2052
|
releasedWait = null;
|
|
1911
2053
|
const holder = attempt.held;
|
|
1912
2054
|
const who = holder.projectRoot || "another workspace";
|
|
1913
|
-
phase("build", `${who} is already building ${shortHash(hash)} (pid ${holder.pid})${holder.logFile ? ` -- tail ${holder.logFile}` : ""}`);
|
|
2055
|
+
phase("build", `${who} is already building ${shortHash(hash)} (pid ${holder.pid})${holder.logFile ? ` -- tail ${holder.logFile}` : ""} -- stim guide lifecycle concurrency`);
|
|
1914
2056
|
let waited = null;
|
|
1915
2057
|
try {
|
|
1916
2058
|
const ceilingMs = WAIT_CEILING_MS - (now() - waitStarted);
|
|
@@ -1942,7 +2084,7 @@ async function runAndroid(options = {}) {
|
|
|
1942
2084
|
pid: holder.pid,
|
|
1943
2085
|
ms: waited.waitedMs
|
|
1944
2086
|
};
|
|
1945
|
-
phase("build", `waited ${formatDuration(waited.waitedMs)} for ${who}'s build -> installed from cache`);
|
|
2087
|
+
phase("build", `waited ${formatDuration(waited.waitedMs)} for ${who}'s build -> installed from cache -- stim guide lifecycle concurrency`);
|
|
1946
2088
|
} else if (waited?.lockReleased) {
|
|
1947
2089
|
failedHolder = void 0;
|
|
1948
2090
|
releasedWait = {
|
|
@@ -1961,7 +2103,7 @@ async function runAndroid(options = {}) {
|
|
|
1961
2103
|
}
|
|
1962
2104
|
return true;
|
|
1963
2105
|
}
|
|
1964
|
-
if (!await waitForSharedBuild()) return phaseFailure;
|
|
2106
|
+
if (!easBuild && !await waitForSharedBuild()) return phaseFailure;
|
|
1965
2107
|
let swapDir = null;
|
|
1966
2108
|
let swapFellBack = false;
|
|
1967
2109
|
const installableCachedApk = async (key, cachedPath) => {
|
|
@@ -2049,7 +2191,7 @@ async function runAndroid(options = {}) {
|
|
|
2049
2191
|
phase("cache", `hit ${shortHash(storeHash)} (post-prebuild key)`);
|
|
2050
2192
|
if (releasedWait) {
|
|
2051
2193
|
waitedForBuild = releasedWait.facts;
|
|
2052
|
-
phase("build", `waited ${formatDuration(waitedForBuild.ms)} for ${releasedWait.who}'s build -> installed from cache`);
|
|
2194
|
+
phase("build", `waited ${formatDuration(waitedForBuild.ms)} for ${releasedWait.who}'s build -> installed from cache -- stim guide lifecycle concurrency`);
|
|
2053
2195
|
}
|
|
2054
2196
|
}
|
|
2055
2197
|
}
|
|
@@ -2161,8 +2303,10 @@ async function runAndroid(options = {}) {
|
|
|
2161
2303
|
}
|
|
2162
2304
|
return true;
|
|
2163
2305
|
}
|
|
2164
|
-
|
|
2165
|
-
|
|
2306
|
+
if (!easBuild) {
|
|
2307
|
+
await prepareCachedArtifact();
|
|
2308
|
+
if (!await buildArtifact()) return phaseFailure;
|
|
2309
|
+
}
|
|
2166
2310
|
record.appPath = apkPath;
|
|
2167
2311
|
let leaseHandle = null;
|
|
2168
2312
|
let stopLeaseSignals = null;
|
|
@@ -2207,6 +2351,7 @@ async function runAndroid(options = {}) {
|
|
|
2207
2351
|
}
|
|
2208
2352
|
try {
|
|
2209
2353
|
return await finishAndroidRun({
|
|
2354
|
+
slot,
|
|
2210
2355
|
lease: leaseHandle,
|
|
2211
2356
|
releaseLease,
|
|
2212
2357
|
root,
|
|
@@ -2223,6 +2368,8 @@ async function runAndroid(options = {}) {
|
|
|
2223
2368
|
physical,
|
|
2224
2369
|
remoteDevice,
|
|
2225
2370
|
bootPromise,
|
|
2371
|
+
resolveAvdSerial,
|
|
2372
|
+
waitForDeviceBoot,
|
|
2226
2373
|
bootDuration: () => bootDuration,
|
|
2227
2374
|
apkPath,
|
|
2228
2375
|
androidPackage,
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { t as getExecutor } from "./exec-
|
|
2
|
-
import { k as withDirLock, u as getConfigDir } from "./config-
|
|
3
|
-
import { r as register } from "./cache-manifest-
|
|
4
|
-
import { D as workspaceLogsDir, x as sharedCcache, y as ensureWorkspaceStorage } from "./state-
|
|
1
|
+
import { t as getExecutor } from "./exec-CyylIdq9.mjs";
|
|
2
|
+
import { k as withDirLock, u as getConfigDir } from "./config-CcefMY2Q.mjs";
|
|
3
|
+
import { r as register } from "./cache-manifest-CXR-Y8pH.mjs";
|
|
4
|
+
import { D as workspaceLogsDir, x as sharedCcache, y as ensureWorkspaceStorage } from "./state-B3XA2wp2.mjs";
|
|
5
5
|
import { n as stripAnsi, r as waitForChild, t as createLineReader } from "./process-output-C7IlIzd7.mjs";
|
|
6
6
|
import { l as phaseLine } from "./command-output-8wHMEde0.mjs";
|
|
7
|
-
import { n as androidHome } from "./android-
|
|
8
|
-
import { f as startBuildHeartbeat, r as HEARTBEAT_INTERVAL_MS } from "./xcode-
|
|
7
|
+
import { n as androidHome } from "./android-BqVIJoG7.mjs";
|
|
8
|
+
import { f as startBuildHeartbeat, r as HEARTBEAT_INTERVAL_MS } from "./xcode-D7sAp-Zy.mjs";
|
|
9
9
|
import { accessSync, constants, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, symlinkSync, writeFileSync } from "node:fs";
|
|
10
10
|
import { createHash, randomUUID } from "node:crypto";
|
|
11
11
|
import { dirname, isAbsolute, join, relative } from "node:path";
|