stim 1.2.0 → 1.3.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/README.md +34 -3
- package/dist/{android-Bcmev2s2.mjs → android-CT-PkXUh.mjs} +157 -62
- package/dist/{android-cas-D6HPZ6UQ.mjs → android-cas-DXorPCi_.mjs} +5 -5
- package/dist/android-hMj83VpN.mjs +1374 -0
- package/dist/{app-install-NFxUahnH.mjs → app-install-DIp9riNx.mjs} +6 -6
- package/dist/{build-slots-D0b6sJyo.mjs → build-slots-C4AouyNz.mjs} +2 -2
- package/dist/{cache-manifest-Cu4_Znw9.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 +3 -1
- package/dist/collector-run.mjs +33 -8
- package/dist/{config-CMtIaTk9.mjs → config-CcefMY2Q.mjs} +93 -12
- package/dist/{deps-BLBsTNDS.mjs → deps-0Yt0Inly.mjs} +1 -1
- package/dist/{dev-client-RpvXxCXV.mjs → dev-client-VfSZNLT4.mjs} +186 -9
- package/dist/{device-CoCPmazS.mjs → device-Bbv45Z9Z.mjs} +19 -11
- package/dist/{device-lease-Dpik41RY.mjs → device-lease-CJwK7OZr.mjs} +107 -83
- package/dist/{device-pool-B5pEVa3t.mjs → device-pool-CcBNTvM7.mjs} +18 -12
- package/dist/{device-remote-E81KAdXf.mjs → device-remote-DAA0Zm6Z.mjs} +7 -7
- package/dist/{doctor-CqPeMsSm.mjs → doctor-B4EITgQB.mjs} +11 -12
- package/dist/{doctor-D_CX7ib5.mjs → doctor-BueDfLYG.mjs} +4 -4
- package/dist/{error-diagnostics-Dw8Gn3yw.mjs → error-diagnostics-DwhNBFDU.mjs} +62 -52
- package/dist/{gc-B7KkDinF.mjs → gc-BRu62v4o.mjs} +66 -57
- package/dist/{guide-BS2Xc24o.mjs → guide-D95E0u3i.mjs} +307 -74
- package/dist/{ios-B7KLgIp5.mjs → ios-DAju3TVy.mjs} +183 -54
- package/dist/{ios-D1h1Di39.mjs → ios-Deh5js5V.mjs} +78 -40
- package/dist/{ios-device-xNt0Lg6v.mjs → ios-device-CWOM_S44.mjs} +1 -1
- package/dist/{logs-DcX9AvRF.mjs → logs-CAdwW6c2.mjs} +34 -6
- package/dist/{logs-query-oqn47Ffx.mjs → logs-query-Cdp5aaCH.mjs} +23 -6
- package/dist/{metro-COslRK_F.mjs → metro-CORqXyTL.mjs} +2 -2
- package/dist/{ndjson-DcAtEx_K.mjs → ndjson-BZwI1NBv.mjs} +5 -2
- package/dist/{ownership-CGPart6T.mjs → ownership-8O073GIC.mjs} +3 -3
- package/dist/{ownership-CYOyqxUo.mjs → ownership-DXcol4oU.mjs} +3 -3
- package/dist/{ownership-claim-7zRxxTfZ.mjs → ownership-claim-BtPtJVmq.mjs} +38 -23
- package/dist/{project-BBTVABUz.mjs → project-fKmuIaMI.mjs} +2 -2
- package/dist/{reclaim-C5Jmuahx.mjs → reclaim-CUmwucWG.mjs} +80 -74
- package/dist/{reload-Z8nKifAk.mjs → reload-CWpXoOgN.mjs} +32 -24
- package/dist/{server-bare-DEk37Mz2.mjs → server-bare-DEzPZLvf.mjs} +2 -2
- package/dist/server-expo-44em5Pmg.mjs +2 -0
- package/dist/{server-expo-C3ktAwUG.mjs → server-expo-DizT4OMk.mjs} +4 -4
- package/dist/{settings-DwU1_U_q.mjs → settings-DEJf6_kJ.mjs} +2 -2
- package/dist/slot-launch-CZklomj6.mjs +19 -0
- package/dist/{start-BrY4EuIg.mjs → start-B5G4NjiA.mjs} +11 -11
- package/dist/{state-CZ-ER-8d.mjs → state-B3XA2wp2.mjs} +9 -9
- package/dist/{stats-BCWEVLrM.mjs → stats-BR-kwj0B.mjs} +1 -1
- package/dist/{stats-BsF15Bti.mjs → stats-CaaQr4yN.mjs} +3 -3
- package/dist/{status-DFI1Hfpg.mjs → status-CI9clRSb.mjs} +55 -24
- package/dist/{stop-B7DR4awK.mjs → stop-CQhffpmX.mjs} +2 -2
- package/dist/{stop-Btg770vV.mjs → stop-CjlgDk0a.mjs} +93 -42
- package/dist/supervisor-run.d.mts +1 -2
- package/dist/supervisor-run.mjs +5 -5
- package/dist/{remote-cache-DDmJ1Roq.mjs → teardown--gESs9ck.mjs} +985 -887
- package/dist/{workspace-process-lock-BKfdys1q.mjs → workspace-process-lock-IBj1Towr.mjs} +1 -1
- package/dist/{worktree-BPTbI-LN.mjs → worktree-DtQGO9-s.mjs} +121 -111
- package/dist/{worktree-C79xx_kM.mjs → worktree-Eq6TLH8N.mjs} +1 -1
- package/dist/{xcode-DeKCribl.mjs → xcode-xnxyvJkD.mjs} +159 -96
- package/package.json +5 -5
- package/dist/android-K63_0UfF.mjs +0 -727
- package/dist/build-lock-BsVoLDaq.mjs +0 -645
- package/dist/server-expo-Drd7ksHu.mjs +0 -2
package/README.md
CHANGED
|
@@ -44,8 +44,9 @@ stim stop
|
|
|
44
44
|
|
|
45
45
|
For `stim logs --errors`, a clean check requires exit code 0 and no matching
|
|
46
46
|
errors in the captured logs. Exit code 0 alone means the query succeeded, even
|
|
47
|
-
when it prints errors
|
|
48
|
-
|
|
47
|
+
when it prints errors. A workspace that has never produced a log timeline
|
|
48
|
+
refuses with `STIM_NO_PROJECT`; in a monorepo, the error names the nearest
|
|
49
|
+
registered descendant app with logs when one exists.
|
|
49
50
|
|
|
50
51
|
Use `stim start --reset-cache` to recover from stale Metro transforms or file-map
|
|
51
52
|
state. It restarts only this app's verified owned Metro, keeping its port and
|
|
@@ -77,6 +78,17 @@ Stim builds or restores the app, installs it, launches it, and checks launch
|
|
|
77
78
|
readiness. Plain output streams progress and reports the complete result. Use
|
|
78
79
|
`--json` when a script needs structured data.
|
|
79
80
|
|
|
81
|
+
To use an existing EAS development build, run `stim ios --eas-profile ios-simulator`
|
|
82
|
+
or `stim android --eas-profile development` with your project's profile name.
|
|
83
|
+
The profile must be an internal development build. Add `--device` for a connected
|
|
84
|
+
phone; iOS requires `ios.simulator: true` for a simulator and false for a phone.
|
|
85
|
+
Stim downloads a matching native build and connects it to the workspace's Metro.
|
|
86
|
+
EAS CLI manages the downloaded artifact cache. An iOS provisioning failure
|
|
87
|
+
points to EAS device registration and rebuild commands.
|
|
88
|
+
On a miss it stops and prints the EAS build command, which may incur charges;
|
|
89
|
+
it never triggers the build automatically. See `stim guide lifecycle eas` for
|
|
90
|
+
setup, environment handling, and cache behavior.
|
|
91
|
+
|
|
80
92
|
Use `stim ios --scheme "App Staging"` when a project has several shared Xcode
|
|
81
93
|
app schemes. Combine it with `--configuration Release` if needed. Without the
|
|
82
94
|
flag, Stim keeps its automatic scheme selection. Explicit schemes use separate
|
|
@@ -117,7 +129,7 @@ eligible `.env` and local configuration files. Existing entries are preserved;
|
|
|
117
129
|
existing ignored directories such as `node_modules` are skipped whole.
|
|
118
130
|
|
|
119
131
|
`stim worktree warm --refresh` brings the source checkout up to date first: it
|
|
120
|
-
fetches, fast-forwards whatever branch is checked out there, and installs what
|
|
132
|
+
checks the upstream, fetches changes when needed, fast-forwards whatever branch is checked out there, and installs what
|
|
121
133
|
the new commits moved before copying. It refuses a source checkout it cannot
|
|
122
134
|
move (uncommitted tracked changes, a rebase or merge in progress, a detached
|
|
123
135
|
HEAD, a diverged branch) and never switches branches. One lock per repository
|
|
@@ -167,3 +179,22 @@ Update programmatic imports from `stim-cli/cache-manifest` to
|
|
|
167
179
|
`stim/cache-manifest`. The `@stim-cli/*` packages keep their names.
|
|
168
180
|
|
|
169
181
|
MIT License.
|
|
182
|
+
|
|
183
|
+
### Multiple devices in one workspace
|
|
184
|
+
|
|
185
|
+
Use a stable slot name for each simulator, emulator, or physical device. Slots
|
|
186
|
+
can use identical models and all share the workspace's Metro server and build
|
|
187
|
+
cache. Omit `--slot` to reuse your existing default device.
|
|
188
|
+
|
|
189
|
+
```sh
|
|
190
|
+
stim ios --slot phone
|
|
191
|
+
stim ios --slot tablet --device-type "iPad Pro 13-inch (M4)"
|
|
192
|
+
stim ios --slot hardware --device <udid>
|
|
193
|
+
stim logs --slot tablet --source device
|
|
194
|
+
stim stop --slot tablet
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
`stim status` shows every slot. Plain `stim stop` stops the whole workspace;
|
|
198
|
+
`--slot` preserves the shared server and other devices. `stim reload ios` or
|
|
199
|
+
`stim reload android` can reload multiple connected devices. Read
|
|
200
|
+
`stim guide lifecycle options` for launch verification and recycling behavior.
|
|
@@ -1,34 +1,35 @@
|
|
|
1
1
|
import { t as getExecutor } from "./exec-CyylIdq9.mjs";
|
|
2
|
-
import { C as withConfigLock, S as upsertProject, b as setDevice, 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-
|
|
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-DDmJ1Roq.mjs";
|
|
11
|
-
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-DwU1_U_q.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-BtPtJVmq.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-hMj83VpN.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--gESs9ck.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-VfSZNLT4.mjs";
|
|
17
|
+
import { t as withWorkspaceProcessLock } from "./workspace-process-lock-IBj1Towr.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-CcBNTvM7.mjs";
|
|
20
|
+
import { r as releaseBuildSlot, t as acquireBuildSlot } from "./build-slots-C4AouyNz.mjs";
|
|
21
|
+
import { a as statsProjectKey, i as recordRunStats, n as readRunEstimates, t as createRunRecorder } from "./stats-BR-kwj0B.mjs";
|
|
22
|
+
import { S as ensureOwnedDevice, _ as AvdBootError, f as startBuildHeartbeat, p as tailLines, r as HEARTBEAT_INTERVAL_MS, v as AvdRecoveryError, w as unknownAndroidSystemImageRefusal, x as ensureBooted, y as checkDeviceCapacity } from "./xcode-xnxyvJkD.mjs";
|
|
23
|
+
import { a as ensureMetroReachable, b as ownedSessionName, l as resolveRemoteContext, n as binOnPath, o as ensureRemoteBootOwned, s as remoteAndroidDeps } from "./device-remote-DAA0Zm6Z.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-DXorPCi_.mjs";
|
|
25
|
+
import { n as nativeRunCommand, t as launchSlotScope } from "./slot-launch-CZklomj6.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-DwhNBFDU.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, device, 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,
|
|
@@ -878,6 +893,7 @@ async function verifyAndroidRun({ root, release, remoteRelease, remoteDevice, ve
|
|
|
878
893
|
const verification = metroCheck ? await verifyLaunched({
|
|
879
894
|
timeoutMs,
|
|
880
895
|
requireBundleResponse: true,
|
|
896
|
+
slot: launchSlotScope(root, slot),
|
|
881
897
|
onReadinessPending: () => phase("readiness", "waiting for app readiness (up to 30s after bundle load)"),
|
|
882
898
|
logsDir,
|
|
883
899
|
since: launchedAt,
|
|
@@ -909,10 +925,10 @@ async function verifyAndroidRun({ root, release, remoteRelease, remoteDevice, ve
|
|
|
909
925
|
phase("verify", chalk.red(`FATAL after ${formatDuration(verification.waitedMs ?? 0)}: ${reason}`));
|
|
910
926
|
for (const line of launchErrorPreview(verification.errors ?? [], root)) phase("", chalk.red(line));
|
|
911
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.");
|
|
912
|
-
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.`));
|
|
913
929
|
else if (verification.processAlive === true && metroPort !== null) {
|
|
914
930
|
const reloadRemedy = physical ? `run \`agent-device metro reload --metro-port ${metroPort}\`.` : "run `stim reload android`.";
|
|
915
|
-
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.`));
|
|
916
932
|
}
|
|
917
933
|
return { state: LAUNCH_FATAL };
|
|
918
934
|
}
|
|
@@ -923,7 +939,7 @@ async function verifyAndroidRun({ root, release, remoteRelease, remoteDevice, ve
|
|
|
923
939
|
for (const line of report.lines) phase("launch", chalk.yellow(line));
|
|
924
940
|
if (report.lines.length > 0 && verification.processAlive === true && metroPort !== null) {
|
|
925
941
|
const reloadRemedy = physical ? `run \`agent-device metro reload --metro-port ${metroPort}\`.` : "run `stim reload android`.";
|
|
926
|
-
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.`));
|
|
927
943
|
}
|
|
928
944
|
return {
|
|
929
945
|
state: true,
|
|
@@ -952,7 +968,7 @@ async function verifyAndroidRun({ root, release, remoteRelease, remoteDevice, ve
|
|
|
952
968
|
})) phase("", chalk.yellow(line));
|
|
953
969
|
return { state: LAUNCH_UNVERIFIED };
|
|
954
970
|
}
|
|
955
|
-
async function resolveInstallSerial({ root, device, physical, remoteDevice, serial, resolveAvdSerial, waitForDeviceBoot, phase }) {
|
|
971
|
+
async function resolveInstallSerial({ root, slot, device, physical, remoteDevice, serial, resolveAvdSerial, waitForDeviceBoot, phase }) {
|
|
956
972
|
if (physical || remoteDevice || !device.owned || !device.avdName) return serial;
|
|
957
973
|
const resolved = resolveAvdSerial(device.avdName, { timeoutMs: 5e3 });
|
|
958
974
|
if (!resolved.serial) throw new Error(`Could not verify a running owned AVD ${device.avdName} before installation.`);
|
|
@@ -965,16 +981,16 @@ async function resolveInstallSerial({ root, device, physical, remoteDevice, seri
|
|
|
965
981
|
}
|
|
966
982
|
const consolePort = Number(resolved.serial.replace(/^emulator-/, ""));
|
|
967
983
|
withConfigLock(() => {
|
|
968
|
-
const current = loadConfig()?.projects?.[root]?.
|
|
984
|
+
const current = deviceSlotPlatforms(loadConfig()?.projects?.[root], slot)?.android;
|
|
969
985
|
if (!current?.owned || current.avdName !== device.avdName) throw new Error("The owned emulator assignment changed before installation.");
|
|
970
986
|
if (current.consolePort !== consolePort) setDevice(root, PLATFORM, {
|
|
971
987
|
...current,
|
|
972
988
|
consolePort
|
|
973
|
-
});
|
|
989
|
+
}, slot);
|
|
974
990
|
});
|
|
975
991
|
return resolved.serial;
|
|
976
992
|
}
|
|
977
|
-
async function finishAndroidRun({ lease, releaseLease, root, 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 }) {
|
|
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 }) {
|
|
978
994
|
let androidPackage = initialPackage;
|
|
979
995
|
let leaseWarned = false;
|
|
980
996
|
const raiseLeaseFor = (boundMs, beforeInstall) => {
|
|
@@ -991,13 +1007,12 @@ async function finishAndroidRun({ lease, releaseLease, root, json, metroCheck, u
|
|
|
991
1007
|
return null;
|
|
992
1008
|
};
|
|
993
1009
|
const booted = await bootPromise;
|
|
1010
|
+
const runCommand = nativeRunCommand("android", slot, {
|
|
1011
|
+
physical,
|
|
1012
|
+
deviceId: booted.serial
|
|
1013
|
+
});
|
|
994
1014
|
if (booted.failed) {
|
|
995
|
-
const diag =
|
|
996
|
-
reason: booted.reason ?? "The emulator did not boot.",
|
|
997
|
-
logFile: emuLog,
|
|
998
|
-
remedy: "Run `stim status` to see what Stim thinks it owns; re-running `stim android` creates a fresh owned AVD.",
|
|
999
|
-
localEmulator: !physical
|
|
1000
|
-
});
|
|
1015
|
+
const diag = diagnoseBootFailure(booted, emuLog, runCommand, physical);
|
|
1001
1016
|
return fail(NO_DEVICE, diag.message, diag.remedy, {
|
|
1002
1017
|
lines: diag.lines,
|
|
1003
1018
|
logPath: diag.logPath ? displayPath(root, diag.logPath) : null
|
|
@@ -1007,6 +1022,7 @@ async function finishAndroidRun({ lease, releaseLease, root, json, metroCheck, u
|
|
|
1007
1022
|
try {
|
|
1008
1023
|
serial = await resolveInstallSerial({
|
|
1009
1024
|
root,
|
|
1025
|
+
slot,
|
|
1010
1026
|
device,
|
|
1011
1027
|
physical,
|
|
1012
1028
|
remoteDevice,
|
|
@@ -1016,7 +1032,7 @@ async function finishAndroidRun({ lease, releaseLease, root, json, metroCheck, u
|
|
|
1016
1032
|
phase
|
|
1017
1033
|
});
|
|
1018
1034
|
} catch (error) {
|
|
1019
|
-
return fail(NO_DEVICE, error instanceof Error ? error.message : String(error),
|
|
1035
|
+
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.`);
|
|
1020
1036
|
}
|
|
1021
1037
|
phase("device", physical ? `${device.deviceName || serial} (${serial}) connected, not owned by Stim` : `${device.avdName || serial} (${serial}) ${device.adopted || device.adoptionPending ? "adopted" : "booted"} ${bootDuration()}`);
|
|
1022
1038
|
const packageFromApk = readApkPackage(apkPath);
|
|
@@ -1049,7 +1065,7 @@ async function finishAndroidRun({ lease, releaseLease, root, json, metroCheck, u
|
|
|
1049
1065
|
if (adopting) try {
|
|
1050
1066
|
withConfigLock(() => {
|
|
1051
1067
|
const config = loadConfig();
|
|
1052
|
-
const current = config?.projects?.[root]?.
|
|
1068
|
+
const current = deviceSlotPlatforms(config?.projects?.[root], slot)?.android;
|
|
1053
1069
|
if (!config || !current || current.avdName !== device.avdName) throw new Error("The adopted emulator assignment changed during installation.");
|
|
1054
1070
|
delete current.adoptionPending;
|
|
1055
1071
|
saveConfig(config);
|
|
@@ -1111,6 +1127,7 @@ async function finishAndroidRun({ lease, releaseLease, root, json, metroCheck, u
|
|
|
1111
1127
|
if (launched.failed) {
|
|
1112
1128
|
printNativeCrashReport({
|
|
1113
1129
|
root,
|
|
1130
|
+
slot,
|
|
1114
1131
|
platform: "android",
|
|
1115
1132
|
deviceId: serial,
|
|
1116
1133
|
appId: androidPackage,
|
|
@@ -1173,6 +1190,7 @@ async function finishAndroidRun({ lease, releaseLease, root, json, metroCheck, u
|
|
|
1173
1190
|
if (physical) raiseLeaseFor(0, false);
|
|
1174
1191
|
const collectorPid = await startCollector({
|
|
1175
1192
|
root,
|
|
1193
|
+
slot,
|
|
1176
1194
|
serial,
|
|
1177
1195
|
packageName: androidPackage,
|
|
1178
1196
|
spawn,
|
|
@@ -1186,6 +1204,7 @@ async function finishAndroidRun({ lease, releaseLease, root, json, metroCheck, u
|
|
|
1186
1204
|
if (physical) raiseLeaseFor(release ? RELEASE_VERIFY_WAIT_MS : DEBUG_VERIFY_STEP_MS, false);
|
|
1187
1205
|
const { state: launchState, warning: launchWarning } = await verifyAndroidRun({
|
|
1188
1206
|
root,
|
|
1207
|
+
slot,
|
|
1189
1208
|
release,
|
|
1190
1209
|
remoteRelease,
|
|
1191
1210
|
remoteDevice: Boolean(remoteDevice),
|
|
@@ -1219,6 +1238,7 @@ async function finishAndroidRun({ lease, releaseLease, root, json, metroCheck, u
|
|
|
1219
1238
|
const leaseFacts = lease?.facts() ?? null;
|
|
1220
1239
|
releaseLease();
|
|
1221
1240
|
const facts = reportAndroidResult({
|
|
1241
|
+
slot,
|
|
1222
1242
|
json,
|
|
1223
1243
|
useBuildCache,
|
|
1224
1244
|
variant,
|
|
@@ -1250,6 +1270,14 @@ async function finishAndroidRun({ lease, releaseLease, root, json, metroCheck, u
|
|
|
1250
1270
|
facts
|
|
1251
1271
|
};
|
|
1252
1272
|
}
|
|
1273
|
+
function diagnoseBootFailure(booted, logFile, runCommand, physical) {
|
|
1274
|
+
return noDeviceDiagnostic({
|
|
1275
|
+
reason: booted.reason ?? "The emulator did not boot.",
|
|
1276
|
+
logFile,
|
|
1277
|
+
remedy: booted.remedy ?? `Run \`stim status\` to see what Stim thinks it owns; re-running \`${runCommand}\` creates a fresh owned AVD.`,
|
|
1278
|
+
localEmulator: !physical
|
|
1279
|
+
});
|
|
1280
|
+
}
|
|
1253
1281
|
//#endregion
|
|
1254
1282
|
//#region src/commands/android.ts
|
|
1255
1283
|
const FALLBACK_LINES = 5;
|
|
@@ -1281,7 +1309,7 @@ function androidCommand(program) {
|
|
|
1281
1309
|
registerAndroid(program);
|
|
1282
1310
|
}
|
|
1283
1311
|
function registerAndroid(program) {
|
|
1284
|
-
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.
|
|
1312
|
+
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) => {
|
|
1285
1313
|
if (REMOTE_DEVICE_BACKENDS.includes(value)) return value;
|
|
1286
1314
|
throw new InvalidArgumentError(`expected one of: ${REMOTE_DEVICE_BACKENDS.join(", ")}`);
|
|
1287
1315
|
}).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) => {
|
|
@@ -1291,8 +1319,10 @@ function registerAndroid(program) {
|
|
|
1291
1319
|
process.exit(1);
|
|
1292
1320
|
return;
|
|
1293
1321
|
}
|
|
1294
|
-
if (!(await runAndroid({
|
|
1322
|
+
if (!(await withWorkspaceProcessLock(workspaceDir(root), "native-run", () => runAndroid({
|
|
1295
1323
|
root,
|
|
1324
|
+
slot: opts.slot,
|
|
1325
|
+
easProfile: opts.easProfile,
|
|
1296
1326
|
json: Boolean(opts.json),
|
|
1297
1327
|
metroCheck: opts.metroCheck !== false,
|
|
1298
1328
|
useBuildCache: opts.buildCache !== false,
|
|
@@ -1302,10 +1332,13 @@ function registerAndroid(program) {
|
|
|
1302
1332
|
device: opts.device ?? null,
|
|
1303
1333
|
wait: opts.wait,
|
|
1304
1334
|
waitConflict: waitFlagConflict(process.argv)
|
|
1335
|
+
}), {
|
|
1336
|
+
external: true,
|
|
1337
|
+
waitMs: 18e5
|
|
1305
1338
|
})).ok) process.exit(1);
|
|
1306
1339
|
});
|
|
1307
1340
|
}
|
|
1308
|
-
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, 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) } = {}) {
|
|
1341
|
+
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) } = {}) {
|
|
1309
1342
|
return {
|
|
1310
1343
|
root,
|
|
1311
1344
|
json,
|
|
@@ -1319,6 +1352,8 @@ function resolveRunAndroidOptions({ root, json = false, remoteDevice: commandRem
|
|
|
1319
1352
|
detectRemoteProviders,
|
|
1320
1353
|
metroCheck,
|
|
1321
1354
|
useBuildCache,
|
|
1355
|
+
easProfile,
|
|
1356
|
+
resolveEasBuild,
|
|
1322
1357
|
variantFlag,
|
|
1323
1358
|
systemImageFlag,
|
|
1324
1359
|
deviceFlag,
|
|
@@ -1387,8 +1422,38 @@ function resolveRunAndroidOptions({ root, json = false, remoteDevice: commandRem
|
|
|
1387
1422
|
emit
|
|
1388
1423
|
};
|
|
1389
1424
|
}
|
|
1425
|
+
function androidSlotOptions(options) {
|
|
1426
|
+
const base = resolveRunAndroidOptions(options);
|
|
1427
|
+
const slot = validateDeviceSlot(options.slot);
|
|
1428
|
+
if (slot === "default") return base;
|
|
1429
|
+
return {
|
|
1430
|
+
...base,
|
|
1431
|
+
ensureDevice: (args) => base.ensureDevice({
|
|
1432
|
+
...args,
|
|
1433
|
+
slot
|
|
1434
|
+
}),
|
|
1435
|
+
checkCapacity: (args) => base.checkCapacity({
|
|
1436
|
+
...args,
|
|
1437
|
+
slot
|
|
1438
|
+
}),
|
|
1439
|
+
selectPool: (args) => base.selectPool({
|
|
1440
|
+
...args,
|
|
1441
|
+
slot
|
|
1442
|
+
}),
|
|
1443
|
+
acquireLease: (args) => base.acquireLease({
|
|
1444
|
+
...args,
|
|
1445
|
+
slot
|
|
1446
|
+
}),
|
|
1447
|
+
makeRunLease: (args) => base.makeRunLease({
|
|
1448
|
+
...args,
|
|
1449
|
+
slot
|
|
1450
|
+
}),
|
|
1451
|
+
writeLaunch: ((projectRoot, platform, record) => base.writeLaunch(projectRoot, platform, record, slot))
|
|
1452
|
+
};
|
|
1453
|
+
}
|
|
1390
1454
|
async function runAndroid(options = {}) {
|
|
1391
|
-
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, 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 } =
|
|
1455
|
+
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);
|
|
1456
|
+
const slot = validateDeviceSlot(options.slot);
|
|
1392
1457
|
const started = now();
|
|
1393
1458
|
const startedAt = new Date(started).toISOString();
|
|
1394
1459
|
const projectProblem = appProjectProblem(root);
|
|
@@ -1433,8 +1498,11 @@ async function runAndroid(options = {}) {
|
|
|
1433
1498
|
};
|
|
1434
1499
|
}
|
|
1435
1500
|
const logsDir = workspaceLogsDir(root);
|
|
1436
|
-
const buildLog = join(logsDir, "
|
|
1437
|
-
const writer = createWriter(buildLog, {
|
|
1501
|
+
const buildLog = join(logsDir, `build-${deviceSlotKey("android", slot)}.ndjson`);
|
|
1502
|
+
const writer = createWriter(buildLog, {
|
|
1503
|
+
truncate: true,
|
|
1504
|
+
fields: { slot }
|
|
1505
|
+
});
|
|
1438
1506
|
const record = {
|
|
1439
1507
|
fingerprint: null,
|
|
1440
1508
|
cacheKey: null,
|
|
@@ -1559,7 +1627,7 @@ async function runAndroid(options = {}) {
|
|
|
1559
1627
|
const remoteSettingError = remoteDeviceSettingError(settings);
|
|
1560
1628
|
if (remoteSettingError) return fail("STIM_BAD_ARG", remoteSettingError, `Set ios.remote and android.remote to one of: ${REMOTE_DEVICE_BACKENDS.join(", ")}.`);
|
|
1561
1629
|
const systemImage = resolveSystemImage(systemImageFlag, settings);
|
|
1562
|
-
const variant = resolveVariant(variantFlag, settings);
|
|
1630
|
+
const variant = easProfile !== void 0 ? "debug" : resolveVariant(variantFlag, settings);
|
|
1563
1631
|
const flavorRefusal = productFlavorRefusal({
|
|
1564
1632
|
flavors: readProductFlavors(root),
|
|
1565
1633
|
variant
|
|
@@ -1569,7 +1637,7 @@ async function runAndroid(options = {}) {
|
|
|
1569
1637
|
const cachePolicy = artifactCachePolicy(optimizations, requestedBuildCache, release);
|
|
1570
1638
|
const useBuildCache = cachePolicy.read;
|
|
1571
1639
|
const isExpo = detectIsExpo(root);
|
|
1572
|
-
const physical = deviceFlag
|
|
1640
|
+
const physical = isPhysicalDeviceRequest(deviceFlag);
|
|
1573
1641
|
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.");
|
|
1574
1642
|
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.");
|
|
1575
1643
|
const noWait = waitFlag === false;
|
|
@@ -1581,6 +1649,7 @@ async function runAndroid(options = {}) {
|
|
|
1581
1649
|
const waitSeconds = waitParsed.seconds;
|
|
1582
1650
|
const remoteBackend = physical ? null : commandRemoteBackend ?? remoteAndroidSetting(settings);
|
|
1583
1651
|
const imageRefusal = systemImageRefusal({
|
|
1652
|
+
slot,
|
|
1584
1653
|
flag: systemImageFlag,
|
|
1585
1654
|
resolved: systemImage,
|
|
1586
1655
|
physical,
|
|
@@ -1588,6 +1657,17 @@ async function runAndroid(options = {}) {
|
|
|
1588
1657
|
listImages: listSystemImages
|
|
1589
1658
|
});
|
|
1590
1659
|
if (imageRefusal) return fail(imageRefusal.code, imageRefusal.message, imageRefusal.remedy);
|
|
1660
|
+
const easBuild = await resolveEasBuild({
|
|
1661
|
+
root,
|
|
1662
|
+
platform: PLATFORM,
|
|
1663
|
+
profile: easProfile,
|
|
1664
|
+
note: out,
|
|
1665
|
+
isExpo,
|
|
1666
|
+
physical,
|
|
1667
|
+
selectors: [variantFlag],
|
|
1668
|
+
buildCache: requestedBuildCache
|
|
1669
|
+
});
|
|
1670
|
+
if (isEasBuildFailure(easBuild)) return fail(easBuild.code, easBuild.message, easBuild.remedy);
|
|
1591
1671
|
const requestedSerial = typeof deviceFlag === "string" ? deviceFlag : null;
|
|
1592
1672
|
let androidPackage = detectAndroidPackage(root);
|
|
1593
1673
|
record.bundleId = androidPackage;
|
|
@@ -1719,7 +1799,7 @@ async function runAndroid(options = {}) {
|
|
|
1719
1799
|
reason: `Could not ensure an owned Android emulator: ${err?.message || err}`,
|
|
1720
1800
|
logFile: emuLog,
|
|
1721
1801
|
localEmulator: !(err instanceof AvdRecoveryError),
|
|
1722
|
-
remedy: err instanceof
|
|
1802
|
+
remedy: err instanceof AvdBootError ? 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\"`)."
|
|
1723
1803
|
});
|
|
1724
1804
|
return fail(NO_DEVICE, diag.message, diag.remedy, {
|
|
1725
1805
|
lines: diag.lines,
|
|
@@ -1799,6 +1879,18 @@ async function runAndroid(options = {}) {
|
|
|
1799
1879
|
let storeSources = [];
|
|
1800
1880
|
let apkPath = null;
|
|
1801
1881
|
async function resolveInitialFingerprint() {
|
|
1882
|
+
if (easBuild?.ok) {
|
|
1883
|
+
hash = storeHash = easBuild.fingerprint;
|
|
1884
|
+
cacheKey = storeKey = easBuild.cacheKey;
|
|
1885
|
+
apkPath = easBuild.path;
|
|
1886
|
+
record.fingerprint = hash;
|
|
1887
|
+
record.cacheKey = cacheKey;
|
|
1888
|
+
record.cacheHit = easBuild.cacheHit;
|
|
1889
|
+
record.cacheSkipped = !useBuildCache;
|
|
1890
|
+
providerName = "eas";
|
|
1891
|
+
stats.setCacheKey(cacheKey);
|
|
1892
|
+
return true;
|
|
1893
|
+
}
|
|
1802
1894
|
const fingerprintTimer = stepTimer(now);
|
|
1803
1895
|
try {
|
|
1804
1896
|
const computed = await fingerprint(root, { platform: PLATFORM });
|
|
@@ -1930,7 +2022,7 @@ async function runAndroid(options = {}) {
|
|
|
1930
2022
|
} else phase("cache", `remote miss (${remote.name}) ${remoteTimer()}`);
|
|
1931
2023
|
}
|
|
1932
2024
|
}
|
|
1933
|
-
await resolveRemoteArtifact();
|
|
2025
|
+
if (!easBuild) await resolveRemoteArtifact();
|
|
1934
2026
|
let waitedForBuild = null;
|
|
1935
2027
|
let releasedWait = null;
|
|
1936
2028
|
async function waitForSharedBuild() {
|
|
@@ -2014,7 +2106,7 @@ async function runAndroid(options = {}) {
|
|
|
2014
2106
|
}
|
|
2015
2107
|
return true;
|
|
2016
2108
|
}
|
|
2017
|
-
if (!await waitForSharedBuild()) return phaseFailure;
|
|
2109
|
+
if (!easBuild && !await waitForSharedBuild()) return phaseFailure;
|
|
2018
2110
|
let swapDir = null;
|
|
2019
2111
|
let swapFellBack = false;
|
|
2020
2112
|
const installableCachedApk = async (key, cachedPath) => {
|
|
@@ -2214,8 +2306,10 @@ async function runAndroid(options = {}) {
|
|
|
2214
2306
|
}
|
|
2215
2307
|
return true;
|
|
2216
2308
|
}
|
|
2217
|
-
|
|
2218
|
-
|
|
2309
|
+
if (!easBuild) {
|
|
2310
|
+
await prepareCachedArtifact();
|
|
2311
|
+
if (!await buildArtifact()) return phaseFailure;
|
|
2312
|
+
}
|
|
2219
2313
|
record.appPath = apkPath;
|
|
2220
2314
|
let leaseHandle = null;
|
|
2221
2315
|
let stopLeaseSignals = null;
|
|
@@ -2260,6 +2354,7 @@ async function runAndroid(options = {}) {
|
|
|
2260
2354
|
}
|
|
2261
2355
|
try {
|
|
2262
2356
|
return await finishAndroidRun({
|
|
2357
|
+
slot,
|
|
2263
2358
|
lease: leaseHandle,
|
|
2264
2359
|
releaseLease,
|
|
2265
2360
|
root,
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { t as getExecutor } from "./exec-CyylIdq9.mjs";
|
|
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-
|
|
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-hMj83VpN.mjs";
|
|
8
|
+
import { f as startBuildHeartbeat, r as HEARTBEAT_INTERVAL_MS } from "./xcode-xnxyvJkD.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";
|