stim 1.14.0 → 1.16.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 +2 -0
- package/dist/activity-BxwdD81S.mjs +609 -0
- package/dist/agent-device-usage-output-Dpih1Tfa.mjs +411 -0
- package/dist/{android-d6rGq1pA.mjs → android-B_NumVyQ.mjs} +169 -81
- package/dist/{android-LbPFRWVL.mjs → android-Ift4kVvt.mjs} +517 -413
- package/dist/android-cas-8U4DPqeR.mjs +167 -0
- package/dist/android-cas-compiler.mjs +2 -2
- package/dist/{android-XpvCrV7q.mjs → android-r2rBL8kV.mjs} +2 -2
- package/dist/{app-install-BpgSgkJj.mjs → app-install-CKWdbXIX.mjs} +128 -151
- package/dist/attempt-Bk2ouQVL.mjs +39 -0
- package/dist/budget-DjrURfQn.mjs +2700 -0
- package/dist/{build-plan-Dcr-3ie0.mjs → build-plan-DjeSb2Xl.mjs} +192 -653
- package/dist/build-slots-rUk77xqh.mjs +132 -0
- package/dist/{cache-manifest-C68QQ0ua.mjs → cache-manifest-DnUHBJaU.mjs} +2 -2
- package/dist/cache-manifest.mjs +1 -1
- package/dist/{chrome-BPCiC3ZX.mjs → chrome-BrcVWQyD.mjs} +1 -1
- package/dist/cli-BkYZ_6hy.mjs +51 -0
- package/dist/cli.mjs +6 -42
- package/dist/client-BTY686R6.mjs +1368 -0
- package/dist/collector-run.mjs +9 -195
- package/dist/{command-output-DVcpw6eE.mjs → command-output-DnJMdEY-.mjs} +1 -1
- package/dist/{config-lvc7d3C2.mjs → config-D0DbpZtM.mjs} +28 -29
- package/dist/{created-devices-CllH_8eb.mjs → created-devices-D5owjN4C.mjs} +2 -2
- package/dist/{dependency-state-DsT7aKeo.mjs → dependency-state-B6cMXZab.mjs} +2 -2
- package/dist/{deps-D9IcKqXS.mjs → deps-urbmfXig.mjs} +3 -3
- package/dist/{detached-entry-DJHoca49.mjs → detached-entry-CiTEgMR1.mjs} +1 -1
- package/dist/dev-client-CroAIjjj.mjs +43 -0
- package/dist/{device-CjuuAJjQ.mjs → device-DZQjF7H5.mjs} +8 -7
- package/dist/device-capacity-BHwxNBlu.mjs +136 -0
- package/dist/device-host-worker.d.mts +1 -0
- package/dist/device-host-worker.mjs +964 -0
- package/dist/device-ios-BadmeL2V.mjs +381 -0
- package/dist/{device-lease-5c3WrVRm.mjs → device-lease-D-hLqx1b.mjs} +43 -36
- package/dist/{device-pool-BiyDyPP-.mjs → device-lease-run-CqjShqWW.mjs} +6 -126
- package/dist/device-pool-CUaEWMMm.mjs +125 -0
- package/dist/{device-remote-D87zEzaq.mjs → device-remote-D5WVWqOq.mjs} +69 -163
- package/dist/{doctor-x9pnU9fq.mjs → doctor-Cimf-gGB.mjs} +241 -103
- package/dist/{error-diagnostics-C5HzcwCW.mjs → error-diagnostics-B9L4Ae0x.mjs} +15 -18
- package/dist/{exec-DK8KaVHK.mjs → exec-CaErVv7t.mjs} +5 -1
- package/dist/{gc-ko6mUAYD.mjs → gc-Bqy4bjEu.mjs} +599 -108
- package/dist/{android-cas-iZK3DOCr.mjs → gradle-BsO1MdsH.mjs} +8 -294
- package/dist/{guide-6R0VNFqI.mjs → guide-B_uyKTxW.mjs} +2511 -239
- package/dist/{guide-status-BZrFPIA_.mjs → guide-status-CjEZMAD4.mjs} +3 -3
- package/dist/hosted-client-a-WmqE2L.mjs +197 -0
- package/dist/hosted-ios-CIWelIqf.mjs +220 -0
- package/dist/hosted-logs-D5g041Q3.mjs +187 -0
- package/dist/hosted-macos-BSZyIb3y.mjs +135 -0
- package/dist/idle-DYVymG92.mjs +1099 -0
- package/dist/idle-shutdown-CTsDnAde.mjs +122 -0
- package/dist/in-use-DvBw0Kxo.mjs +130 -0
- package/dist/{ios-BwDPrnYQ.mjs → ios-BaInn3sW.mjs} +201 -10
- package/dist/ios-Cr1h5gnO.mjs +189 -0
- package/dist/{ios-P1f5nm2n.mjs → ios-aq3QoGI3.mjs} +1159 -633
- package/dist/{ios-device-BiOsSVzd.mjs → ios-device-Ee7sDXfp.mjs} +1 -1
- package/dist/{ios-device-DGglU3E_.mjs → ios-device-FfCN_C8p.mjs} +12 -7
- package/dist/ios-state-DS5UpYTH.mjs +48 -0
- package/dist/{launch-verify-Deg4tB3Z.mjs → launch-verify-2DBRizjo.mjs} +5 -5
- package/dist/logs-CHEVK8zD.mjs +371 -0
- package/dist/machines-BIBUdhpj.mjs +265 -0
- package/dist/macos-CSYEAFmF.mjs +473 -0
- package/dist/macos-run.d.mts +5 -0
- package/dist/macos-run.mjs +2 -0
- package/dist/maintenance-run.d.mts +4 -0
- package/dist/maintenance-run.mjs +254 -0
- package/dist/{metro-CcEo8Fl8.mjs → metro-BeK1ZPz1.mjs} +20 -120
- package/dist/metro-gateway-BJ-rxQAB.mjs +84 -0
- package/dist/{named-ports-Blg8IxOh.mjs → named-ports-Bk_YXEmE.mjs} +6 -59
- package/dist/native-run-NaBrRFtQ.mjs +147 -0
- package/dist/{native-runtime-DjBHRg5m.mjs → native-runtime-DBzRnEKS.mjs} +9 -5
- package/dist/{ndjson-CDY9IkVC.mjs → ndjson-PclOIwVY.mjs} +2 -2
- package/dist/offload-worker.d.mts +122 -0
- package/dist/offload-worker.mjs +606 -0
- package/dist/{ownership-DyYDF5ww.mjs → ownership-DiA16AsN.mjs} +3 -2
- package/dist/ownership-hCkPzGmr.mjs +571 -0
- package/dist/{page-BHUzTDFt.mjs → page-QF_Ke10s.mjs} +7 -2
- package/dist/{ports-BguS83ex.mjs → ports-R2k4Knz3.mjs} +3 -3
- package/dist/prebuild-D2fsca_Z.mjs +213 -0
- package/dist/preview-DhVbvPAZ.mjs +318 -0
- package/dist/process-identity-BmRm1qV0.mjs +12 -0
- package/dist/{project-CWlvEaeN.mjs → project-Du91Ilvc.mjs} +74 -19
- package/dist/pull-request-pxnDCQzn.mjs +322 -0
- package/dist/pull-requests.d.mts +120 -0
- package/dist/pull-requests.mjs +2 -0
- package/dist/recordings-BIXOzVnr.mjs +37 -0
- package/dist/{reload-BiniwsSZ.mjs → reload-rM9jV1fz.mjs} +109 -24
- package/dist/{remote-cache-CcR6o6lI.mjs → remote-cache-C8-V13XN.mjs} +5 -5
- package/dist/run-f4vI6EnK.mjs +119 -0
- package/dist/{server-bare-DGfgyJ5J.mjs → server-bare-CpPGmnmO.mjs} +33 -15
- package/dist/server-expo-CVqO2c35.mjs +2 -0
- package/dist/{server-expo-BVuwQ65v.mjs → server-expo-MC0rHlOC.mjs} +8 -12
- package/dist/settings-BBpMcDbD.mjs +25 -0
- package/dist/{settings-B-fsC-Om.mjs → settings-ZkDzkzvB.mjs} +22 -10
- package/dist/settings-oTSkkNrM.mjs +1439 -0
- package/dist/settings.schema.json +845 -97
- package/dist/{simslim-DrDN4WuV.mjs → simslim-B8-7AoTI.mjs} +8 -132
- package/dist/{slot-launch-D5t8EiMf.mjs → slot-launch-B8VrHxn1.mjs} +11 -8
- package/dist/{spawn-claims-Drw_o-Fb.mjs → spawn-claims-D0XfSe8u.mjs} +1 -1
- package/dist/spawn-entry-jw19m8Nl.mjs +17 -0
- package/dist/stage-CzaiwFsI.mjs +143 -0
- package/dist/start-CX8Ig-U2.mjs +2 -0
- package/dist/{start-s4RXp1wy.mjs → start-ClDfwXa4.mjs} +116 -65
- package/dist/state-3DwSQIBK.mjs +41 -0
- package/dist/state-B4PQFXMW.d.mts +12 -0
- package/dist/{state-Dk9RVXI7.mjs → state-C1jB7Q8q.mjs} +70 -8
- package/dist/{state-DHqvsbqC.mjs → state-DcW6vOd_.mjs} +7 -4
- package/dist/stats-CEZ_i5je.mjs +77 -0
- package/dist/{status-BgAuxESg.mjs → status-BuvBFeII.mjs} +118 -20
- package/dist/{status-BsPAnfOI.mjs → status-Cs7VLg9D.mjs} +542 -283
- package/dist/status-DlKvVJBE.mjs +49 -0
- package/dist/stdout-reader-C8yjre1x.mjs +23 -0
- package/dist/stim-desktop-Cnz8CPnl.mjs +63 -0
- package/dist/stim-footprint.swift +32 -0
- package/dist/{stim-installations-CINXy_su.mjs → stim-installations-QMEI32v1.mjs} +1 -1
- package/dist/stop-B0Yxtmiy.mjs +48 -0
- package/dist/{stop-BzreOWJp.mjs → stop-Bffozsac.mjs} +83 -16
- package/dist/{stop-BWIF_6BK.mjs → stop-Dt_dM16A.mjs} +2 -2
- package/dist/stop-cause-CidxIqup.mjs +97 -0
- package/dist/supervisor-run.d.mts +43 -11
- package/dist/supervisor-run.mjs +363 -24
- package/dist/support-CAP8qDUF.mjs +316 -0
- package/dist/support-D4aYJniO.mjs +201 -0
- package/dist/teardown-CL6vDs2Y.mjs +727 -0
- package/dist/{xcode-Cp5KvK9x.mjs → toolchain-DkUflduf.mjs} +364 -14
- package/dist/trigger-Bemu8ytw.mjs +65 -0
- package/dist/trigger-CXts6qt1.mjs +2 -0
- package/dist/warm-progress-CpGv3Wb6.mjs +732 -0
- package/dist/{web-BsQs1WlO.mjs → web-DWUaMp8P.mjs} +37 -21
- package/dist/web-run.mjs +310 -13
- package/dist/workspace-process-lock-BL7Q0HCO.mjs +57 -0
- package/dist/workspace-state-B8-aJ1_5.mjs +371 -0
- package/dist/workspaces-BV4pdHFz.mjs +306 -0
- package/dist/{worktree-UrQWXSeb.mjs → worktree-K-xj9Db0.mjs} +625 -256
- package/dist/worktree-jnG8Iph5.mjs +2 -0
- package/package.json +10 -5
- package/shim/bundle-response.cjs +28 -12
- package/dist/activity-CYVDcrt3.mjs +0 -356
- package/dist/budget-CGDkhvv_.mjs +0 -1745
- package/dist/build-progress-DxKpwnaS.mjs +0 -247
- package/dist/idle-DXtx3RbX.mjs +0 -191
- package/dist/in-use-DlqBDKqY.mjs +0 -249
- package/dist/logs-Civ4gbBH.mjs +0 -486
- package/dist/merge-state-ytFQCeh4.mjs +0 -262
- package/dist/server-expo-DIHDhvGg.mjs +0 -2
- package/dist/settings-D61iNOsB.mjs +0 -629
- package/dist/start-Cetfvy8l.mjs +0 -2
- package/dist/stats-CFpFGlJV.mjs +0 -62
- package/dist/stats-CX1Z8Tca.mjs +0 -368
- package/dist/stim-desktop-CXt8-Z3J.mjs +0 -40
- package/dist/stop-cause-hjGBOZTG.mjs +0 -1231
- package/dist/worktree-BVrHsYZG.mjs +0 -752
- package/dist/worktree-CHWWvm5v.mjs +0 -2
- package/dist/{metro-store-DzeWkfqW.mjs → metro-store-CDDZHtPl.mjs} +1 -1
|
@@ -1,38 +1,48 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { M as workspaceLogsDir, a as pidExists, b as writeWorkspaceState, c as resolveProjectMetro, g as recordWorkspaceUse, h as readWorkspaceState$1, k as workspaceDir, l as signalProcessTree, x as emulatorLogFile } from "./metro-CcEo8Fl8.mjs";
|
|
5
|
-
import { D as makeTemporaryDirectory, O as removeTemporaryEntry, s as gitCommonDir, y as repoRoot } from "./worktree-BVrHsYZG.mjs";
|
|
6
|
-
import { a as findProjectRoot, l as projectShortcut, n as appProjectProblem, r as detectIsExpo, t as NO_PROJECT_REFUSAL } from "./project-CWlvEaeN.mjs";
|
|
7
|
-
import { m as writeWorkspaceLaunch, n as MODE_EXPO, t as MODE_BARE } from "./state-DHqvsbqC.mjs";
|
|
1
|
+
import { t as getExecutor } from "./exec-CaErVv7t.mjs";
|
|
2
|
+
import { C as saveConfig, D as withConfigLock$1, E as upsertProject, L as deviceSlotFileKey, R as deviceSlotKey, V as parseDeviceSlotOption, W as validateDeviceSlot, _ as loadConfig$1, j as formatBytes, p as getProject, u as getConcurrencyLimits, w as setDevice, z as deviceSlotPlatforms } from "./config-D0DbpZtM.mjs";
|
|
3
|
+
import { n as ownership_claim_exports } from "./process-identity-BmRm1qV0.mjs";
|
|
8
4
|
import { r as waitForChild, t as createLineReader } from "./process-output-Fpl8PYi8.mjs";
|
|
9
|
-
import { _ as stepTimer, c as launchErrorReport, f as refuseNoProject, g as stepClock, i as formatDuration, l as phaseLine, m as shortHash, n as appReadinessMessage, v as launchErrorPreview } from "./command-output-
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import { c as
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import { t as
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import { a as
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
29
|
-
import { d as
|
|
30
|
-
import {
|
|
31
|
-
import {
|
|
32
|
-
import {
|
|
5
|
+
import { _ as stepTimer, c as launchErrorReport, f as refuseNoProject, g as stepClock, i as formatDuration, l as phaseLine, m as shortHash, n as appReadinessMessage, v as launchErrorPreview } from "./command-output-DnJMdEY-.mjs";
|
|
6
|
+
import { B as settingsLayers, F as settingFile, G as detectProviders, H as unknownSettingKeys, J as artifactCachePolicy, L as settingOriginScope, N as resolveCacheProviderConfig, P as resolveSettings, R as settingShapeErrors, S as metroPortSetting, Tt as removeTemporaryEntry, V as tunnelModeSetting, X as optimizationBuildProfile, Y as compilerCacheFallbackMessage, Z as resolveOptimizations, a as SETTING_SHAPE_REMEDY, d as cacheProviderSettingError, gt as repoRoot, it as gitCommonDir, j as remoteAndroidSetting, k as publicUrlSetting, r as REMOTE_DEVICE_BACKENDS, s as androidAvdConfigSettingError, u as androidDataPartitionSizeGbSettingError, w as metroWarmupUrlSetting, wt as makeTemporaryDirectory } from "./settings-oTSkkNrM.mjs";
|
|
7
|
+
import { n as appProjectProblem, o as findProjectRoot$1, r as detectIsExpo, t as NO_PROJECT_REFUSAL, u as projectShortcut } from "./project-Du91Ilvc.mjs";
|
|
8
|
+
import { l as resolveProjectMetro, o as pidExists, u as signalProcessTree } from "./metro-BeK1ZPz1.mjs";
|
|
9
|
+
import { c as writeWorkspaceState, g as workspaceAgentDeviceDir, i as recordWorkspaceUse, l as emulatorLogFile, r as readWorkspaceState$1, v as workspaceDir, x as workspaceLogsDir$1 } from "./workspace-state-B8-aJ1_5.mjs";
|
|
10
|
+
import { t as budgetGate } from "./budget-DjrURfQn.mjs";
|
|
11
|
+
import { A as providerDownloadPath, B as captureAssetManifest, D as fingerprintProject, E as fingerprintDiffRecord, F as storedAssetManifest, H as readAssetManifest, L as untrackedMissLine, M as refingerprintAfterMutation, N as resolveBuild, O as inputsChangedDuringBuild, P as storeBuild, R as untrackedNativeFiles, S as configInputsChanged, T as filesystemBuildCapability, V as compareAssetManifests, _ as waitForBuild, b as buildCacheKey, g as releaseBuildLock, j as providerUploadOutcome, k as prepareProviderDownloadDir, m as acquireBuildLock, v as waitForSharedBuild, x as changedDuringBuildLine, z as assetDiffReason } from "./device-lease-D-hLqx1b.mjs";
|
|
12
|
+
import { r as releaseBuildSlot, t as acquireBuildSlot } from "./build-slots-rUk77xqh.mjs";
|
|
13
|
+
import { m as writeWorkspaceLaunch, n as MODE_EXPO, t as MODE_BARE } from "./state-DcW6vOd_.mjs";
|
|
14
|
+
import { S as statsProjectKey$1, _ as tapBuildLog, g as startBuildProgress, h as recordFinishedBuild, l as NO_BUILD_PROGRESS, v as createRunRecorder, x as recordRunStats, y as readRunEstimates } from "./idle-DYVymG92.mjs";
|
|
15
|
+
import { $ as trimAndroidCaches, A as listAvds, B as ownedAvdSystemImage, D as hostSystemImageArch, F as ownedAvdDeviceProfile, G as probeEmulatorSerial, I as ownedAvdDirectory, J as resolveOwnedAvdSerial, K as profileNeedsFoldFeature, O as listAdbDevices, Q as systemImageSupportsFold, T as findBuildTool, U as physicalDeviceModel, W as pickDefaultSystemImage, Y as resolvePhysicalDevice, a as androidDeviceAbi, ft as parkedMaxSetting, i as androidDataFreeBytes, j as listInstalledSystemImages, k as listAvdDeviceProfiles, q as resetAdoptedAvd, r as androidBuildToolName, rt as configuredAndroidEmulatorApp, t as DEFAULT_AVD_DEVICE_PROFILE, tt as waitForBoot, u as androidToolCwd } from "./android-B_NumVyQ.mjs";
|
|
16
|
+
import { a as workspaceLinks, i as workspaceLinkLine } from "./stim-desktop-Cnz8CPnl.mjs";
|
|
17
|
+
import { i as teardownOwnedAvd } from "./teardown-CL6vDs2Y.mjs";
|
|
18
|
+
import { r as createNdjsonWriter } from "./ndjson-PclOIwVY.mjs";
|
|
19
|
+
import { E as ccacheActivityLine, T as CCACHE_UNAVAILABLE, b as tailLines, k as resolveCcache, n as androidToolchain, t as androidRequirements, u as HEARTBEAT_INTERVAL_MS, w as CCACHE_NOT_RUN, y as startBuildHeartbeat } from "./toolchain-DkUflduf.mjs";
|
|
20
|
+
import { t as cleanLine } from "./server-expo-MC0rHlOC.mjs";
|
|
21
|
+
import { a as offloadBuild, c as placementLoad, d as OffloadRefusal, f as namedBuildMachine, i as closeOffload, l as remotePhaseText, m as resolveBuildPlacement, n as buildPlacementCandidates, p as parseBuildMachineOption, r as chooseBuildMachine, s as offloadPlacement } from "./client-BTY686R6.mjs";
|
|
22
|
+
import { n as verifyCollectorOwnership, r as readCollectors } from "./ownership-DiA16AsN.mjs";
|
|
23
|
+
import { S as restartedAppNote, b as launchAndroidReleaseApp, c as androidDevClientUrl, g as installConflictKind, h as installAndroidApp, r as DEFAULT_METRO_PORT, s as androidAppProcess, t as ADB_INSTALL_TIMEOUT_MS, y as launchAndroidApp } from "./app-install-CKWdbXIX.mjs";
|
|
24
|
+
import { t as checkDeviceCapacity } from "./device-capacity-BHwxNBlu.mjs";
|
|
25
|
+
import { C as resolveSystemImage, S as resolveDeviceProfile, T as systemImageRefusal, _ as isReleaseVariant, a as PLATFORM, b as pooledAndroidDevice, c as androidSystemImageSetting, d as apkPackage, f as deviceProfileRefusal, g as foldableImageRefusal, h as findAapt, i as NO_FINGERPRINT, l as androidVariantSetting, m as dumpApkManifest, n as LAUNCH_FAILED, o as androidBuildOptions, p as displayPath, r as NO_DEVICE, s as androidDevClientScheme, t as INSTALL_FAILED, u as apkDevClientFacts, v as noDeviceDiagnostic, w as resolveVariant, x as remoteAvdFlagRefusal, y as parseXmltree } from "./support-CAP8qDUF.mjs";
|
|
26
|
+
import { t as spawnEntry } from "./spawn-entry-jw19m8Nl.mjs";
|
|
27
|
+
import { i as staleNativeDirRefusal, n as recordPrebuild, r as runPrebuild, t as planPrebuild } from "./prebuild-D2fsca_Z.mjs";
|
|
28
|
+
import { c as formatDiagnostic, i as buildAndroid, o as productFlavorRefusal, s as readProductFlavors } from "./gradle-BsO1MdsH.mjs";
|
|
29
|
+
import { a as VERIFY_TIMEOUT_MS, d as verifyAndroidReleaseLaunch, f as verifyLaunch, i as RELEASE_VERIFY_WAIT_MS, l as unattributedLaunchLines, n as LAUNCH_FATAL, r as LAUNCH_UNVERIFIED, t as LAUNCH_BUNDLING, u as unverifiedLaunchLines } from "./launch-verify-2DBRizjo.mjs";
|
|
30
|
+
import { a as acquireRunLease, c as lostRefusal, d as runLease, f as waitFlagConflict, l as parseDeviceWait, o as leaseExpiryText, s as lostLine, t as DEBUG_VERIFY_STEP_MS, u as releaseLeaseOnSignal } from "./device-lease-run-CqjShqWW.mjs";
|
|
31
|
+
import { a as easAuthNote, c as loadProjectProvider, f as resolveEasCliBin, i as checkEasAuth, m as uploadRemote, n as UPLOAD_TIMEOUT_MS, o as exitAfterFlush, p as resolveRemote, r as cacheLevel, s as isEasAuthFailureText, t as RESOLVE_TIMEOUT_MS } from "./remote-cache-C8-V13XN.mjs";
|
|
32
|
+
import { a as ensureMetroReachable, c as remoteAndroidDeps, n as binOnPath, o as ensureRemoteBootOwned, u as resolveRemoteContext, w as ownedSessionName } from "./device-remote-D5WVWqOq.mjs";
|
|
33
|
+
import { d as withNativeBuildRun, r as cancelledFailure, u as runCancellation } from "./native-run-NaBrRFtQ.mjs";
|
|
34
|
+
import { a as isEasBuildFailure, o as planEasDevelopmentBuild, s as resolveEasDevelopmentBuild } from "./simslim-B8-7AoTI.mjs";
|
|
35
|
+
import { c as startDevServer, m as detectAppIds, p as detectAndroidPackage } from "./start-ClDfwXa4.mjs";
|
|
36
|
+
import { _ as AvdRecoveryError, a as refusePlan, c as skippedMissReason, d as refreshUpdatesManifestFile, g as AvdBootError, h as ensureOwnedDevice, i as printPlan, l as UPDATES_MANIFEST_NAME, m as ensureBooted, n as planFlagRefusal, o as explainBuildMiss, p as didSetUpDevice, r as planPayload, s as fingerprintErrorMissReason, t as planCachedBuild, u as detectEntryFile, v as warmMetro } from "./build-plan-DjeSb2Xl.mjs";
|
|
37
|
+
import { i as launchOutcomeRecord, n as ensureWorkspaceStorageSafely, r as isPhysicalDeviceRequest, t as ensureDevServer } from "./native-runtime-DBzRnEKS.mjs";
|
|
38
|
+
import { t as selectFromPool } from "./device-pool-CUaEWMMm.mjs";
|
|
39
|
+
import { n as nativeRunCommand, r as siblingPlatformSlots, t as launchSlotScope } from "./slot-launch-B8VrHxn1.mjs";
|
|
40
|
+
import { a as printNativeCrashReport, r as captureNativeCrashes, t as errorDiagnostics } from "./error-diagnostics-B9L4Ae0x.mjs";
|
|
41
|
+
import { n as resolveAndroidCas, r as resolveAndroidCompilerCache } from "./android-cas-8U4DPqeR.mjs";
|
|
42
|
+
import { constants, copyFileSync, existsSync, mkdirSync, openSync, readFileSync, renameSync, rmSync, statSync } from "node:fs";
|
|
43
|
+
import { basename, dirname, isAbsolute, join } from "node:path";
|
|
33
44
|
import { InvalidArgumentError } from "commander";
|
|
34
|
-
import { buildDiagnostics } from "@stim-cli/core/state";
|
|
35
|
-
import { basename, dirname, isAbsolute, join, relative } from "node:path";
|
|
45
|
+
import { buildDiagnostics, machineCapacity, parseMachine } from "@stim-cli/core/state";
|
|
36
46
|
import chalk from "chalk";
|
|
37
47
|
import { createWarnOnce, loadCacheProvider, resolveTieredBuild, storeTieredBuild } from "@stim-cli/cache";
|
|
38
48
|
//#region src/engine/apk-swap.ts
|
|
@@ -316,6 +326,18 @@ async function swapApkBundle({ root, isExpo, cachedApkPath, keystore, logWriter
|
|
|
316
326
|
const diff = compareAssetManifests(fresh, storedAssets);
|
|
317
327
|
if (!diff.same) return refuse(assetDiffReason(diff), diff);
|
|
318
328
|
const jar = jarPath();
|
|
329
|
+
const updatesManifest = join(stage, "assets", UPDATES_MANIFEST_NAME);
|
|
330
|
+
try {
|
|
331
|
+
e.runFile(jar, [
|
|
332
|
+
"--extract",
|
|
333
|
+
"--file",
|
|
334
|
+
work,
|
|
335
|
+
`assets/${UPDATES_MANIFEST_NAME}`
|
|
336
|
+
], { cwd: stage });
|
|
337
|
+
if (exists(updatesManifest)) refreshUpdatesManifestFile(updatesManifest, now());
|
|
338
|
+
} catch (err) {
|
|
339
|
+
return fail("updates", `could not refresh the expo-updates manifest from ${work}: ${describe(err)}`);
|
|
340
|
+
}
|
|
319
341
|
try {
|
|
320
342
|
e.runFile(jar, [
|
|
321
343
|
"--update",
|
|
@@ -387,299 +409,6 @@ function describe(err) {
|
|
|
387
409
|
return streams ? `${message}: ${streams}` : message;
|
|
388
410
|
}
|
|
389
411
|
//#endregion
|
|
390
|
-
//#region src/commands/android/support.ts
|
|
391
|
-
const PLATFORM = "android";
|
|
392
|
-
function androidVariantSetting(settings) {
|
|
393
|
-
const android = settings?.["android"];
|
|
394
|
-
if (!android || typeof android !== "object" || Array.isArray(android)) return null;
|
|
395
|
-
const raw = android["variant"];
|
|
396
|
-
return typeof raw === "string" && raw.trim() !== "" ? raw.trim() : null;
|
|
397
|
-
}
|
|
398
|
-
function resolveVariant(flag, settings) {
|
|
399
|
-
return (typeof flag === "string" && flag.trim() !== "" ? flag.trim() : null) || androidVariantSetting(settings);
|
|
400
|
-
}
|
|
401
|
-
function androidSystemImageSetting(settings) {
|
|
402
|
-
const android = settings?.["android"];
|
|
403
|
-
if (!android || typeof android !== "object" || Array.isArray(android)) return null;
|
|
404
|
-
const raw = android["systemImage"];
|
|
405
|
-
return typeof raw === "string" && raw.trim() !== "" ? raw.trim() : null;
|
|
406
|
-
}
|
|
407
|
-
function resolveSystemImage(flag, settings) {
|
|
408
|
-
return (typeof flag === "string" && flag.trim() !== "" ? flag.trim() : null) || androidSystemImageSetting(settings);
|
|
409
|
-
}
|
|
410
|
-
function systemImageRefusal({ slot, flag, resolved, physical, remoteBackend, listImages }) {
|
|
411
|
-
if (slot && slot !== "default" && remoteBackend) return {
|
|
412
|
-
code: "STIM_BAD_ARG",
|
|
413
|
-
message: "Named slots currently support local simulators and physical devices.",
|
|
414
|
-
remedy: "Use the default slot for a remote session."
|
|
415
|
-
};
|
|
416
|
-
if (typeof flag === "string" && flag.trim() === "") return {
|
|
417
|
-
code: "STIM_BAD_ARG",
|
|
418
|
-
message: "--system-image was given an empty id.",
|
|
419
|
-
remedy: "Pass `--system-image <id>` with an sdkmanager package id, e.g. \"system-images;android-36;google_apis;arm64-v8a\"."
|
|
420
|
-
};
|
|
421
|
-
if (physical || remoteBackend) return null;
|
|
422
|
-
if (!resolved) return null;
|
|
423
|
-
let images;
|
|
424
|
-
try {
|
|
425
|
-
images = listImages();
|
|
426
|
-
} catch (error) {
|
|
427
|
-
return {
|
|
428
|
-
code: NO_DEVICE,
|
|
429
|
-
message: `Could not read the installed Android system images: ${error?.message || error}`,
|
|
430
|
-
remedy: "Check that ANDROID_HOME points at a readable SDK, then try again."
|
|
431
|
-
};
|
|
432
|
-
}
|
|
433
|
-
const refusal = unknownAndroidSystemImageRefusal(resolved, images);
|
|
434
|
-
return refusal ? {
|
|
435
|
-
code: "STIM_BAD_ARG",
|
|
436
|
-
message: refusal.message,
|
|
437
|
-
remedy: refusal.remedy
|
|
438
|
-
} : null;
|
|
439
|
-
}
|
|
440
|
-
function resolveDeviceProfile(flag, settings) {
|
|
441
|
-
const fromFlag = typeof flag === "string" && flag.trim() !== "" ? flag.trim() : null;
|
|
442
|
-
const android = settings?.["android"];
|
|
443
|
-
const raw = android && typeof android === "object" && !Array.isArray(android) ? android["deviceProfile"] : void 0;
|
|
444
|
-
return fromFlag || (typeof raw === "string" && raw.trim() !== "" ? raw.trim() : null);
|
|
445
|
-
}
|
|
446
|
-
function deviceProfileRefusal({ flag, resolved, physical, remoteBackend, listProfiles }) {
|
|
447
|
-
if (typeof flag === "string" && flag.trim() === "") return {
|
|
448
|
-
code: "STIM_BAD_ARG",
|
|
449
|
-
message: "--device-profile was given an empty id.",
|
|
450
|
-
remedy: "Pass `--device-profile <id>` with an id `avdmanager list device -c` prints, e.g. \"pixel_fold\"."
|
|
451
|
-
};
|
|
452
|
-
if (physical || remoteBackend || !resolved) return null;
|
|
453
|
-
let profiles;
|
|
454
|
-
try {
|
|
455
|
-
profiles = listProfiles();
|
|
456
|
-
} catch (error) {
|
|
457
|
-
return {
|
|
458
|
-
code: NO_DEVICE,
|
|
459
|
-
message: `Could not list the Android hardware profiles with avdmanager: ${error?.message || error}`,
|
|
460
|
-
remedy: "Check that JAVA_HOME and ANDROID_HOME are set and the SDK command-line tools are installed, then try again."
|
|
461
|
-
};
|
|
462
|
-
}
|
|
463
|
-
const refusal = unknownAndroidDeviceProfileRefusal(resolved, profiles);
|
|
464
|
-
return refusal ? {
|
|
465
|
-
code: "STIM_BAD_ARG",
|
|
466
|
-
message: refusal.message,
|
|
467
|
-
remedy: refusal.remedy
|
|
468
|
-
} : null;
|
|
469
|
-
}
|
|
470
|
-
function foldableImageRefusal({ profile, image, avdName, images, supportsFold }) {
|
|
471
|
-
if (!profileNeedsFoldFeature(profile) || !image || supportsFold(image)) return null;
|
|
472
|
-
const compatible = pickDefaultSystemImage(images().filter((candidate) => supportsFold(candidate.pkg)))?.pkg;
|
|
473
|
-
const message = `${avdName ? `This workspace's emulator ${avdName} uses device profile ${profile} on ${image}` : `Device profile ${profile} with system image ${image}`}, which the emulator refuses to boot: ${profile} needs a system image with foldable support (SupportPixelFold in its advancedFeatures.ini).`;
|
|
474
|
-
if (!compatible) return {
|
|
475
|
-
code: "STIM_BAD_ARG",
|
|
476
|
-
message,
|
|
477
|
-
remedy: `Install an image with foldable support, e.g. \`sdkmanager "system-images;android-36;google_apis;${hostSystemImageArch()}"\`, then pass it with \`--system-image\`, or pick another \`--device-profile\`.`
|
|
478
|
-
};
|
|
479
|
-
return {
|
|
480
|
-
code: "STIM_BAD_ARG",
|
|
481
|
-
message,
|
|
482
|
-
remedy: avdName ? `Pass \`--system-image "${compatible}"\`: it replaces ${avdName} if that emulator never finished a boot. Otherwise add \`--slot <name>\` to create the foldable beside it.` : `Pass \`--system-image "${compatible}"\` (or set android.systemImage to it), or pick another \`--device-profile\`.`
|
|
483
|
-
};
|
|
484
|
-
}
|
|
485
|
-
function isReleaseVariant(variant) {
|
|
486
|
-
return typeof variant === "string" && /release$/i.test(variant.trim());
|
|
487
|
-
}
|
|
488
|
-
const NO_FINGERPRINT = "STIM_NO_FINGERPRINT";
|
|
489
|
-
const NO_DEVICE = "STIM_NO_DEVICE";
|
|
490
|
-
const LAUNCH_FAILED = "STIM_LAUNCH_FAILED";
|
|
491
|
-
function findAapt(home = androidHome(), { readDir = readdirSync, exists = existsSync } = {}) {
|
|
492
|
-
const found = findBuildTool(["aapt", "aapt2"], {
|
|
493
|
-
home,
|
|
494
|
-
readDir,
|
|
495
|
-
exists
|
|
496
|
-
});
|
|
497
|
-
return found ? {
|
|
498
|
-
path: found.path,
|
|
499
|
-
tool: found.tool,
|
|
500
|
-
version: found.version
|
|
501
|
-
} : null;
|
|
502
|
-
}
|
|
503
|
-
function dumpApkManifest(apkPath, { exec = null, aapt = null } = {}) {
|
|
504
|
-
if (typeof apkPath !== "string" || apkPath.trim() === "") return null;
|
|
505
|
-
const tool = aapt || findAapt();
|
|
506
|
-
if (!tool) return null;
|
|
507
|
-
const e = exec || getExecutor();
|
|
508
|
-
const args = tool.tool === "aapt2" ? [
|
|
509
|
-
"dump",
|
|
510
|
-
"xmltree",
|
|
511
|
-
"--file",
|
|
512
|
-
"AndroidManifest.xml",
|
|
513
|
-
apkPath
|
|
514
|
-
] : [
|
|
515
|
-
"dump",
|
|
516
|
-
"xmltree",
|
|
517
|
-
apkPath,
|
|
518
|
-
"AndroidManifest.xml"
|
|
519
|
-
];
|
|
520
|
-
try {
|
|
521
|
-
const out = e.runFile(tool.path, args);
|
|
522
|
-
return typeof out === "string" && out.includes("E: manifest") ? out : null;
|
|
523
|
-
} catch {
|
|
524
|
-
return null;
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
function parseXmltree(text) {
|
|
528
|
-
const root = {
|
|
529
|
-
tag: "#root",
|
|
530
|
-
attrs: {},
|
|
531
|
-
children: [],
|
|
532
|
-
indent: -1
|
|
533
|
-
};
|
|
534
|
-
const stack = [root];
|
|
535
|
-
for (const raw of String(text ?? "").split("\n")) {
|
|
536
|
-
const indent = raw.search(/\S/);
|
|
537
|
-
if (indent < 0) continue;
|
|
538
|
-
const line = raw.trim();
|
|
539
|
-
const element = /^E: ([\w.:-]+)/.exec(line);
|
|
540
|
-
if (element) {
|
|
541
|
-
while (stack.length > 1 && stack[stack.length - 1].indent >= indent) stack.pop();
|
|
542
|
-
const node = {
|
|
543
|
-
tag: element[1],
|
|
544
|
-
attrs: {},
|
|
545
|
-
children: [],
|
|
546
|
-
indent
|
|
547
|
-
};
|
|
548
|
-
stack[stack.length - 1].children.push(node);
|
|
549
|
-
stack.push(node);
|
|
550
|
-
continue;
|
|
551
|
-
}
|
|
552
|
-
const attr = /^A: ([^(=]+?)(?:\(0x[0-9a-f]+\))?=(.*)$/.exec(line);
|
|
553
|
-
if (attr && stack.length > 1) {
|
|
554
|
-
const name = attr[1].replace(/^http:\/\/schemas\.android\.com\/apk\/res\/android:/, "android:");
|
|
555
|
-
const value = /^"((?:[^"\\]|\\.)*)"/.exec(attr[2]);
|
|
556
|
-
stack[stack.length - 1].attrs[name] = value ? value[1] : null;
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
return root;
|
|
560
|
-
}
|
|
561
|
-
function eachNode(node, fn) {
|
|
562
|
-
fn(node);
|
|
563
|
-
for (const child of node.children) eachNode(child, fn);
|
|
564
|
-
}
|
|
565
|
-
function apkPackage(text) {
|
|
566
|
-
const pkg = parseXmltree(text).children.find((c) => c.tag === "manifest")?.attrs["package"];
|
|
567
|
-
return typeof pkg === "string" && pkg.trim() ? pkg.trim() : null;
|
|
568
|
-
}
|
|
569
|
-
function apkDevClientFacts(text) {
|
|
570
|
-
const root = parseXmltree(text);
|
|
571
|
-
const facts = {
|
|
572
|
-
devClient: false,
|
|
573
|
-
schemes: []
|
|
574
|
-
};
|
|
575
|
-
let launchable = null;
|
|
576
|
-
eachNode(root, (node) => {
|
|
577
|
-
const name = node.attrs["android:name"];
|
|
578
|
-
if (typeof name === "string" && name.startsWith("expo.modules.devlauncher")) facts.devClient = true;
|
|
579
|
-
if (node.tag !== "activity" && node.tag !== "activity-alias") return;
|
|
580
|
-
const filters = node.children.filter((c) => c.tag === "intent-filter");
|
|
581
|
-
if (!filters.some((f) => f.children.some((c) => c.tag === "action" && c.attrs["android:name"] === "android.intent.action.MAIN")) || launchable) return;
|
|
582
|
-
launchable = node;
|
|
583
|
-
for (const filter of filters) for (const data of filter.children) {
|
|
584
|
-
if (data.tag !== "data") continue;
|
|
585
|
-
const scheme = data.attrs["android:scheme"];
|
|
586
|
-
if (typeof scheme === "string" && scheme.trim()) facts.schemes.push(scheme.trim());
|
|
587
|
-
}
|
|
588
|
-
});
|
|
589
|
-
return facts;
|
|
590
|
-
}
|
|
591
|
-
function androidDevClientScheme(root, apkPath, { exec = null, dump = dumpApkManifest, aapt = null } = {}) {
|
|
592
|
-
const text = dump(apkPath, {
|
|
593
|
-
exec,
|
|
594
|
-
aapt
|
|
595
|
-
});
|
|
596
|
-
if (text) {
|
|
597
|
-
const facts = apkDevClientFacts(text);
|
|
598
|
-
if (!facts.devClient) return void 0;
|
|
599
|
-
const scheme = pickDevClientScheme(facts.schemes);
|
|
600
|
-
if (scheme) return scheme;
|
|
601
|
-
}
|
|
602
|
-
return devClientScheme(root, null);
|
|
603
|
-
}
|
|
604
|
-
function noDeviceDiagnostic({ reason, logFile, remedy, localEmulator = true, readLog = readEmulatorLogTail }) {
|
|
605
|
-
const text = localEmulator ? readLog(logFile) : "";
|
|
606
|
-
const logPath = text.trim() ? logFile : null;
|
|
607
|
-
const found = extractEmulatorFailure(text);
|
|
608
|
-
const diskRemedy = localEmulator ? emulatorDiskSpaceRemedy([reason, ...found]) : null;
|
|
609
|
-
if (found.length === 0) return {
|
|
610
|
-
message: reason,
|
|
611
|
-
remedy: diskRemedy ?? remedy,
|
|
612
|
-
lines: [],
|
|
613
|
-
logPath
|
|
614
|
-
};
|
|
615
|
-
return {
|
|
616
|
-
message: `${reason} The emulator reported: ${found[0]}`,
|
|
617
|
-
remedy: diskRemedy ?? emulatorFailureRemedy(found),
|
|
618
|
-
lines: found.slice(1),
|
|
619
|
-
logPath
|
|
620
|
-
};
|
|
621
|
-
}
|
|
622
|
-
function readEmulatorLogTail(file) {
|
|
623
|
-
try {
|
|
624
|
-
return readFileSync(file, "utf-8").split("\n").slice(-400).join("\n");
|
|
625
|
-
} catch {
|
|
626
|
-
return "";
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
function displayPath(root, path) {
|
|
630
|
-
const rel = relative(root, path);
|
|
631
|
-
return rel && !rel.startsWith("..") ? rel : path;
|
|
632
|
-
}
|
|
633
|
-
async function pooledAndroidDevice({ root, selectPool, listDevices, isEmulatorDevice, deviceModel, waitSeconds, noWait, now, warn }) {
|
|
634
|
-
const isEmulator = memoizeEmulatorProbe(isEmulatorDevice);
|
|
635
|
-
const pooled = await selectPool({
|
|
636
|
-
root,
|
|
637
|
-
platform: PLATFORM,
|
|
638
|
-
idLabel: "serial",
|
|
639
|
-
list: () => androidPoolCandidates(listDevices(), isEmulator).map((entry) => ({ id: entry.serial })),
|
|
640
|
-
noCandidates: () => {
|
|
641
|
-
const resolved = androidPoolNoCandidatesRefusal(listDevices(), isEmulator);
|
|
642
|
-
return {
|
|
643
|
-
message: resolved.error,
|
|
644
|
-
remedy: resolved.remedy
|
|
645
|
-
};
|
|
646
|
-
},
|
|
647
|
-
waitSeconds,
|
|
648
|
-
noWait,
|
|
649
|
-
now,
|
|
650
|
-
warn
|
|
651
|
-
});
|
|
652
|
-
if (pooled.status === "refused") {
|
|
653
|
-
const { code, message, remedy, lease } = pooled.refusal;
|
|
654
|
-
return {
|
|
655
|
-
code,
|
|
656
|
-
message,
|
|
657
|
-
remedy,
|
|
658
|
-
extra: lease === null ? {} : { lease }
|
|
659
|
-
};
|
|
660
|
-
}
|
|
661
|
-
return { device: {
|
|
662
|
-
serial: pooled.candidate.id,
|
|
663
|
-
deviceName: deviceModel(pooled.candidate.id) ?? pooled.candidate.id,
|
|
664
|
-
owned: false
|
|
665
|
-
} };
|
|
666
|
-
}
|
|
667
|
-
function androidBuildOptions({ release, physical, device, variant, deviceAbi, compiler, buildProfile, targetAbiOnly = true }) {
|
|
668
|
-
let abi = null;
|
|
669
|
-
if (targetAbiOnly && !release && physical && device.serial) abi = deviceAbi(device.serial);
|
|
670
|
-
if (targetAbiOnly && !release && !physical) abi = androidSystemImageAbi(device.systemImage);
|
|
671
|
-
const runOptions = {};
|
|
672
|
-
if (variant) runOptions.variant = variant;
|
|
673
|
-
if (abi) runOptions.abi = abi;
|
|
674
|
-
if (compiler) runOptions.compiler = compiler;
|
|
675
|
-
if (buildProfile) runOptions.buildProfile = buildProfile;
|
|
676
|
-
return {
|
|
677
|
-
abi,
|
|
678
|
-
runOptions,
|
|
679
|
-
remoteRunOptions: Object.keys(runOptions).length === 0 ? null : runOptions
|
|
680
|
-
};
|
|
681
|
-
}
|
|
682
|
-
//#endregion
|
|
683
412
|
//#region src/commands/android/artifact.ts
|
|
684
413
|
const FALLBACK_LINES = 5;
|
|
685
414
|
function fail$1(code, message, remedy, extra = {}) {
|
|
@@ -690,8 +419,17 @@ function fail$1(code, message, remedy, extra = {}) {
|
|
|
690
419
|
extra
|
|
691
420
|
};
|
|
692
421
|
}
|
|
693
|
-
async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo, device, physical, buildPlan, cacheProviderConfig, requestedBuildCache, easBuild, androidPackage: initialPackage, record, maxBuilds, progress }, { deviceAbi, fingerprint, untracked, resolveCached, storeCached, storedAssets, captureAssets, acquireLock, releaseLock, waitForBuild, loadProvider, easAuth, resolveRemoteBuild, uploadRemoteBuild, loadCacheProviderModule, acquireSlot, releaseSlot, planPrebuildFor, prebuild, build, ccacheFor, swapApk, readState, now }) {
|
|
694
|
-
const { phase, out, estimates, stats, step } = progress;
|
|
422
|
+
async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo, device, physical, remote: remoteTarget, buildPlan, cacheProviderConfig, requestedBuildCache, easBuild, androidPackage: initialPackage, record, maxBuilds, progress }, { deviceAbi, fingerprint, untracked, resolveCached, storeCached, storedAssets, captureAssets, acquireLock, releaseLock, waitForBuild, loadProvider, easAuth, resolveRemoteBuild, uploadRemoteBuild, loadCacheProviderModule, acquireSlot, releaseSlot, planPrebuildFor, prebuild, build, ccacheFor, swapApk, readState, now }) {
|
|
423
|
+
const { phase, out, estimates, stats, step, miss, hit: lateHit, place, waitingOn } = progress;
|
|
424
|
+
let fallbackMachine = null;
|
|
425
|
+
let hereReason = "no build machine is paired";
|
|
426
|
+
let slotWaitMs;
|
|
427
|
+
const buildMachine = record.buildMachine ?? "auto";
|
|
428
|
+
const fallBack = (reason, line = reason) => {
|
|
429
|
+
if (namedBuildMachine(buildMachine)) throw new OffloadRefusal(buildMachine, reason);
|
|
430
|
+
record.offloadFallback = reason;
|
|
431
|
+
phase("build", `${line} -> building here`);
|
|
432
|
+
};
|
|
695
433
|
const { variant, release, profile: buildProfile, cas, cache: cachePolicy } = buildPlan;
|
|
696
434
|
const useBuildCache = cachePolicy.read;
|
|
697
435
|
let androidPackage = initialPackage;
|
|
@@ -749,6 +487,7 @@ async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo
|
|
|
749
487
|
let storeKey = "";
|
|
750
488
|
let storeSources = [];
|
|
751
489
|
let apkPath = null;
|
|
490
|
+
let swapFellBack = false;
|
|
752
491
|
async function resolveInitialFingerprint() {
|
|
753
492
|
if (easBuild?.ok) {
|
|
754
493
|
hash = storeHash = easBuild.fingerprint;
|
|
@@ -869,7 +608,10 @@ async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo
|
|
|
869
608
|
} else phase("cache", `remote miss (${remote.name}) ${remoteTimer()}`);
|
|
870
609
|
}
|
|
871
610
|
}
|
|
872
|
-
if (!easBuild)
|
|
611
|
+
if (!easBuild) {
|
|
612
|
+
await resolveRemoteArtifact();
|
|
613
|
+
if (!apkPath) miss(reasonForMiss([]).reason);
|
|
614
|
+
}
|
|
873
615
|
let waitedForBuild = null;
|
|
874
616
|
let releasedWait = null;
|
|
875
617
|
if (!easBuild && useBuildCache && !apkPath) {
|
|
@@ -887,6 +629,7 @@ async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo
|
|
|
887
629
|
step("wait");
|
|
888
630
|
phase("build", text);
|
|
889
631
|
},
|
|
632
|
+
waitingOn,
|
|
890
633
|
warn: (text) => phase("build", chalk.yellow(text)),
|
|
891
634
|
out
|
|
892
635
|
});
|
|
@@ -904,7 +647,6 @@ async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo
|
|
|
904
647
|
}
|
|
905
648
|
}
|
|
906
649
|
let swapDir = null;
|
|
907
|
-
let swapFellBack = false;
|
|
908
650
|
const installableCachedApk = async (key, cachedPath) => {
|
|
909
651
|
if (!release) return cachedPath;
|
|
910
652
|
phase("swap", `regenerating this workspace's JS for the cached ${variant} APK`);
|
|
@@ -938,51 +680,220 @@ async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo
|
|
|
938
680
|
}
|
|
939
681
|
}
|
|
940
682
|
}
|
|
941
|
-
function
|
|
942
|
-
if (swapFellBack)
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
683
|
+
function reasonForMiss(rekeyedBy) {
|
|
684
|
+
if (swapFellBack) return {
|
|
685
|
+
reason: skippedMissReason("the cached APK could not be reused, so this run built it fresh"),
|
|
686
|
+
diff: null
|
|
687
|
+
};
|
|
688
|
+
if (!useBuildCache) return {
|
|
689
|
+
reason: skippedMissReason(requestedBuildCache ? "cache reuse off in config" : "cache reuse turned off by --no-build-cache"),
|
|
690
|
+
diff: null
|
|
691
|
+
};
|
|
692
|
+
const current = {
|
|
693
|
+
hash: storeHash,
|
|
694
|
+
sources: storeSources
|
|
695
|
+
};
|
|
696
|
+
const explained = explainBuildMiss({
|
|
697
|
+
root,
|
|
698
|
+
platform: PLATFORM,
|
|
699
|
+
current,
|
|
700
|
+
rekeyedBy,
|
|
701
|
+
baselineDeps: { readState }
|
|
702
|
+
});
|
|
703
|
+
return {
|
|
704
|
+
reason: explained.reason,
|
|
705
|
+
diff: explained.previousHash && explained.changedNames.length ? fingerprintDiffRecord({
|
|
958
706
|
changed: explained.changedNames,
|
|
959
707
|
previousHash: explained.previousHash,
|
|
960
708
|
hash: current.hash
|
|
961
|
-
})
|
|
962
|
-
}
|
|
709
|
+
}) : null
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
function explainMiss(rekeyedBy) {
|
|
713
|
+
const explained = reasonForMiss(rekeyedBy);
|
|
714
|
+
record.missReason = explained.reason;
|
|
715
|
+
if (explained.diff) writer.write(explained.diff);
|
|
716
|
+
miss(record.missReason);
|
|
963
717
|
phase("cache", `miss: ${record.missReason.summary}`);
|
|
964
718
|
if (record.missReason.kind === "no-baseline") {
|
|
965
719
|
const line = untrackedMissLine(untracked({ projectRoot: root }));
|
|
966
720
|
if (line) phase("fingerprint", chalk.dim(line));
|
|
967
721
|
}
|
|
968
722
|
}
|
|
969
|
-
async function
|
|
970
|
-
if (!
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
723
|
+
async function takeBuildSlot() {
|
|
724
|
+
if (!maxBuilds) return true;
|
|
725
|
+
try {
|
|
726
|
+
buildSlot = await acquireSlot({
|
|
727
|
+
max: maxBuilds,
|
|
728
|
+
root,
|
|
729
|
+
logFile: buildLog,
|
|
730
|
+
out
|
|
731
|
+
});
|
|
732
|
+
slotWaitMs = buildSlot.slotWaitMs;
|
|
733
|
+
} catch (err) {
|
|
734
|
+
const refusal = (0, ownership_claim_exports.claimFailure)(err, "stim android");
|
|
735
|
+
if (refusal) {
|
|
736
|
+
phaseFailure = fail$1(refusal.code, refusal.message, refusal.remedy, { lastBuildStatus: true });
|
|
737
|
+
return false;
|
|
738
|
+
}
|
|
739
|
+
phase("build", chalk.yellow(`could not take a build slot: ${err?.message || err}; building anyway`));
|
|
740
|
+
}
|
|
741
|
+
return true;
|
|
742
|
+
}
|
|
743
|
+
/** Whether this build should leave this Mac, before any machine is asked; null builds here. */
|
|
744
|
+
function placeBuild() {
|
|
745
|
+
const { mode, machines } = buildPlacementCandidates(buildMachine);
|
|
746
|
+
if (machines.length === 0 && !namedBuildMachine(buildMachine) && buildMachine !== "local") return null;
|
|
747
|
+
const unsupported = physical ? "device builds build here" : remoteTarget ? "remote device builds build here" : release ? `${variant} builds build here` : cas ? "Apple Clang CAS builds build here" : !cachePolicy.write ? "the build cache is off" : null;
|
|
748
|
+
const here = machineCapacity();
|
|
749
|
+
const placement = offloadPlacement({
|
|
750
|
+
mode,
|
|
751
|
+
machines: machines.length,
|
|
752
|
+
here,
|
|
753
|
+
unsupported,
|
|
754
|
+
selected: buildMachine
|
|
755
|
+
});
|
|
756
|
+
if (!placement.offload) {
|
|
757
|
+
hereReason = placement.reason;
|
|
758
|
+
if (mode !== "off") phase("build", `placement: here (${placement.reason})`);
|
|
759
|
+
return null;
|
|
760
|
+
}
|
|
761
|
+
return {
|
|
762
|
+
mode,
|
|
763
|
+
here,
|
|
764
|
+
reason: placement.reason,
|
|
765
|
+
machines
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
const openOffload = { choice: null };
|
|
769
|
+
/** Asks the paired machines once the post-mutation key is known; null builds here. */
|
|
770
|
+
async function chooseMachine(candidate) {
|
|
771
|
+
const choice = await chooseBuildMachine({
|
|
772
|
+
projectRoot: root,
|
|
773
|
+
target: {
|
|
774
|
+
platform: "android",
|
|
775
|
+
local: androidToolchain(),
|
|
776
|
+
requires: androidRequirements(root)
|
|
777
|
+
},
|
|
778
|
+
mode: candidate.mode,
|
|
779
|
+
here: candidate.here,
|
|
780
|
+
note: (line) => phase("build", chalk.dim(`offload: ${line}`)),
|
|
781
|
+
machines: candidate.machines,
|
|
782
|
+
selected: buildMachine
|
|
783
|
+
});
|
|
784
|
+
if (typeof choice === "string") {
|
|
785
|
+
const only = candidate.machines.length === 1 ? candidate.machines[0].machine : null;
|
|
786
|
+
if (only && choice.startsWith(`${only}: `)) fallbackMachine = only;
|
|
787
|
+
fallBack(choice);
|
|
788
|
+
return null;
|
|
789
|
+
}
|
|
790
|
+
openOffload.choice = choice;
|
|
791
|
+
phase("build", `placement: ${choice.machine} (${candidate.reason}${placementLoad(choice)})`);
|
|
792
|
+
return choice;
|
|
793
|
+
}
|
|
794
|
+
/** Builds on the chosen machine and stores the APK under the post-mutation key; false builds here instead unless a machine was named. */
|
|
795
|
+
async function compileElsewhere(choice, candidate) {
|
|
796
|
+
if (!storeKey || !storeHash) {
|
|
797
|
+
if (namedBuildMachine(buildMachine)) fallBack("the build fingerprint or cache key is unavailable");
|
|
798
|
+
return false;
|
|
799
|
+
}
|
|
800
|
+
const stagingDir = join(workspaceDir(root), "offload", PLATFORM);
|
|
801
|
+
const outcome = await offloadBuild({
|
|
802
|
+
choice,
|
|
803
|
+
expectedFingerprint: storeHash,
|
|
804
|
+
request: {
|
|
805
|
+
platform: "android",
|
|
806
|
+
isExpo,
|
|
807
|
+
android: {
|
|
808
|
+
variant,
|
|
809
|
+
abi: buildAbi,
|
|
810
|
+
gradleBuildCache: buildPlan.gradleBuildCache,
|
|
811
|
+
pch: buildPlan.pch,
|
|
812
|
+
compilerCache: buildPlan.compilerCache === "none" ? "none" : "ccache"
|
|
813
|
+
}
|
|
814
|
+
},
|
|
815
|
+
stagingDir,
|
|
816
|
+
onPhase: (name, msg) => phase(name, remotePhaseText(name, msg, choice.machine)),
|
|
817
|
+
onEnter: (name) => {
|
|
818
|
+
if (name === "compile" || name === "build" || name === "prebuild" || name === "pods") record.builtOn = choice.machine;
|
|
819
|
+
place({
|
|
820
|
+
host: choice.machine,
|
|
821
|
+
phase: name
|
|
822
|
+
});
|
|
823
|
+
step(name === "prebuild" ? name : "compile");
|
|
824
|
+
},
|
|
825
|
+
onRecord: (entry) => writer.write({
|
|
826
|
+
...entry,
|
|
827
|
+
offloadedTo: choice.machine
|
|
828
|
+
}),
|
|
829
|
+
note: (line) => phase("build", line)
|
|
830
|
+
});
|
|
831
|
+
place(null);
|
|
832
|
+
let stored = null;
|
|
833
|
+
let reason = outcome.ok ? null : outcome.reason;
|
|
834
|
+
if (outcome.ok) {
|
|
835
|
+
const settled = await refingerprintAfterMutation({
|
|
836
|
+
projectRoot: root,
|
|
837
|
+
platform: PLATFORM,
|
|
838
|
+
previousHash: storeHash,
|
|
839
|
+
fingerprint
|
|
840
|
+
});
|
|
841
|
+
if (!settled || settled.moved) reason = "the checkout here changed while it built";
|
|
842
|
+
else try {
|
|
843
|
+
stored = storeCached(PLATFORM, storeKey, outcome.artifactPath, {
|
|
844
|
+
sources: storeSources,
|
|
845
|
+
overwrite: !useBuildCache
|
|
977
846
|
});
|
|
978
847
|
} catch (err) {
|
|
979
|
-
|
|
980
|
-
if (refusal) {
|
|
981
|
-
phaseFailure = fail$1(refusal.code, refusal.message, refusal.remedy, { lastBuildStatus: true });
|
|
982
|
-
return false;
|
|
983
|
-
}
|
|
984
|
-
phase("build", chalk.yellow(`could not take a build slot: ${err?.message || err}; building anyway`));
|
|
848
|
+
reason = `could not store the APK: ${err?.message || err}`;
|
|
985
849
|
}
|
|
850
|
+
}
|
|
851
|
+
try {
|
|
852
|
+
rmSync(stagingDir, {
|
|
853
|
+
recursive: true,
|
|
854
|
+
force: true
|
|
855
|
+
});
|
|
856
|
+
} catch {}
|
|
857
|
+
step("compile");
|
|
858
|
+
if (!outcome.ok || !stored) {
|
|
859
|
+
const why = reason ?? "the APK was not stored";
|
|
860
|
+
fallbackMachine = choice.machine;
|
|
861
|
+
fallBack(`${choice.machine}: ${why}`, `offload failed: ${why}`);
|
|
862
|
+
writer.write({
|
|
863
|
+
src: "build",
|
|
864
|
+
level: "warn",
|
|
865
|
+
event: "offload_failed",
|
|
866
|
+
msg: reason,
|
|
867
|
+
machine: choice.machine
|
|
868
|
+
});
|
|
869
|
+
return false;
|
|
870
|
+
}
|
|
871
|
+
const { timings } = outcome;
|
|
872
|
+
apkPath = stored;
|
|
873
|
+
record.builtOn = outcome.machine;
|
|
874
|
+
record.offloadedTo = outcome.machine;
|
|
875
|
+
stats.setPlacement({
|
|
876
|
+
decision: "offloaded",
|
|
877
|
+
machine: outcome.machine,
|
|
878
|
+
reason: `${candidate.reason}${placementLoad(choice)}`,
|
|
879
|
+
buildMs: timings.totalMs
|
|
880
|
+
});
|
|
881
|
+
ccacheActivity = outcome.ccache;
|
|
882
|
+
phase("build", `built on ${outcome.machine} in ${formatDuration(timings.totalMs)}: offer ${formatDuration(timings.offerMs)}, sync ${formatDuration(timings.syncMs)}, build ${formatDuration(timings.workerMs)}, fetch ${formatDuration(timings.fetchMs)}`);
|
|
883
|
+
phase("cache", `compilation cache on ${outcome.machine} ${ccacheActivityLine(ccacheActivity)}`);
|
|
884
|
+
writer.write({
|
|
885
|
+
src: "build",
|
|
886
|
+
level: "info",
|
|
887
|
+
event: "offload_done",
|
|
888
|
+
msg: `built on ${outcome.machine}`,
|
|
889
|
+
timings
|
|
890
|
+
});
|
|
891
|
+
return true;
|
|
892
|
+
}
|
|
893
|
+
async function buildArtifact() {
|
|
894
|
+
if (!apkPath) try {
|
|
895
|
+
const offload = placeBuild();
|
|
896
|
+
if (!offload && !await takeBuildSlot()) return false;
|
|
986
897
|
const rekeyedBy = [];
|
|
987
898
|
let editedConfig = [];
|
|
988
899
|
const prebuildPlan = planPrebuildFor(root, PLATFORM, {
|
|
@@ -997,6 +908,7 @@ async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo
|
|
|
997
908
|
return false;
|
|
998
909
|
}
|
|
999
910
|
if (prebuildRan) {
|
|
911
|
+
miss(reasonForMiss([]).reason, true);
|
|
1000
912
|
step("prebuild");
|
|
1001
913
|
recordPrebuild(root, PLATFORM, null);
|
|
1002
914
|
const pre = await prebuild(root, PLATFORM, writer, {
|
|
@@ -1036,6 +948,7 @@ async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo
|
|
|
1036
948
|
if (prepared) {
|
|
1037
949
|
apkPath = prepared;
|
|
1038
950
|
record.cacheHit = "local";
|
|
951
|
+
lateHit();
|
|
1039
952
|
phase("cache", `hit ${shortHash(storeHash)} (post-prebuild key)`);
|
|
1040
953
|
if (releasedWait) {
|
|
1041
954
|
waitedForBuild = releasedWait.facts;
|
|
@@ -1045,9 +958,34 @@ async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo
|
|
|
1045
958
|
}
|
|
1046
959
|
}
|
|
1047
960
|
}
|
|
1048
|
-
if (!apkPath) {
|
|
961
|
+
if (offload && !apkPath) {
|
|
1049
962
|
explainMiss(rekeyedBy);
|
|
1050
963
|
step("compile");
|
|
964
|
+
let built = false;
|
|
965
|
+
if (editedConfig.length) fallBack("prebuild changed config inputs, so the APK cannot be cached", "offload failed: prebuild changed config inputs, so the APK cannot be cached");
|
|
966
|
+
else {
|
|
967
|
+
const choice = await chooseMachine(offload);
|
|
968
|
+
if (choice) built = await compileElsewhere(choice, offload);
|
|
969
|
+
}
|
|
970
|
+
if (!built && !await takeBuildSlot()) return false;
|
|
971
|
+
}
|
|
972
|
+
if (!apkPath) {
|
|
973
|
+
if (!offload) {
|
|
974
|
+
explainMiss(rekeyedBy);
|
|
975
|
+
step("compile");
|
|
976
|
+
}
|
|
977
|
+
if (offload) stats.setPlacement({
|
|
978
|
+
decision: "fell-back",
|
|
979
|
+
slotWaitMs,
|
|
980
|
+
reason: record.offloadFallback ?? "offload failed",
|
|
981
|
+
...fallbackMachine ? { machine: fallbackMachine } : {}
|
|
982
|
+
});
|
|
983
|
+
else stats.setPlacement({
|
|
984
|
+
decision: "here",
|
|
985
|
+
reason: hereReason,
|
|
986
|
+
slotWaitMs
|
|
987
|
+
});
|
|
988
|
+
record.builtOn = "here";
|
|
1051
989
|
phase("build", `compiling ${variant || "debug"} with Gradle`);
|
|
1052
990
|
const built = await build({
|
|
1053
991
|
root,
|
|
@@ -1155,7 +1093,13 @@ async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo
|
|
|
1155
1093
|
});
|
|
1156
1094
|
}
|
|
1157
1095
|
}
|
|
1096
|
+
} catch (error) {
|
|
1097
|
+
if (!(error instanceof OffloadRefusal)) throw error;
|
|
1098
|
+
const refusal = error;
|
|
1099
|
+
phaseFailure = fail$1(refusal.code, refusal.message, refusal.remedy, { lastBuildStatus: true });
|
|
1100
|
+
return false;
|
|
1158
1101
|
} finally {
|
|
1102
|
+
if (openOffload.choice) closeOffload(openOffload.choice);
|
|
1159
1103
|
releaseHeldLock();
|
|
1160
1104
|
releaseHeldSlot();
|
|
1161
1105
|
}
|
|
@@ -1163,6 +1107,8 @@ async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo
|
|
|
1163
1107
|
}
|
|
1164
1108
|
if (!easBuild) {
|
|
1165
1109
|
await prepareCachedArtifact();
|
|
1110
|
+
if (apkPath) lateHit();
|
|
1111
|
+
else if (swapFellBack) miss(reasonForMiss([]).reason);
|
|
1166
1112
|
if (!await buildArtifact()) return refused();
|
|
1167
1113
|
}
|
|
1168
1114
|
return {
|
|
@@ -1186,7 +1132,7 @@ function tail(lines, n = FALLBACK_LINES) {
|
|
|
1186
1132
|
}
|
|
1187
1133
|
//#endregion
|
|
1188
1134
|
//#region src/commands/android/result.ts
|
|
1189
|
-
function androidFacts({ slot, serial, avdName = null, deviceName = null, systemImage = null, deviceProfile = 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, devServer = null }) {
|
|
1135
|
+
function androidFacts({ root, slot, serial, avdName = null, deviceName = null, systemImage = null, deviceProfile = 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, devServer = null, buildMachine = "auto", builtOn, offloadedTo = null, offloadFallback = null }) {
|
|
1190
1136
|
return {
|
|
1191
1137
|
platform: PLATFORM,
|
|
1192
1138
|
...slot && slot !== "default" ? { slot } : {},
|
|
@@ -1201,6 +1147,10 @@ function androidFacts({ slot, serial, avdName = null, deviceName = null, systemI
|
|
|
1201
1147
|
metroPort: metroPort ?? null,
|
|
1202
1148
|
cacheHit: cacheLevel(cacheHit),
|
|
1203
1149
|
cacheSkipped: Boolean(cacheSkipped),
|
|
1150
|
+
buildMachine,
|
|
1151
|
+
...builtOn ? { builtOn } : {},
|
|
1152
|
+
...offloadedTo ? { offloadedTo } : {},
|
|
1153
|
+
...offloadFallback ? { offloadFallback } : {},
|
|
1204
1154
|
waitedForBuild: waitedForBuild ? {
|
|
1205
1155
|
pid: waitedForBuild.pid ?? null,
|
|
1206
1156
|
ms: waitedForBuild.ms ?? 0
|
|
@@ -1214,12 +1164,13 @@ function androidFacts({ slot, serial, avdName = null, deviceName = null, systemI
|
|
|
1214
1164
|
debugHttpHostNote: debugHttpHostNote ?? null,
|
|
1215
1165
|
devClientUrl: devClientUrl ?? null,
|
|
1216
1166
|
logs: logs ?? null,
|
|
1167
|
+
...root ? { agentDevice: { stateDir: workspaceAgentDeviceDir(root) } } : {},
|
|
1217
1168
|
durationMs: typeof durationMs === "number" && Number.isFinite(durationMs) ? durationMs : null,
|
|
1218
1169
|
...lease === void 0 ? {} : { lease },
|
|
1219
1170
|
...devServer ? { devServer } : {}
|
|
1220
1171
|
};
|
|
1221
1172
|
}
|
|
1222
|
-
function lastBuildRecord({ fingerprint, cacheKey, cacheHit, cacheSkipped = false, durationMs, appPath, bundleId, startedAt, status, errorCode = null, avdName = null, deviceName = null, missReason = null, diagnostics = null, configuration = null }) {
|
|
1173
|
+
function lastBuildRecord({ fingerprint, cacheKey, cacheHit, cacheSkipped = false, durationMs, appPath, bundleId, startedAt, status, errorCode = null, avdName = null, deviceName = null, missReason = null, diagnostics = null, configuration = null, buildMachine = "auto", builtOn, offloadedTo = null, offloadFallback = null }) {
|
|
1223
1174
|
const record = {
|
|
1224
1175
|
platform: PLATFORM,
|
|
1225
1176
|
avdName: avdName ?? null,
|
|
@@ -1236,6 +1187,10 @@ function lastBuildRecord({ fingerprint, cacheKey, cacheHit, cacheSkipped = false
|
|
|
1236
1187
|
configuration: configuration ?? null
|
|
1237
1188
|
};
|
|
1238
1189
|
if (errorCode) record.errorCode = errorCode;
|
|
1190
|
+
record.buildMachine = buildMachine;
|
|
1191
|
+
if (builtOn) record.builtOn = builtOn;
|
|
1192
|
+
if (offloadedTo) record.offloadedTo = offloadedTo;
|
|
1193
|
+
if (offloadFallback) record.offloadFallback = offloadFallback;
|
|
1239
1194
|
if (missReason && !cacheLevel(cacheHit)) record.missReason = missReason;
|
|
1240
1195
|
const recorded = status === "failed" ? buildDiagnostics(diagnostics) : [];
|
|
1241
1196
|
if (recorded.length) record.diagnostics = recorded;
|
|
@@ -1258,14 +1213,17 @@ async function finishAndroidUpload(uploadPending, remote, phase) {
|
|
|
1258
1213
|
}
|
|
1259
1214
|
return false;
|
|
1260
1215
|
}
|
|
1261
|
-
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, reclaimed = [], devServer = null }) {
|
|
1216
|
+
function reportAndroidResult({ root, 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, reclaimed = [], devServer = null, links }) {
|
|
1217
|
+
const offloadedTo = record.offloadedTo ?? null;
|
|
1262
1218
|
recordRun({
|
|
1263
1219
|
failed: false,
|
|
1264
1220
|
cacheHit: cacheLevel(record.cacheHit),
|
|
1265
1221
|
waited: waitedForBuild,
|
|
1266
|
-
durationMs
|
|
1222
|
+
durationMs,
|
|
1223
|
+
offloadedTo
|
|
1267
1224
|
});
|
|
1268
1225
|
const facts = androidFacts({
|
|
1226
|
+
root,
|
|
1269
1227
|
slot,
|
|
1270
1228
|
serial,
|
|
1271
1229
|
avdName: record.avdName,
|
|
@@ -1290,18 +1248,23 @@ function reportAndroidResult({ slot, json, useBuildCache, variant, release, metr
|
|
|
1290
1248
|
logs: logsDir,
|
|
1291
1249
|
durationMs,
|
|
1292
1250
|
lease,
|
|
1293
|
-
devServer
|
|
1251
|
+
devServer,
|
|
1252
|
+
buildMachine: record.buildMachine,
|
|
1253
|
+
builtOn: record.builtOn ?? void 0,
|
|
1254
|
+
offloadedTo,
|
|
1255
|
+
offloadFallback: record.offloadFallback ?? null
|
|
1294
1256
|
});
|
|
1295
1257
|
writer.close();
|
|
1296
|
-
if (json) emit(JSON.stringify(
|
|
1258
|
+
if (json) emit(JSON.stringify({
|
|
1297
1259
|
...facts,
|
|
1298
|
-
|
|
1299
|
-
|
|
1260
|
+
...links ? { links } : {},
|
|
1261
|
+
...reclaimed.length ? { reclaimed } : {}
|
|
1262
|
+
}));
|
|
1300
1263
|
else {
|
|
1301
|
-
const summary = `${launchWarning ? "WARNING" : "OK"}: ${androidPackage} launched on ${serial}, ${release ? `${variant} (embedded JS, no Metro)` : `Metro port ${metroPort}`} (${cacheOutcome(record.cacheHit, remote?.name ?? providerName)})`;
|
|
1264
|
+
const summary = `${launchWarning ? "WARNING" : "OK"}: ${androidPackage} launched on ${serial}, ${release ? `${variant} (embedded JS, no Metro)` : `Metro port ${metroPort}`} (${cacheOutcome(record.cacheHit, remote?.name ?? providerName, offloadedTo)})`;
|
|
1302
1265
|
const outcome = launchWarning ? chalk.yellow(`${summary} -- ${launchWarning}`) : launchState === "unverified" ? chalk.yellow(`${summary} -- launch UNVERIFIED`) : launchState === "bundling" ? chalk.green(`${summary} -- bundle requested, still building`) : chalk.green(summary);
|
|
1303
1266
|
const deviceName = record.avdName || record.deviceName || serial;
|
|
1304
|
-
const cacheResult = useBuildCache ? cacheOutcome(record.cacheHit, remote?.name ?? providerName) :
|
|
1267
|
+
const cacheResult = useBuildCache ? cacheOutcome(record.cacheHit, remote?.name ?? providerName, offloadedTo) : `bypassed; ${cacheOutcome(false, null, offloadedTo)}`;
|
|
1305
1268
|
const metroResult = release ? `embedded (${variant})` : !metroCheck ? `check skipped on port ${metroPort}` : launchState === "unverified" ? `state unverified on port ${metroPort}` : `running on port ${metroPort}${devServer ? ` (started: ${devServer.reason})` : ""}`;
|
|
1306
1269
|
emit([
|
|
1307
1270
|
outcome,
|
|
@@ -1310,15 +1273,17 @@ function reportAndroidResult({ slot, json, useBuildCache, variant, release, metr
|
|
|
1310
1273
|
phaseLine("metro", metroResult),
|
|
1311
1274
|
phaseLine("cache", cacheResult),
|
|
1312
1275
|
...ccache.status === "not-run" ? [phaseLine("compilation cache", ccacheActivityLine(ccache))] : [],
|
|
1313
|
-
phaseLine("logs", logsDir || "unavailable (remote device)")
|
|
1276
|
+
phaseLine("logs", logsDir || "unavailable (remote device)"),
|
|
1277
|
+
phaseLine("agent-device", `AGENT_DEVICE_STATE_DIR=${workspaceAgentDeviceDir(root)}`)
|
|
1314
1278
|
].join("\n"));
|
|
1279
|
+
if (links) console.error(chalk.dim(workspaceLinkLine(links)));
|
|
1315
1280
|
}
|
|
1316
1281
|
return facts;
|
|
1317
1282
|
}
|
|
1318
|
-
function cacheOutcome(cacheHit, providerName = null) {
|
|
1283
|
+
function cacheOutcome(cacheHit, providerName = null, offloadedTo = null) {
|
|
1319
1284
|
if (cacheHit === "remote") return `cache hit from ${providerName || "the remote cache"}`;
|
|
1320
1285
|
if (cacheHit === "local") return "cache hit";
|
|
1321
|
-
return "built";
|
|
1286
|
+
return offloadedTo ? `built on ${offloadedTo}` : "built";
|
|
1322
1287
|
}
|
|
1323
1288
|
function persistLastBuild({ recordBuild = recordFinishedBuild, root, record, startedAt, durationMs, status, errorCode = null, diagnostics = null, out }) {
|
|
1324
1289
|
const lastBuild = lastBuildRecord({
|
|
@@ -1342,7 +1307,7 @@ function collectorEntry() {
|
|
|
1342
1307
|
return spawnEntry("collector-run");
|
|
1343
1308
|
}
|
|
1344
1309
|
function collectorLogFile(root, slot = "default") {
|
|
1345
|
-
return join(workspaceLogsDir(root), `collector-${deviceSlotFileKey(PLATFORM, slot)}.log`);
|
|
1310
|
+
return join(workspaceLogsDir$1(root), `collector-${deviceSlotFileKey(PLATFORM, slot)}.log`);
|
|
1346
1311
|
}
|
|
1347
1312
|
const COLLECTOR_EXIT_WAIT_MS = 2e3;
|
|
1348
1313
|
const COLLECTOR_POLL_MS = 25;
|
|
@@ -1383,7 +1348,7 @@ async function startCollector({ root, slot = "default", serial, packageName, spa
|
|
|
1383
1348
|
}
|
|
1384
1349
|
let stdio = "ignore";
|
|
1385
1350
|
try {
|
|
1386
|
-
mkdirSync(workspaceLogsDir(root), { recursive: true });
|
|
1351
|
+
mkdirSync(workspaceLogsDir$1(root), { recursive: true });
|
|
1387
1352
|
const fd = openSync(collectorLogFile(root, slot), "a");
|
|
1388
1353
|
stdio = [
|
|
1389
1354
|
"ignore",
|
|
@@ -1455,9 +1420,9 @@ async function verifyAndroidRun({ root, slot, release, remoteRelease, remoteDevi
|
|
|
1455
1420
|
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."));
|
|
1456
1421
|
return { state: LAUNCH_FATAL };
|
|
1457
1422
|
}
|
|
1458
|
-
const
|
|
1459
|
-
const timeoutMs =
|
|
1460
|
-
if (metroCheck &&
|
|
1423
|
+
const freshEmulator = Boolean((device.created || device.adoptionPending) && device.owned && !remoteDevice);
|
|
1424
|
+
const timeoutMs = freshEmulator ? 6e4 : VERIFY_TIMEOUT_MS;
|
|
1425
|
+
if (metroCheck && freshEmulator) phase("verify", `waiting up to 60s for bundle load (${device.created ? "new" : "adopted"} emulator)`);
|
|
1461
1426
|
const siblings = metroCheck ? siblingPlatformSlots(root, "android", slot) : [];
|
|
1462
1427
|
const verification = metroCheck ? await verifyLaunched({
|
|
1463
1428
|
timeoutMs,
|
|
@@ -1553,6 +1518,60 @@ async function verifyAndroidRun({ root, slot, release, remoteRelease, remoteDevi
|
|
|
1553
1518
|
})) phase("", chalk.yellow(line));
|
|
1554
1519
|
return { state: LAUNCH_UNVERIFIED };
|
|
1555
1520
|
}
|
|
1521
|
+
const ADOPTED_INSTALL_HEADROOM_BYTES = 536870912;
|
|
1522
|
+
const INSUFFICIENT_STORAGE = /INSTALL_FAILED_INSUFFICIENT_STORAGE/;
|
|
1523
|
+
async function wipeAdoptedAvd(room, free) {
|
|
1524
|
+
room.phase("device", chalk.yellow(`${room.avdName} has ${free === null ? "too little space" : `${formatBytes(free)} free`} on /data after removing other apps and trimming caches; wiping its user data`));
|
|
1525
|
+
const expectedRecord = deviceSlotPlatforms(loadConfig$1()?.projects?.[room.root], room.slot)?.android;
|
|
1526
|
+
if (!expectedRecord?.adoptionPending || expectedRecord.avdName !== room.avdName) throw new Error("the emulator assignment changed before the wipe; its data was kept");
|
|
1527
|
+
const wiped = room.wipeDevice(room.avdName, {
|
|
1528
|
+
wipe: true,
|
|
1529
|
+
owner: {
|
|
1530
|
+
projectPath: room.root,
|
|
1531
|
+
slot: room.slot,
|
|
1532
|
+
expectedRecord
|
|
1533
|
+
},
|
|
1534
|
+
workspace: room.root
|
|
1535
|
+
});
|
|
1536
|
+
if (wiped.status !== "torn-down") throw new Error(`could not wipe it: ${wiped.reason ?? wiped.status}`);
|
|
1537
|
+
const rebooted = await room.rebootDevice();
|
|
1538
|
+
if (!rebooted.ok || !rebooted.serial) throw new Error(`it did not boot after its user data was wiped: ${rebooted.reason ?? "no serial"}`);
|
|
1539
|
+
if (room.resolveAvdSerial(room.avdName).serial !== rebooted.serial) throw new Error(`${rebooted.serial} is not running ${room.avdName} after the wipe`);
|
|
1540
|
+
room.phase("device", `${room.avdName} (${rebooted.serial}) booted with wiped user data`);
|
|
1541
|
+
return rebooted.serial;
|
|
1542
|
+
}
|
|
1543
|
+
async function installOnAdoptedAvd(room, serial, installApk) {
|
|
1544
|
+
const neededBytes = 2 * statSync(room.apkPath).size + ADOPTED_INSTALL_HEADROOM_BYTES;
|
|
1545
|
+
const roomy = (free) => free === null || free >= neededBytes;
|
|
1546
|
+
const outOfSpace = (result) => Boolean(result.failed) && INSUFFICIENT_STORAGE.test(result.reason ?? "");
|
|
1547
|
+
let target = serial;
|
|
1548
|
+
let wiped = false;
|
|
1549
|
+
if (!roomy(room.dataFreeBytes(target))) {
|
|
1550
|
+
room.trimCaches(target, neededBytes);
|
|
1551
|
+
const free = room.dataFreeBytes(target);
|
|
1552
|
+
if (roomy(free)) room.phase("install", `trimmed app caches on ${room.avdName} to make room for the APK`);
|
|
1553
|
+
else {
|
|
1554
|
+
target = await wipeAdoptedAvd(room, free);
|
|
1555
|
+
wiped = true;
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
let installed = installApk(target);
|
|
1559
|
+
if (wiped || !outOfSpace(installed)) return {
|
|
1560
|
+
serial: target,
|
|
1561
|
+
installed
|
|
1562
|
+
};
|
|
1563
|
+
room.trimCaches(target, neededBytes);
|
|
1564
|
+
installed = installApk(target);
|
|
1565
|
+
if (!outOfSpace(installed)) return {
|
|
1566
|
+
serial: target,
|
|
1567
|
+
installed
|
|
1568
|
+
};
|
|
1569
|
+
target = await wipeAdoptedAvd(room, room.dataFreeBytes(target));
|
|
1570
|
+
return {
|
|
1571
|
+
serial: target,
|
|
1572
|
+
installed: installApk(target)
|
|
1573
|
+
};
|
|
1574
|
+
}
|
|
1556
1575
|
async function resolveInstallSerial({ root, slot, device, physical, remoteDevice, serial, resolveAvdSerial, waitForDeviceBoot, phase }) {
|
|
1557
1576
|
if (physical || remoteDevice || !device.owned || !device.avdName) return serial;
|
|
1558
1577
|
const resolved = resolveAvdSerial(device.avdName, { timeoutMs: 5e3 });
|
|
@@ -1575,7 +1594,7 @@ async function resolveInstallSerial({ root, slot, device, physical, remoteDevice
|
|
|
1575
1594
|
});
|
|
1576
1595
|
return resolved.serial;
|
|
1577
1596
|
}
|
|
1578
|
-
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, recordBuild, now, out, emit, recordRun, reclaimed, devServer, enterPhase }) {
|
|
1597
|
+
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, recordBuild, now, out, emit, recordRun, reclaimed, devServer, enterPhase, rebootDevice, dataFreeBytes = androidDataFreeBytes, trimCaches = trimAndroidCaches, wipeDevice = teardownOwnedAvd }) {
|
|
1579
1598
|
let androidPackage = initialPackage;
|
|
1580
1599
|
let leaseWarned = false;
|
|
1581
1600
|
const raiseLeaseFor = (boundMs, beforeInstall) => {
|
|
@@ -1591,7 +1610,7 @@ async function finishAndroidRun({ lease, releaseLease, root, slot, json, metroCh
|
|
|
1591
1610
|
}
|
|
1592
1611
|
return null;
|
|
1593
1612
|
};
|
|
1594
|
-
enterPhase("
|
|
1613
|
+
enterPhase("device");
|
|
1595
1614
|
const booted = await bootPromise;
|
|
1596
1615
|
const runCommand = nativeRunCommand("android", slot, {
|
|
1597
1616
|
physical,
|
|
@@ -1635,13 +1654,37 @@ async function finishAndroidRun({ lease, releaseLease, root, slot, json, metroCh
|
|
|
1635
1654
|
}
|
|
1636
1655
|
const lostBeforeInstall = physical ? raiseLeaseFor(ADB_INSTALL_TIMEOUT_MS, true) : null;
|
|
1637
1656
|
if (lostBeforeInstall) return lostBeforeInstall;
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1657
|
+
enterPhase("install");
|
|
1658
|
+
let installTimer = stepTimer(now);
|
|
1659
|
+
const installApk = (target) => {
|
|
1660
|
+
installTimer = stepTimer(now);
|
|
1661
|
+
return install({
|
|
1662
|
+
serial: target,
|
|
1663
|
+
apkPath,
|
|
1664
|
+
packageName: androidPackage,
|
|
1665
|
+
allowUninstall: release || adopting
|
|
1666
|
+
});
|
|
1667
|
+
};
|
|
1668
|
+
let installed;
|
|
1669
|
+
if (adopting && device.avdName && apkPath) {
|
|
1670
|
+
const room = {
|
|
1671
|
+
root,
|
|
1672
|
+
slot,
|
|
1673
|
+
avdName: device.avdName,
|
|
1674
|
+
apkPath,
|
|
1675
|
+
phase,
|
|
1676
|
+
rebootDevice,
|
|
1677
|
+
dataFreeBytes,
|
|
1678
|
+
trimCaches,
|
|
1679
|
+
wipeDevice,
|
|
1680
|
+
resolveAvdSerial
|
|
1681
|
+
};
|
|
1682
|
+
try {
|
|
1683
|
+
({serial, installed} = await installOnAdoptedAvd(room, serial, installApk));
|
|
1684
|
+
} catch (error) {
|
|
1685
|
+
return fail(INSTALL_FAILED, `Could not make room on adopted emulator ${device.avdName}: ${String(error?.message || error)}`, `Run \`${runCommand}\` again; adoption remains pending, so it retries the cleanup.`);
|
|
1686
|
+
}
|
|
1687
|
+
} else installed = installApk(serial);
|
|
1645
1688
|
if (installed.failed) {
|
|
1646
1689
|
const installRemedy = remoteDevice?.failureRemedy() ?? localInstallRemedy({
|
|
1647
1690
|
reason: installed.reason,
|
|
@@ -1841,6 +1884,7 @@ async function finishAndroidRun({ lease, releaseLease, root, slot, json, metroCh
|
|
|
1841
1884
|
const leaseFacts = lease?.facts() ?? null;
|
|
1842
1885
|
releaseLease();
|
|
1843
1886
|
const facts = reportAndroidResult({
|
|
1887
|
+
root,
|
|
1844
1888
|
slot,
|
|
1845
1889
|
json,
|
|
1846
1890
|
useBuildCache,
|
|
@@ -1867,7 +1911,11 @@ async function finishAndroidRun({ lease, releaseLease, root, slot, json, metroCh
|
|
|
1867
1911
|
lease: physical ? leaseFacts : void 0,
|
|
1868
1912
|
recordRun,
|
|
1869
1913
|
reclaimed,
|
|
1870
|
-
devServer
|
|
1914
|
+
devServer,
|
|
1915
|
+
links: workspaceLinks(root, {
|
|
1916
|
+
platform: "android",
|
|
1917
|
+
slot
|
|
1918
|
+
})
|
|
1871
1919
|
});
|
|
1872
1920
|
if (remoteWasAbandoned || uploadWasAbandoned) exitAfterFlush(0);
|
|
1873
1921
|
return {
|
|
@@ -2004,10 +2052,12 @@ function resolveAndroidRunPlan({ settings, settingsContext, slot, easProfile, va
|
|
|
2004
2052
|
if ("error" in waitParsed) return fail("STIM_BAD_ARG", waitParsed.error, "Pass a whole number of seconds, e.g. --wait 90. `--wait 0` refuses a leased device at once.");
|
|
2005
2053
|
const waitSeconds = waitParsed.seconds;
|
|
2006
2054
|
const remoteBackend = physical ? null : commandRemoteBackend ?? remoteAndroidSetting(settings);
|
|
2055
|
+
const settingsLayersForOrigin = settingsLayers(settingsContext);
|
|
2007
2056
|
const imageRefusal = systemImageRefusal({
|
|
2008
2057
|
slot,
|
|
2009
2058
|
flag: systemImageFlag,
|
|
2010
2059
|
resolved: systemImage,
|
|
2060
|
+
origin: settingOriginScope(settingsLayersForOrigin, "android.systemImage"),
|
|
2011
2061
|
physical,
|
|
2012
2062
|
remoteBackend,
|
|
2013
2063
|
listImages: listSystemImages
|
|
@@ -2016,11 +2066,18 @@ function resolveAndroidRunPlan({ settings, settingsContext, slot, easProfile, va
|
|
|
2016
2066
|
const profileRefusal = deviceProfileRefusal({
|
|
2017
2067
|
flag: deviceProfileFlag,
|
|
2018
2068
|
resolved: deviceProfile,
|
|
2069
|
+
origin: settingOriginScope(settingsLayersForOrigin, "android.deviceProfile"),
|
|
2019
2070
|
physical,
|
|
2020
2071
|
remoteBackend,
|
|
2021
2072
|
listProfiles: listDeviceProfiles
|
|
2022
2073
|
});
|
|
2023
2074
|
if (profileRefusal) return fail(profileRefusal.code, profileRefusal.message, profileRefusal.remedy);
|
|
2075
|
+
const avdFlagRefusal = remoteAvdFlagRefusal({
|
|
2076
|
+
systemImageFlag,
|
|
2077
|
+
deviceProfileFlag,
|
|
2078
|
+
remoteBackend
|
|
2079
|
+
});
|
|
2080
|
+
if (avdFlagRefusal) return fail(avdFlagRefusal.code, avdFlagRefusal.message, avdFlagRefusal.remedy);
|
|
2024
2081
|
if (!physical && !remoteBackend) {
|
|
2025
2082
|
const flagImage = typeof systemImageFlag === "string" && systemImageFlag.trim() ? systemImageFlag.trim() : null;
|
|
2026
2083
|
const existing = ownedAvd(root, slot);
|
|
@@ -2077,7 +2134,7 @@ function resolveAndroidRunPlan({ settings, settingsContext, slot, easProfile, va
|
|
|
2077
2134
|
//#endregion
|
|
2078
2135
|
//#region src/commands/android/next-build.ts
|
|
2079
2136
|
const DEFAULT_PLAN_DEPS = {
|
|
2080
|
-
findRoot: findProjectRoot,
|
|
2137
|
+
findRoot: findProjectRoot$1,
|
|
2081
2138
|
fingerprint: fingerprintProject,
|
|
2082
2139
|
listSystemImages: listInstalledSystemImages,
|
|
2083
2140
|
avdSystemImage: ownedAvdSystemImage,
|
|
@@ -2180,7 +2237,7 @@ async function planAndroid(opts, overrides = {}) {
|
|
|
2180
2237
|
platform: PLATFORM,
|
|
2181
2238
|
slot,
|
|
2182
2239
|
root,
|
|
2183
|
-
projectKey: statsProjectKey({
|
|
2240
|
+
projectKey: statsProjectKey$1({
|
|
2184
2241
|
root,
|
|
2185
2242
|
commonDir: settingsContext.gitCommonDir,
|
|
2186
2243
|
repoRoot: settingsContext.repoRoot
|
|
@@ -2267,12 +2324,13 @@ function androidCommand(program) {
|
|
|
2267
2324
|
registerAndroid(program);
|
|
2268
2325
|
}
|
|
2269
2326
|
function registerAndroid(program) {
|
|
2270
|
-
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. A Debug run starts the dev server when it is not running.").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)", parseDeviceSlotOption).option("--json", "Emit the facts as a single JSON line on stdout; every other line goes to stderr").option("--plan", "Predict the next build without building, booting or installing: resolve the fingerprint, check the local then remote cache, and print the expected cache result and duration. Writes no Stim state.").option("--no-metro-check", "Skip the reserved-port Metro health check and do not start it (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-profile <id>", "Hardware profile to create this workspace's owned AVD with, as an `avdmanager list device -c` id (e.g. \"pixel_fold\", \"pixel_tablet\"). Overrides the android.deviceProfile setting for this invocation. An unknown id refuses with STIM_BAD_ARG and prints the available ids.").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) => {
|
|
2327
|
+
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. A Debug run starts the dev server when it is not running.").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)", parseDeviceSlotOption).option("--build-machine <value>", "Build on auto, local, or one named machine; a name refuses without fallback", parseBuildMachineOption).option("--json", "Emit the facts as a single JSON line on stdout; every other line goes to stderr").option("--plan", "Predict the next build without building, booting or installing: resolve the fingerprint, check the local then remote cache, and print the expected cache result and duration. Writes no Stim state.").option("--no-metro-check", "Skip the reserved-port Metro health check and do not start it (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-profile <id>", "Hardware profile to create this workspace's owned AVD with, as an `avdmanager list device -c` id (e.g. \"pixel_fold\", \"pixel_tablet\"). Overrides the android.deviceProfile setting for this invocation. An unknown id refuses with STIM_BAD_ARG and prints the available ids.").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) => {
|
|
2271
2328
|
if (REMOTE_DEVICE_BACKENDS.includes(value)) return value;
|
|
2272
|
-
throw new InvalidArgumentError(`expected one of: ${REMOTE_DEVICE_BACKENDS.join(", ")}`);
|
|
2329
|
+
if (!parseMachine(value)) throw new InvalidArgumentError(`expected one of: ${REMOTE_DEVICE_BACKENDS.join(", ")}`);
|
|
2330
|
+
throw new InvalidArgumentError("STIM_BAD_ARG: Android on a paired Mac is not available yet. Use eas or proxy, or run Android locally.");
|
|
2273
2331
|
}).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) => {
|
|
2274
2332
|
if (opts.plan) return planAndroid(opts);
|
|
2275
|
-
const root = findProjectRoot(process.cwd());
|
|
2333
|
+
const root = findProjectRoot$1(process.cwd());
|
|
2276
2334
|
if (!root) {
|
|
2277
2335
|
refuseNoProject({ json: Boolean(opts.json) });
|
|
2278
2336
|
return;
|
|
@@ -2293,6 +2351,7 @@ function registerAndroid(program) {
|
|
|
2293
2351
|
try {
|
|
2294
2352
|
return await runAndroid({
|
|
2295
2353
|
root,
|
|
2354
|
+
buildMachine: opts.buildMachine,
|
|
2296
2355
|
slot: opts.slot,
|
|
2297
2356
|
easProfile: opts.easProfile,
|
|
2298
2357
|
json: Boolean(opts.json),
|
|
@@ -2313,7 +2372,7 @@ function registerAndroid(program) {
|
|
|
2313
2372
|
}, { write: (line) => console.error(chalk.dim(phaseLine("lock", line))) })).ok) process.exit(runCancellation() ? 130 : 1);
|
|
2314
2373
|
});
|
|
2315
2374
|
}
|
|
2316
|
-
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, deviceProfile: deviceProfileFlag, 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, listDeviceProfiles, ensureDeviceBooted = ensureBooted, resolveAvdSerial = resolveOwnedAvdSerial, waitForDeviceBoot = waitForBoot, resolveMetro = resolveProjectMetro, warmMetro: prewarmMetro = warmMetro, startServer = startDevServer, readState = readWorkspaceState$1, 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, planPrebuildFor = planPrebuild, 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, recordBuild, recordStats = recordRunStats, readEstimates = readRunEstimates, now = Date.now, out = (line) => console.error(line), emit = (line) => console.log(line) } = {}) {
|
|
2375
|
+
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, deviceProfile: deviceProfileFlag, 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, listDeviceProfiles, ensureDeviceBooted = ensureBooted, resolveAvdSerial = resolveOwnedAvdSerial, waitForDeviceBoot = waitForBoot, dataFreeBytes, trimCaches, wipeDevice, resolveMetro = resolveProjectMetro, warmMetro: prewarmMetro = warmMetro, startServer = startDevServer, readState = readWorkspaceState$1, 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, planPrebuildFor = planPrebuild, 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, recordBuild, recordStats = recordRunStats, readEstimates = readRunEstimates, now = Date.now, out = (line) => console.error(line), emit = (line) => console.log(line) } = {}) {
|
|
2317
2376
|
return {
|
|
2318
2377
|
root,
|
|
2319
2378
|
json,
|
|
@@ -2354,6 +2413,9 @@ function resolveRunAndroidOptions({ root, json = false, remoteDevice: commandRem
|
|
|
2354
2413
|
ensureDeviceBooted,
|
|
2355
2414
|
resolveAvdSerial,
|
|
2356
2415
|
waitForDeviceBoot,
|
|
2416
|
+
dataFreeBytes,
|
|
2417
|
+
trimCaches,
|
|
2418
|
+
wipeDevice,
|
|
2357
2419
|
resolveMetro,
|
|
2358
2420
|
prewarmMetro,
|
|
2359
2421
|
startServer,
|
|
@@ -2453,7 +2515,7 @@ function avdSetupFailure(error, root, logFile) {
|
|
|
2453
2515
|
};
|
|
2454
2516
|
}
|
|
2455
2517
|
async function runAndroid(options = {}) {
|
|
2456
|
-
let { root, json, commandRemoteBackend, resolveSettingsFor, resolveRemoteDeviceContext, makeRemoteDeviceDeps, resolveEasBin, ensureRemoteMetro, ensureRemoteOwned, detectRemoteProviders, metroCheck, useBuildCache: requestedBuildCache, easProfile, resolveEasBuild, variantFlag, systemImageFlag, deviceProfileFlag, deviceFlag, waitFlag, waitConflict, acquireLease, makeRunLease, selectPool, onLeaseSignal, listDevices, deviceModel, deviceAbi, isEmulatorDevice, readApkPackage, getLimits, checkCapacity, acquireSlot, releaseSlot, ensureDevice, listSystemImages, listDeviceProfiles, ensureDeviceBooted, resolveAvdSerial, waitForDeviceBoot, resolveMetro, prewarmMetro, startServer, readState, pidAlive, verifyCollector, verifyLaunched, ensureStorage, fingerprint, untracked, resolveCached, storeCached, storedAssets, captureAssets, acquireLock, releaseLock, waitForBuild, loadProvider, easAuth, resolveRemoteBuild, uploadRemoteBuild, resolveCacheProvider, loadCacheProviderModule, planPrebuildFor, prebuild, build, ccacheFor, install, launch, launchRelease, verifyReleaseLaunched, swapApk, resolveDevClientScheme, spawn, kill, createWriter, writeLaunch, writeState, recordBuild, recordStats, readEstimates, now, out, emit } = androidSlotOptions(options);
|
|
2518
|
+
let { root, json, commandRemoteBackend, resolveSettingsFor, resolveRemoteDeviceContext, makeRemoteDeviceDeps, resolveEasBin, ensureRemoteMetro, ensureRemoteOwned, detectRemoteProviders, metroCheck, useBuildCache: requestedBuildCache, easProfile, resolveEasBuild, variantFlag, systemImageFlag, deviceProfileFlag, deviceFlag, waitFlag, waitConflict, acquireLease, makeRunLease, selectPool, onLeaseSignal, listDevices, deviceModel, deviceAbi, isEmulatorDevice, readApkPackage, getLimits, checkCapacity, acquireSlot, releaseSlot, ensureDevice, listSystemImages, listDeviceProfiles, ensureDeviceBooted, resolveAvdSerial, waitForDeviceBoot, dataFreeBytes, trimCaches, wipeDevice, resolveMetro, prewarmMetro, startServer, readState, pidAlive, verifyCollector, verifyLaunched, ensureStorage, fingerprint, untracked, resolveCached, storeCached, storedAssets, captureAssets, acquireLock, releaseLock, waitForBuild, loadProvider, easAuth, resolveRemoteBuild, uploadRemoteBuild, resolveCacheProvider, loadCacheProviderModule, planPrebuildFor, prebuild, build, ccacheFor, install, launch, launchRelease, verifyReleaseLaunched, swapApk, resolveDevClientScheme, spawn, kill, createWriter, writeLaunch, writeState, recordBuild, recordStats, readEstimates, now, out, emit } = androidSlotOptions(options);
|
|
2457
2519
|
const progress = options.progress ?? NO_BUILD_PROGRESS;
|
|
2458
2520
|
const slot = validateDeviceSlot(options.slot);
|
|
2459
2521
|
const started = now();
|
|
@@ -2499,12 +2561,15 @@ async function runAndroid(options = {}) {
|
|
|
2499
2561
|
}
|
|
2500
2562
|
};
|
|
2501
2563
|
}
|
|
2502
|
-
const logsDir = workspaceLogsDir(root);
|
|
2564
|
+
const logsDir = workspaceLogsDir$1(root);
|
|
2503
2565
|
const buildLog = join(logsDir, `build-${deviceSlotFileKey("android", slot)}.ndjson`);
|
|
2504
|
-
const writer = createWriter(buildLog, {
|
|
2566
|
+
const writer = tapBuildLog(createWriter(buildLog, {
|
|
2505
2567
|
truncate: true,
|
|
2506
|
-
fields: {
|
|
2507
|
-
|
|
2568
|
+
fields: {
|
|
2569
|
+
platform: PLATFORM,
|
|
2570
|
+
slot
|
|
2571
|
+
}
|
|
2572
|
+
}), progress);
|
|
2508
2573
|
const record = {
|
|
2509
2574
|
fingerprint: null,
|
|
2510
2575
|
cacheKey: null,
|
|
@@ -2520,12 +2585,13 @@ async function runAndroid(options = {}) {
|
|
|
2520
2585
|
write: recordStats,
|
|
2521
2586
|
now,
|
|
2522
2587
|
note: (line) => out(phaseLine("stats", chalk.dim(line))),
|
|
2523
|
-
phases: () => progress.durations()
|
|
2588
|
+
phases: () => progress.durations(),
|
|
2589
|
+
deviceSetup: () => progress.deviceSetupKnown()
|
|
2524
2590
|
});
|
|
2525
2591
|
const recordRun = stats.record;
|
|
2526
2592
|
let ccacheActivity = CCACHE_NOT_RUN;
|
|
2527
2593
|
let reclaimed = [];
|
|
2528
|
-
const fail = (code, message, remedy, { lastBuildStatus = false, diagnostics = [], buildDiagnostics: rawDiagnostics = [], lines = [], logPath = null, lease } = {}) => {
|
|
2594
|
+
const fail = (code, message, remedy, { lastBuildStatus = false, setup = false, diagnostics = [], buildDiagnostics: rawDiagnostics = [], lines = [], logPath = null, lease } = {}) => {
|
|
2529
2595
|
const cancellation = cancelledFailure(PLATFORM, {
|
|
2530
2596
|
code,
|
|
2531
2597
|
message
|
|
@@ -2547,7 +2613,7 @@ async function runAndroid(options = {}) {
|
|
|
2547
2613
|
for (const line of lines) out(phaseLine("", chalk.dim(line)));
|
|
2548
2614
|
if (remedy) out(phaseLine("remedy", remedy));
|
|
2549
2615
|
if (logPath) out(phaseLine("log", logPath));
|
|
2550
|
-
recordRun({
|
|
2616
|
+
if (!setup) recordRun({
|
|
2551
2617
|
failed: true,
|
|
2552
2618
|
durationMs: now() - started
|
|
2553
2619
|
});
|
|
@@ -2576,18 +2642,25 @@ async function runAndroid(options = {}) {
|
|
|
2576
2642
|
gitCommonDir: gitCommonDir(root),
|
|
2577
2643
|
repoRoot: settingsRepoRoot
|
|
2578
2644
|
};
|
|
2579
|
-
const projectKey = statsProjectKey({
|
|
2645
|
+
const projectKey = statsProjectKey$1({
|
|
2580
2646
|
root,
|
|
2581
2647
|
commonDir: settingsContext.gitCommonDir,
|
|
2582
2648
|
repoRoot: settingsRepoRoot
|
|
2583
2649
|
});
|
|
2584
2650
|
stats.setProject(projectKey);
|
|
2651
|
+
progress.estimate(projectKey);
|
|
2585
2652
|
let estimatesRead = null;
|
|
2586
2653
|
const estimates = () => estimatesRead ??= readEstimates({
|
|
2587
2654
|
projectKey,
|
|
2588
2655
|
platform: PLATFORM
|
|
2589
2656
|
});
|
|
2590
2657
|
const settings = resolveSettingsFor(settingsContext);
|
|
2658
|
+
const placement = resolveBuildPlacement(options.buildMachine);
|
|
2659
|
+
record.buildMachine = placement.selected;
|
|
2660
|
+
if (placement.failure) {
|
|
2661
|
+
const { code, message, remedy } = placement.failure;
|
|
2662
|
+
return fail(code, message, remedy, { setup: true });
|
|
2663
|
+
}
|
|
2591
2664
|
const planned = resolveAndroidRunPlan({
|
|
2592
2665
|
settings,
|
|
2593
2666
|
settingsContext,
|
|
@@ -2677,8 +2750,16 @@ async function runAndroid(options = {}) {
|
|
|
2677
2750
|
devServer = gate.devServer;
|
|
2678
2751
|
phase("metro", `port ${metroPort} (${devServer ? `started: ${devServer.reason}` : `pid ${gate.pid ?? "unknown, started outside Stim"}`})`);
|
|
2679
2752
|
return true;
|
|
2680
|
-
} else
|
|
2681
|
-
|
|
2753
|
+
} else {
|
|
2754
|
+
const pin = metroPortSetting(root);
|
|
2755
|
+
if (pin.error) {
|
|
2756
|
+
phaseFailure = fail("STIM_BAD_ARG", pin.error, SETTING_SHAPE_REMEDY);
|
|
2757
|
+
return false;
|
|
2758
|
+
}
|
|
2759
|
+
metroPort = pin.port ?? reservedPort ?? 8081;
|
|
2760
|
+
phase("metro", pin.port ?? reservedPort ? `port ${metroPort} (not checked)` : `no reservation; using ${DEFAULT_METRO_PORT} (not checked)`);
|
|
2761
|
+
}
|
|
2762
|
+
if (release) metroPort = null;
|
|
2682
2763
|
return true;
|
|
2683
2764
|
}
|
|
2684
2765
|
if (!await resolveMetroPort()) return phaseFailure;
|
|
@@ -2691,7 +2772,7 @@ async function runAndroid(options = {}) {
|
|
|
2691
2772
|
isExpo,
|
|
2692
2773
|
tunnelMode: tunnelModeSetting(settings) ?? void 0,
|
|
2693
2774
|
publicUrl: publicUrlSetting(settings),
|
|
2694
|
-
available: detectRemoteProviders(binOnPath)
|
|
2775
|
+
available: detectRemoteProviders(binOnPath, tunnelModeSetting(settings) ?? "auto")
|
|
2695
2776
|
});
|
|
2696
2777
|
if ("failed" in reachable) return fail(reachable.code ?? "STIM_NO_REMOTE_SESSION", reachable.failed, reachable.remedy);
|
|
2697
2778
|
}
|
|
@@ -2770,6 +2851,7 @@ async function runAndroid(options = {}) {
|
|
|
2770
2851
|
const failure = avdSetupFailure(err, root, emuLog);
|
|
2771
2852
|
return fail(failure.code, failure.message, failure.remedy, failure.extra);
|
|
2772
2853
|
}
|
|
2854
|
+
progress.deviceSetup(didSetUpDevice(device, Boolean(remoteDevice)));
|
|
2773
2855
|
const prepareMs = prepare();
|
|
2774
2856
|
if (device.created || prepareMs >= 2e3) phase("device", `${device.avdName || device.deviceName || label} ${device.created ? "created" : "prepared"} (${formatDuration(prepareMs)})`);
|
|
2775
2857
|
const boot = () => Promise.resolve(ensureDeviceBooted({
|
|
@@ -2825,6 +2907,7 @@ async function runAndroid(options = {}) {
|
|
|
2825
2907
|
isExpo,
|
|
2826
2908
|
device,
|
|
2827
2909
|
physical,
|
|
2910
|
+
remote: Boolean(remoteDevice),
|
|
2828
2911
|
buildPlan,
|
|
2829
2912
|
cacheProviderConfig,
|
|
2830
2913
|
requestedBuildCache,
|
|
@@ -2837,7 +2920,11 @@ async function runAndroid(options = {}) {
|
|
|
2837
2920
|
out,
|
|
2838
2921
|
estimates,
|
|
2839
2922
|
stats,
|
|
2840
|
-
step: progress.step
|
|
2923
|
+
step: progress.step,
|
|
2924
|
+
miss: progress.miss,
|
|
2925
|
+
hit: progress.hit,
|
|
2926
|
+
place: progress.place,
|
|
2927
|
+
waitingOn: progress.waitingOn
|
|
2841
2928
|
}
|
|
2842
2929
|
}, {
|
|
2843
2930
|
deviceAbi,
|
|
@@ -2877,7 +2964,7 @@ async function runAndroid(options = {}) {
|
|
|
2877
2964
|
record.appPath = apkPath;
|
|
2878
2965
|
if (runCancellation()) return fail("STIM_CANCELLED", "before install");
|
|
2879
2966
|
if (startRemoteBoot) {
|
|
2880
|
-
progress.step("
|
|
2967
|
+
progress.step("device");
|
|
2881
2968
|
bootPromise = startRemoteBoot();
|
|
2882
2969
|
const booted = await bootPromise;
|
|
2883
2970
|
if (booted.failed) {
|
|
@@ -2909,6 +2996,7 @@ async function runAndroid(options = {}) {
|
|
|
2909
2996
|
}
|
|
2910
2997
|
};
|
|
2911
2998
|
if (physical) {
|
|
2999
|
+
progress.step("device");
|
|
2912
3000
|
const acquired = await acquireLease({
|
|
2913
3001
|
root,
|
|
2914
3002
|
platform: PLATFORM,
|
|
@@ -2956,6 +3044,9 @@ async function runAndroid(options = {}) {
|
|
|
2956
3044
|
bootPromise,
|
|
2957
3045
|
resolveAvdSerial,
|
|
2958
3046
|
waitForDeviceBoot,
|
|
3047
|
+
dataFreeBytes,
|
|
3048
|
+
trimCaches,
|
|
3049
|
+
wipeDevice,
|
|
2959
3050
|
bootDuration: () => bootDuration,
|
|
2960
3051
|
apkPath,
|
|
2961
3052
|
androidPackage,
|
|
@@ -2992,7 +3083,20 @@ async function runAndroid(options = {}) {
|
|
|
2992
3083
|
recordRun,
|
|
2993
3084
|
reclaimed,
|
|
2994
3085
|
devServer,
|
|
2995
|
-
enterPhase: progress.step
|
|
3086
|
+
enterPhase: progress.step,
|
|
3087
|
+
rebootDevice: () => Promise.resolve(ensureDeviceBooted({
|
|
3088
|
+
platform: PLATFORM,
|
|
3089
|
+
device: {
|
|
3090
|
+
...device,
|
|
3091
|
+
serial: void 0
|
|
3092
|
+
},
|
|
3093
|
+
projectPath: root,
|
|
3094
|
+
out,
|
|
3095
|
+
logFile: emuLog
|
|
3096
|
+
})).catch((e) => ({
|
|
3097
|
+
failed: true,
|
|
3098
|
+
reason: String(e?.message || e)
|
|
3099
|
+
}))
|
|
2996
3100
|
});
|
|
2997
3101
|
} finally {
|
|
2998
3102
|
releaseLease();
|