stim 1.13.0 → 1.15.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-DfVKZTEB.mjs → activity-B2-kb7k0.mjs} +281 -41
- package/dist/{android-BHfAu2mf.mjs → android-CYmq0jNi.mjs} +2 -2
- package/dist/{android-CD8pr8Q2.mjs → android-Cu_L4RpL.mjs} +524 -412
- package/dist/{android-qx_31wJo.mjs → android-Db2eEMaf.mjs} +152 -65
- package/dist/android-cas-PnKfAdB2.mjs +167 -0
- package/dist/android-cas-compiler.mjs +2 -2
- package/dist/{app-install-CDFY2nUL.mjs → app-install-CAAplXAt.mjs} +162 -173
- package/dist/{budget-E5o91NR1.mjs → budget-CMiqghig.mjs} +1048 -77
- package/dist/{build-plan-BTa93o_r.mjs → build-plan-aeGMFX7e.mjs} +193 -650
- package/dist/build-progress-C2Jv1AtA.mjs +872 -0
- package/dist/build-slots-Df1ApSuH.mjs +127 -0
- package/dist/{cache-manifest-DWPF4RKJ.mjs → cache-manifest-DnUHBJaU.mjs} +2 -2
- package/dist/cache-manifest.mjs +1 -1
- package/dist/{chrome-B5hWgQJS.mjs → chrome-BrcVWQyD.mjs} +4 -4
- package/dist/cli-0cX5s4NM.mjs +45 -0
- package/dist/cli.mjs +6 -42
- package/dist/client-C2IS36Qf.mjs +1333 -0
- package/dist/collector-run.mjs +8 -7
- package/dist/{command-output-BIL7rlX7.mjs → command-output-CliHHn7a.mjs} +1 -1
- package/dist/{config-0jCcMM0E.mjs → config-D0DbpZtM.mjs} +29 -29
- package/dist/{created-devices-Cd558h31.mjs → created-devices-BwVb3liH.mjs} +2 -2
- package/dist/{dependency-state-DNN0yfx6.mjs → dependency-state-BmsjKZ2H.mjs} +2 -2
- package/dist/{deps-2Wo81np9.mjs → deps-BFlKbOIp.mjs} +3 -3
- package/dist/{detached-entry-DJHoca49.mjs → detached-entry-CiTEgMR1.mjs} +1 -1
- package/dist/dev-client-CjzLoNs0.mjs +80 -0
- package/dist/{device-Cf7vl-so.mjs → device-BZ1IyS5A.mjs} +7 -7
- package/dist/device-capacity-DMuKC7nx.mjs +136 -0
- package/dist/device-host-worker.d.mts +1 -0
- package/dist/device-host-worker.mjs +769 -0
- package/dist/device-ios-DMQBUPus.mjs +381 -0
- package/dist/{device-lease-CL9dZf73.mjs → device-lease-W5Pi1q38.mjs} +43 -36
- package/dist/{device-pool-MuoiyRcu.mjs → device-pool-B2r38YjS.mjs} +4 -4
- package/dist/{device-remote-DT9ngZiD.mjs → device-remote-9ulPsdhY.mjs} +68 -162
- package/dist/{doctor-D2iysNNH.mjs → doctor-BcE5xsBn.mjs} +191 -99
- package/dist/{error-diagnostics-BoiOhck8.mjs → error-diagnostics-Cb0P1WLS.mjs} +12 -11
- package/dist/{exec-DK8KaVHK.mjs → exec-CaErVv7t.mjs} +5 -1
- package/dist/{gc-eA-PRBX8.mjs → gc-BuL1Genr.mjs} +430 -102
- package/dist/{android-cas-B9Kv9aIy.mjs → gradle-CmdOBq1d.mjs} +8 -294
- package/dist/{guide-BfZ0OmiP.mjs → guide-cCSGRFd3.mjs} +2052 -240
- package/dist/{guide-status-BaMYu55M.mjs → guide-status-CIoWK6mG.mjs} +3 -3
- package/dist/hosted-macos-DwowNU7W.mjs +447 -0
- package/dist/idle-shutdown-B5LPaNyY.mjs +122 -0
- package/dist/in-use-CndPMilN.mjs +110 -0
- package/dist/{ios-CmEzZiOj.mjs → ios-3CtCRhwr.mjs} +201 -10
- package/dist/{ios-CcfCeZ19.mjs → ios-Chob8ny7.mjs} +560 -259
- package/dist/{ios-device-BW-t_Lkv.mjs → ios-device-DERmMmk-.mjs} +12 -7
- package/dist/{ios-device-BiOsSVzd.mjs → ios-device-Ee7sDXfp.mjs} +1 -1
- package/dist/{launch-verify-CuJaBpEa.mjs → launch-verify-CLlofxRt.mjs} +42 -11
- package/dist/{logs-DcSRbC2t.mjs → logs-h9h6MUvE.mjs} +95 -13
- package/dist/machine-usage-D_YYMzfR.mjs +283 -0
- package/dist/machines-CYtOCqcj.mjs +259 -0
- package/dist/macos-BwtbUtTN.mjs +468 -0
- package/dist/macos-run.d.mts +5 -0
- package/dist/macos-run.mjs +2 -0
- package/dist/{merge-state-ytFQCeh4.mjs → merge-state-BnxLNQiM.mjs} +22 -12
- package/dist/{metro-BPeY1YMh.mjs → metro-Di8EN2Tb.mjs} +20 -120
- package/dist/{named-ports-n7tnJsI8.mjs → named-ports-Dj_XBNaL.mjs} +8 -64
- package/dist/native-run-DFDoIRbY.mjs +147 -0
- package/dist/{native-runtime-CiiFHt0e.mjs → native-runtime-i6OkVg-w.mjs} +11 -6
- package/dist/offload-worker.d.mts +121 -0
- package/dist/offload-worker.mjs +601 -0
- package/dist/{ownership-Bz2kETAS.mjs → ownership-BkYxUtuj.mjs} +3 -2
- package/dist/{page-BTu7I60K.mjs → page-QF_Ke10s.mjs} +7 -2
- package/dist/{ports-C5UutqpJ.mjs → ports-DddwttKM.mjs} +3 -3
- package/dist/prebuild-BvxDlD6C.mjs +213 -0
- package/dist/process-identity-BmRm1qV0.mjs +12 -0
- package/dist/{project-DGeclhvO.mjs → project-CV3tDFHl.mjs} +9 -18
- 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-GEXp4iBT.mjs +37 -0
- package/dist/{reload-C-cV5yy_.mjs → reload-ChPiwvot.mjs} +70 -22
- package/dist/{remote-cache--aehr4Pr.mjs → remote-cache-CO2elm_b.mjs} +4 -4
- package/dist/run-B-xqRrsg.mjs +119 -0
- package/dist/{server-bare-Bq8BpSIo.mjs → server-bare-X7PUgizL.mjs} +60 -13
- package/dist/{server-expo-DUCg52-K.mjs → server-expo-DhtX6bom.mjs} +6 -10
- package/dist/server-expo-KJeD109I.mjs +2 -0
- package/dist/{settings-xB0Frkg6.mjs → settings-CduVcoxj.mjs} +77 -26
- package/dist/{settings-ssVBToDR.mjs → settings-DbL6uhH8.mjs} +16 -10
- package/dist/settings.schema.json +357 -2
- package/dist/{simslim-Bbg9UWrt.mjs → simslim-Ciluxlq4.mjs} +8 -132
- package/dist/slot-launch-BY0BrOLh.mjs +65 -0
- package/dist/{spawn-claims-PT8aQpCa.mjs → spawn-claims-D0XfSe8u.mjs} +1 -1
- package/dist/spawn-entry-DncpJvWL.mjs +16 -0
- package/dist/stage-CzaiwFsI.mjs +143 -0
- package/dist/start-CPO4wRg7.mjs +2 -0
- package/dist/{start-BZhItkiL.mjs → start-CUD9nasA.mjs} +108 -61
- package/dist/state-B4PQFXMW.d.mts +12 -0
- package/dist/state-BxCgeTQu.mjs +41 -0
- package/dist/{state-DwAU2_-D.mjs → state-CaeaHbDA.mjs} +7 -4
- package/dist/{state-l1pYwOre.mjs → state-DFm77wRn.mjs} +67 -5
- package/dist/stats-DBLLFmKd.mjs +66 -0
- package/dist/{status-TJlL0HWo.mjs → status-DYXxNOT0.mjs} +425 -260
- package/dist/{status-sszP4XKU.mjs → status-l84Gbvp2.mjs} +122 -20
- 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-BZ2qMiBP.mjs → stop-Bad61z_2.mjs} +128 -19
- package/dist/stop-Bvf1MflS.mjs +48 -0
- package/dist/{stop-DskDKNTU.mjs → stop-CceYh1yG.mjs} +2 -2
- package/dist/stop-cause-CZ_Mr-v9.mjs +665 -0
- package/dist/supervisor-run.d.mts +44 -12
- package/dist/supervisor-run.mjs +276 -33
- package/dist/support-CDQbDkL5.mjs +315 -0
- package/dist/support-WLOzVKUv.mjs +176 -0
- package/dist/teardown-BYuQafiA.mjs +718 -0
- package/dist/{xcode-LM6A8cNZ.mjs → toolchain-DmeYjXD9.mjs} +363 -13
- package/dist/warm-progress-DVHLN4uC.mjs +463 -0
- package/dist/{idle-DxLi5OkN.mjs → watchman-Bx0wceoF.mjs} +55 -8
- package/dist/{web-sWXd5iek.mjs → web-k6w8-HnE.mjs} +39 -22
- package/dist/web-run.mjs +326 -13
- package/dist/workspace-process-lock-BL7Q0HCO.mjs +57 -0
- package/dist/workspace-state-CL2V2Ons.mjs +371 -0
- package/dist/worktree-BT_uULFx.mjs +2 -0
- package/dist/{worktree-DeBQSVtR.mjs → worktree-C0c5Udwa.mjs} +55 -54
- package/dist/{worktree-D4Do_XiQ.mjs → worktree-qXKzADSC.mjs} +161 -255
- package/package.json +10 -5
- package/shim/bundle-response.cjs +55 -10
- package/shim/bundle-response.d.cts +1 -0
- package/shim/expo-metro-config.cjs +10 -2
- package/dist/build-progress-l7d-X-ZP.mjs +0 -247
- package/dist/in-use-wYEaKQM9.mjs +0 -249
- package/dist/ownership-PUP4Dwai.mjs +0 -1138
- package/dist/server-expo-BeYwP_Xy.mjs +0 -2
- package/dist/slot-launch-BIWEW-wI.mjs +0 -19
- package/dist/start-Rw2GD3nV.mjs +0 -2
- package/dist/stats-CaSwmsSZ.mjs +0 -62
- package/dist/stats-D8rUTPHd.mjs +0 -368
- package/dist/stim-desktop-CXt8-Z3J.mjs +0 -40
- package/dist/worktree-3cBa-bQt.mjs +0 -2
- package/dist/{metro-store-DzeWkfqW.mjs → metro-store-CDDZHtPl.mjs} +1 -1
|
@@ -1,38 +1,48 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
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";
|
|
3
4
|
import { r as createNdjsonWriter } from "./ndjson-CDY9IkVC.mjs";
|
|
4
|
-
import {
|
|
5
|
-
import { D as makeTemporaryDirectory, O as removeTemporaryEntry, s as gitCommonDir, y as repoRoot } from "./worktree-
|
|
6
|
-
import { a as findProjectRoot, l as projectShortcut, n as appProjectProblem, r as detectIsExpo, t as NO_PROJECT_REFUSAL } from "./project-
|
|
7
|
-
import { m as writeWorkspaceLaunch, n as MODE_EXPO, t as MODE_BARE } from "./state-
|
|
5
|
+
import { C as workspaceLogsDir, b as workspaceDir, c as writeWorkspaceState, f as emulatorLogFile, i as recordWorkspaceUse, r as readWorkspaceState$1 } from "./workspace-state-CL2V2Ons.mjs";
|
|
6
|
+
import { D as makeTemporaryDirectory, O as removeTemporaryEntry, s as gitCommonDir, y as repoRoot } from "./worktree-C0c5Udwa.mjs";
|
|
7
|
+
import { a as findProjectRoot$1, l as projectShortcut, n as appProjectProblem, r as detectIsExpo, t as NO_PROJECT_REFUSAL } from "./project-CV3tDFHl.mjs";
|
|
8
|
+
import { m as writeWorkspaceLaunch, n as MODE_EXPO, t as MODE_BARE } from "./state-CaeaHbDA.mjs";
|
|
8
9
|
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 { 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-
|
|
11
|
-
import {
|
|
12
|
-
import { A as
|
|
13
|
-
import {
|
|
14
|
-
import { a as
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import { c as
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import { t as
|
|
26
|
-
import { i as
|
|
27
|
-
import {
|
|
28
|
-
import { c as
|
|
29
|
-
import {
|
|
30
|
-
import { n as
|
|
31
|
-
import {
|
|
32
|
-
import {
|
|
10
|
+
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-CliHHn7a.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-W5Pi1q38.mjs";
|
|
12
|
+
import { a as workspaceLinks, i as workspaceLinkLine } from "./stim-desktop-Cnz8CPnl.mjs";
|
|
13
|
+
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, dt as parkedMaxSetting, ht as configuredAndroidEmulatorApp, i as androidDataFreeBytes, j as listInstalledSystemImages, k as listAvdDeviceProfiles, q as resetAdoptedAvd, r as androidBuildToolName, t as DEFAULT_AVD_DEVICE_PROFILE, tt as waitForBoot, u as androidToolCwd } from "./android-Db2eEMaf.mjs";
|
|
14
|
+
import { l as resolveProjectMetro, o as pidExists, u as signalProcessTree } from "./metro-Di8EN2Tb.mjs";
|
|
15
|
+
import { A as remoteAndroidSetting, B as unknownSettingKeys, I as settingOriginScope, J as optimizationBuildProfile, K as artifactCachePolicy, L as settingShapeErrors, M as resolveCacheProviderConfig, N as resolveSettings, O as publicUrlSetting, P as settingFile, R as settingsLayers, S as metroPortSetting, U as detectProviders, Y as resolveOptimizations, a as SETTING_SHAPE_REMEDY, d as cacheProviderSettingError, q as compilerCacheFallbackMessage, r as REMOTE_DEVICE_BACKENDS, s as androidAvdConfigSettingError, u as androidDataPartitionSizeGbSettingError, w as metroWarmupUrlSetting, z as tunnelModeSetting } from "./settings-CduVcoxj.mjs";
|
|
16
|
+
import { i as teardownOwnedAvd } from "./teardown-BYuQafiA.mjs";
|
|
17
|
+
import { c as recordFinishedBuild, d as createRunRecorder, f as readRunEstimates, h as statsProjectKey$1, l as startBuildProgress, m as recordRunStats, n as NO_BUILD_PROGRESS, u as tapBuildLog } from "./build-progress-C2Jv1AtA.mjs";
|
|
18
|
+
import { r as releaseBuildSlot, t as acquireBuildSlot } from "./build-slots-Df1ApSuH.mjs";
|
|
19
|
+
import { n as verifyCollectorOwnership, r as readCollectors } from "./ownership-BkYxUtuj.mjs";
|
|
20
|
+
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-CAAplXAt.mjs";
|
|
21
|
+
import { t as checkDeviceCapacity } from "./device-capacity-DMuKC7nx.mjs";
|
|
22
|
+
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-CDQbDkL5.mjs";
|
|
23
|
+
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-DmeYjXD9.mjs";
|
|
24
|
+
import { t as cleanLine } from "./server-expo-DhtX6bom.mjs";
|
|
25
|
+
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-C2IS36Qf.mjs";
|
|
26
|
+
import { t as spawnEntry } from "./spawn-entry-DncpJvWL.mjs";
|
|
27
|
+
import { i as staleNativeDirRefusal, n as recordPrebuild, r as runPrebuild, t as planPrebuild } from "./prebuild-BvxDlD6C.mjs";
|
|
28
|
+
import { c as formatDiagnostic, i as buildAndroid, o as productFlavorRefusal, s as readProductFlavors } from "./gradle-CmdOBq1d.mjs";
|
|
29
|
+
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-CO2elm_b.mjs";
|
|
30
|
+
import { a as ensureMetroReachable, c as remoteAndroidDeps, n as binOnPath, o as ensureRemoteBootOwned, u as resolveRemoteContext, w as ownedSessionName } from "./device-remote-9ulPsdhY.mjs";
|
|
31
|
+
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-CLlofxRt.mjs";
|
|
32
|
+
import { d as withNativeBuildRun, r as cancelledFailure, u as runCancellation } from "./native-run-DFDoIRbY.mjs";
|
|
33
|
+
import { a as isEasBuildFailure, o as planEasDevelopmentBuild, s as resolveEasDevelopmentBuild } from "./simslim-Ciluxlq4.mjs";
|
|
34
|
+
import { c as startDevServer, m as detectAppIds, p as detectAndroidPackage } from "./start-CUD9nasA.mjs";
|
|
35
|
+
import { a as lostLine, c as releaseLeaseOnSignal, i as leaseExpiryText, l as runLease, n as DEBUG_VERIFY_STEP_MS, o as lostRefusal, r as acquireRunLease, s as parseDeviceWait, t as selectFromPool, u as waitFlagConflict } from "./device-pool-B2r38YjS.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-aeGMFX7e.mjs";
|
|
37
|
+
import { t as budgetGate } from "./budget-CMiqghig.mjs";
|
|
38
|
+
import { i as launchOutcomeRecord, n as ensureWorkspaceStorageSafely, r as isPhysicalDeviceRequest, t as ensureDevServer } from "./native-runtime-i6OkVg-w.mjs";
|
|
39
|
+
import { n as nativeRunCommand, r as siblingPlatformSlots, t as launchSlotScope } from "./slot-launch-BY0BrOLh.mjs";
|
|
40
|
+
import { a as printNativeCrashReport, r as captureNativeCrashes, t as errorDiagnostics } from "./error-diagnostics-Cb0P1WLS.mjs";
|
|
41
|
+
import { n as resolveAndroidCas, r as resolveAndroidCompilerCache } from "./android-cas-PnKfAdB2.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 } 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,16 @@ 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 = null;
|
|
426
|
+
const buildMachine = record.buildMachine ?? "auto";
|
|
427
|
+
const fallBack = (reason, line = reason) => {
|
|
428
|
+
if (namedBuildMachine(buildMachine)) throw new OffloadRefusal(buildMachine, reason);
|
|
429
|
+
record.offloadFallback = reason;
|
|
430
|
+
phase("build", `${line} -> building here`);
|
|
431
|
+
};
|
|
695
432
|
const { variant, release, profile: buildProfile, cas, cache: cachePolicy } = buildPlan;
|
|
696
433
|
const useBuildCache = cachePolicy.read;
|
|
697
434
|
let androidPackage = initialPackage;
|
|
@@ -749,6 +486,7 @@ async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo
|
|
|
749
486
|
let storeKey = "";
|
|
750
487
|
let storeSources = [];
|
|
751
488
|
let apkPath = null;
|
|
489
|
+
let swapFellBack = false;
|
|
752
490
|
async function resolveInitialFingerprint() {
|
|
753
491
|
if (easBuild?.ok) {
|
|
754
492
|
hash = storeHash = easBuild.fingerprint;
|
|
@@ -869,7 +607,10 @@ async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo
|
|
|
869
607
|
} else phase("cache", `remote miss (${remote.name}) ${remoteTimer()}`);
|
|
870
608
|
}
|
|
871
609
|
}
|
|
872
|
-
if (!easBuild)
|
|
610
|
+
if (!easBuild) {
|
|
611
|
+
await resolveRemoteArtifact();
|
|
612
|
+
if (!apkPath) miss(reasonForMiss([]).reason);
|
|
613
|
+
}
|
|
873
614
|
let waitedForBuild = null;
|
|
874
615
|
let releasedWait = null;
|
|
875
616
|
if (!easBuild && useBuildCache && !apkPath) {
|
|
@@ -887,6 +628,7 @@ async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo
|
|
|
887
628
|
step("wait");
|
|
888
629
|
phase("build", text);
|
|
889
630
|
},
|
|
631
|
+
waitingOn,
|
|
890
632
|
warn: (text) => phase("build", chalk.yellow(text)),
|
|
891
633
|
out
|
|
892
634
|
});
|
|
@@ -904,7 +646,6 @@ async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo
|
|
|
904
646
|
}
|
|
905
647
|
}
|
|
906
648
|
let swapDir = null;
|
|
907
|
-
let swapFellBack = false;
|
|
908
649
|
const installableCachedApk = async (key, cachedPath) => {
|
|
909
650
|
if (!release) return cachedPath;
|
|
910
651
|
phase("swap", `regenerating this workspace's JS for the cached ${variant} APK`);
|
|
@@ -938,51 +679,219 @@ async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo
|
|
|
938
679
|
}
|
|
939
680
|
}
|
|
940
681
|
}
|
|
941
|
-
function
|
|
942
|
-
if (swapFellBack)
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
682
|
+
function reasonForMiss(rekeyedBy) {
|
|
683
|
+
if (swapFellBack) return {
|
|
684
|
+
reason: skippedMissReason("the cached APK could not be reused, so this run built it fresh"),
|
|
685
|
+
diff: null
|
|
686
|
+
};
|
|
687
|
+
if (!useBuildCache) return {
|
|
688
|
+
reason: skippedMissReason(requestedBuildCache ? "cache reuse off in config" : "cache reuse turned off by --no-build-cache"),
|
|
689
|
+
diff: null
|
|
690
|
+
};
|
|
691
|
+
const current = {
|
|
692
|
+
hash: storeHash,
|
|
693
|
+
sources: storeSources
|
|
694
|
+
};
|
|
695
|
+
const explained = explainBuildMiss({
|
|
696
|
+
root,
|
|
697
|
+
platform: PLATFORM,
|
|
698
|
+
current,
|
|
699
|
+
rekeyedBy,
|
|
700
|
+
baselineDeps: { readState }
|
|
701
|
+
});
|
|
702
|
+
return {
|
|
703
|
+
reason: explained.reason,
|
|
704
|
+
diff: explained.previousHash && explained.changedNames.length ? fingerprintDiffRecord({
|
|
958
705
|
changed: explained.changedNames,
|
|
959
706
|
previousHash: explained.previousHash,
|
|
960
707
|
hash: current.hash
|
|
961
|
-
})
|
|
962
|
-
}
|
|
708
|
+
}) : null
|
|
709
|
+
};
|
|
710
|
+
}
|
|
711
|
+
function explainMiss(rekeyedBy) {
|
|
712
|
+
const explained = reasonForMiss(rekeyedBy);
|
|
713
|
+
record.missReason = explained.reason;
|
|
714
|
+
if (explained.diff) writer.write(explained.diff);
|
|
715
|
+
miss(record.missReason);
|
|
963
716
|
phase("cache", `miss: ${record.missReason.summary}`);
|
|
964
717
|
if (record.missReason.kind === "no-baseline") {
|
|
965
718
|
const line = untrackedMissLine(untracked({ projectRoot: root }));
|
|
966
719
|
if (line) phase("fingerprint", chalk.dim(line));
|
|
967
720
|
}
|
|
968
721
|
}
|
|
969
|
-
async function
|
|
970
|
-
if (!
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
722
|
+
async function takeBuildSlot() {
|
|
723
|
+
if (!maxBuilds) return true;
|
|
724
|
+
try {
|
|
725
|
+
buildSlot = await acquireSlot({
|
|
726
|
+
max: maxBuilds,
|
|
727
|
+
root,
|
|
728
|
+
logFile: buildLog,
|
|
729
|
+
out
|
|
730
|
+
});
|
|
731
|
+
} catch (err) {
|
|
732
|
+
const refusal = (0, ownership_claim_exports.claimFailure)(err, "stim android");
|
|
733
|
+
if (refusal) {
|
|
734
|
+
phaseFailure = fail$1(refusal.code, refusal.message, refusal.remedy, { lastBuildStatus: true });
|
|
735
|
+
return false;
|
|
736
|
+
}
|
|
737
|
+
phase("build", chalk.yellow(`could not take a build slot: ${err?.message || err}; building anyway`));
|
|
738
|
+
}
|
|
739
|
+
return true;
|
|
740
|
+
}
|
|
741
|
+
/** Whether this build should leave this Mac, before any machine is asked; null builds here. */
|
|
742
|
+
function placeBuild() {
|
|
743
|
+
const { mode, machines } = buildPlacementCandidates(buildMachine);
|
|
744
|
+
if (machines.length === 0 && !namedBuildMachine(buildMachine) && buildMachine !== "local") return null;
|
|
745
|
+
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;
|
|
746
|
+
const here = machineCapacity();
|
|
747
|
+
const placement = offloadPlacement({
|
|
748
|
+
mode,
|
|
749
|
+
machines: machines.length,
|
|
750
|
+
here,
|
|
751
|
+
unsupported,
|
|
752
|
+
selected: buildMachine
|
|
753
|
+
});
|
|
754
|
+
if (!placement.offload) {
|
|
755
|
+
hereReason = placement.reason;
|
|
756
|
+
if (mode !== "off") phase("build", `placement: here (${placement.reason})`);
|
|
757
|
+
return null;
|
|
758
|
+
}
|
|
759
|
+
return {
|
|
760
|
+
mode,
|
|
761
|
+
here,
|
|
762
|
+
reason: placement.reason,
|
|
763
|
+
machines
|
|
764
|
+
};
|
|
765
|
+
}
|
|
766
|
+
const openOffload = { choice: null };
|
|
767
|
+
/** Asks the paired machines once the post-mutation key is known; null builds here. */
|
|
768
|
+
async function chooseMachine(candidate) {
|
|
769
|
+
const choice = await chooseBuildMachine({
|
|
770
|
+
projectRoot: root,
|
|
771
|
+
target: {
|
|
772
|
+
platform: "android",
|
|
773
|
+
local: androidToolchain(),
|
|
774
|
+
requires: androidRequirements(root)
|
|
775
|
+
},
|
|
776
|
+
mode: candidate.mode,
|
|
777
|
+
here: candidate.here,
|
|
778
|
+
note: (line) => phase("build", chalk.dim(`offload: ${line}`)),
|
|
779
|
+
machines: candidate.machines,
|
|
780
|
+
selected: buildMachine
|
|
781
|
+
});
|
|
782
|
+
if (typeof choice === "string") {
|
|
783
|
+
const only = candidate.machines.length === 1 ? candidate.machines[0].machine : null;
|
|
784
|
+
if (only && choice.startsWith(`${only}: `)) fallbackMachine = only;
|
|
785
|
+
fallBack(choice);
|
|
786
|
+
return null;
|
|
787
|
+
}
|
|
788
|
+
openOffload.choice = choice;
|
|
789
|
+
phase("build", `placement: ${choice.machine} (${candidate.reason}${placementLoad(choice)})`);
|
|
790
|
+
return choice;
|
|
791
|
+
}
|
|
792
|
+
/** Builds on the chosen machine and stores the APK under the post-mutation key; false builds here instead unless a machine was named. */
|
|
793
|
+
async function compileElsewhere(choice, candidate) {
|
|
794
|
+
if (!storeKey || !storeHash) {
|
|
795
|
+
if (namedBuildMachine(buildMachine)) fallBack("the build fingerprint or cache key is unavailable");
|
|
796
|
+
return false;
|
|
797
|
+
}
|
|
798
|
+
const stagingDir = join(workspaceDir(root), "offload", PLATFORM);
|
|
799
|
+
const outcome = await offloadBuild({
|
|
800
|
+
choice,
|
|
801
|
+
expectedFingerprint: storeHash,
|
|
802
|
+
request: {
|
|
803
|
+
platform: "android",
|
|
804
|
+
isExpo,
|
|
805
|
+
android: {
|
|
806
|
+
variant,
|
|
807
|
+
abi: buildAbi,
|
|
808
|
+
gradleBuildCache: buildPlan.gradleBuildCache,
|
|
809
|
+
pch: buildPlan.pch,
|
|
810
|
+
compilerCache: buildPlan.compilerCache === "none" ? "none" : "ccache"
|
|
811
|
+
}
|
|
812
|
+
},
|
|
813
|
+
stagingDir,
|
|
814
|
+
onPhase: (name, msg) => phase(name, remotePhaseText(name, msg, choice.machine)),
|
|
815
|
+
onEnter: (name) => {
|
|
816
|
+
if (name === "compile" || name === "build" || name === "prebuild" || name === "pods") record.builtOn = choice.machine;
|
|
817
|
+
place({
|
|
818
|
+
host: choice.machine,
|
|
819
|
+
phase: name
|
|
820
|
+
});
|
|
821
|
+
step(name === "prebuild" ? name : "compile");
|
|
822
|
+
},
|
|
823
|
+
onRecord: (entry) => writer.write({
|
|
824
|
+
...entry,
|
|
825
|
+
offloadedTo: choice.machine
|
|
826
|
+
}),
|
|
827
|
+
note: (line) => phase("build", line)
|
|
828
|
+
});
|
|
829
|
+
place(null);
|
|
830
|
+
let stored = null;
|
|
831
|
+
let reason = outcome.ok ? null : outcome.reason;
|
|
832
|
+
if (outcome.ok) {
|
|
833
|
+
const settled = await refingerprintAfterMutation({
|
|
834
|
+
projectRoot: root,
|
|
835
|
+
platform: PLATFORM,
|
|
836
|
+
previousHash: storeHash,
|
|
837
|
+
fingerprint
|
|
838
|
+
});
|
|
839
|
+
if (!settled || settled.moved) reason = "the checkout here changed while it built";
|
|
840
|
+
else try {
|
|
841
|
+
stored = storeCached(PLATFORM, storeKey, outcome.artifactPath, {
|
|
842
|
+
sources: storeSources,
|
|
843
|
+
overwrite: !useBuildCache
|
|
977
844
|
});
|
|
978
845
|
} 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`));
|
|
846
|
+
reason = `could not store the APK: ${err?.message || err}`;
|
|
985
847
|
}
|
|
848
|
+
}
|
|
849
|
+
try {
|
|
850
|
+
rmSync(stagingDir, {
|
|
851
|
+
recursive: true,
|
|
852
|
+
force: true
|
|
853
|
+
});
|
|
854
|
+
} catch {}
|
|
855
|
+
step("compile");
|
|
856
|
+
if (!outcome.ok || !stored) {
|
|
857
|
+
const why = reason ?? "the APK was not stored";
|
|
858
|
+
fallbackMachine = choice.machine;
|
|
859
|
+
fallBack(`${choice.machine}: ${why}`, `offload failed: ${why}`);
|
|
860
|
+
writer.write({
|
|
861
|
+
src: "build",
|
|
862
|
+
level: "warn",
|
|
863
|
+
event: "offload_failed",
|
|
864
|
+
msg: reason,
|
|
865
|
+
machine: choice.machine
|
|
866
|
+
});
|
|
867
|
+
return false;
|
|
868
|
+
}
|
|
869
|
+
const { timings } = outcome;
|
|
870
|
+
apkPath = stored;
|
|
871
|
+
record.builtOn = outcome.machine;
|
|
872
|
+
record.offloadedTo = outcome.machine;
|
|
873
|
+
stats.setPlacement({
|
|
874
|
+
decision: "offloaded",
|
|
875
|
+
machine: outcome.machine,
|
|
876
|
+
reason: `${candidate.reason}${placementLoad(choice)}`,
|
|
877
|
+
buildMs: timings.totalMs
|
|
878
|
+
});
|
|
879
|
+
ccacheActivity = outcome.ccache;
|
|
880
|
+
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)}`);
|
|
881
|
+
phase("cache", `compilation cache on ${outcome.machine} ${ccacheActivityLine(ccacheActivity)}`);
|
|
882
|
+
writer.write({
|
|
883
|
+
src: "build",
|
|
884
|
+
level: "info",
|
|
885
|
+
event: "offload_done",
|
|
886
|
+
msg: `built on ${outcome.machine}`,
|
|
887
|
+
timings
|
|
888
|
+
});
|
|
889
|
+
return true;
|
|
890
|
+
}
|
|
891
|
+
async function buildArtifact() {
|
|
892
|
+
if (!apkPath) try {
|
|
893
|
+
const offload = placeBuild();
|
|
894
|
+
if (!offload && !await takeBuildSlot()) return false;
|
|
986
895
|
const rekeyedBy = [];
|
|
987
896
|
let editedConfig = [];
|
|
988
897
|
const prebuildPlan = planPrebuildFor(root, PLATFORM, {
|
|
@@ -997,6 +906,7 @@ async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo
|
|
|
997
906
|
return false;
|
|
998
907
|
}
|
|
999
908
|
if (prebuildRan) {
|
|
909
|
+
miss(reasonForMiss([]).reason, true);
|
|
1000
910
|
step("prebuild");
|
|
1001
911
|
recordPrebuild(root, PLATFORM, null);
|
|
1002
912
|
const pre = await prebuild(root, PLATFORM, writer, {
|
|
@@ -1036,6 +946,7 @@ async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo
|
|
|
1036
946
|
if (prepared) {
|
|
1037
947
|
apkPath = prepared;
|
|
1038
948
|
record.cacheHit = "local";
|
|
949
|
+
lateHit();
|
|
1039
950
|
phase("cache", `hit ${shortHash(storeHash)} (post-prebuild key)`);
|
|
1040
951
|
if (releasedWait) {
|
|
1041
952
|
waitedForBuild = releasedWait.facts;
|
|
@@ -1045,9 +956,32 @@ async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo
|
|
|
1045
956
|
}
|
|
1046
957
|
}
|
|
1047
958
|
}
|
|
1048
|
-
if (!apkPath) {
|
|
959
|
+
if (offload && !apkPath) {
|
|
1049
960
|
explainMiss(rekeyedBy);
|
|
1050
961
|
step("compile");
|
|
962
|
+
let built = false;
|
|
963
|
+
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");
|
|
964
|
+
else {
|
|
965
|
+
const choice = await chooseMachine(offload);
|
|
966
|
+
if (choice) built = await compileElsewhere(choice, offload);
|
|
967
|
+
}
|
|
968
|
+
if (!built && !await takeBuildSlot()) return false;
|
|
969
|
+
}
|
|
970
|
+
if (!apkPath) {
|
|
971
|
+
if (!offload) {
|
|
972
|
+
explainMiss(rekeyedBy);
|
|
973
|
+
step("compile");
|
|
974
|
+
}
|
|
975
|
+
if (offload) stats.setPlacement({
|
|
976
|
+
decision: "fell-back",
|
|
977
|
+
reason: record.offloadFallback ?? "offload failed",
|
|
978
|
+
...fallbackMachine ? { machine: fallbackMachine } : {}
|
|
979
|
+
});
|
|
980
|
+
else if (hereReason) stats.setPlacement({
|
|
981
|
+
decision: "here",
|
|
982
|
+
reason: hereReason
|
|
983
|
+
});
|
|
984
|
+
record.builtOn = "here";
|
|
1051
985
|
phase("build", `compiling ${variant || "debug"} with Gradle`);
|
|
1052
986
|
const built = await build({
|
|
1053
987
|
root,
|
|
@@ -1155,7 +1089,13 @@ async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo
|
|
|
1155
1089
|
});
|
|
1156
1090
|
}
|
|
1157
1091
|
}
|
|
1092
|
+
} catch (error) {
|
|
1093
|
+
if (!(error instanceof OffloadRefusal)) throw error;
|
|
1094
|
+
const refusal = error;
|
|
1095
|
+
phaseFailure = fail$1(refusal.code, refusal.message, refusal.remedy, { lastBuildStatus: true });
|
|
1096
|
+
return false;
|
|
1158
1097
|
} finally {
|
|
1098
|
+
if (openOffload.choice) closeOffload(openOffload.choice);
|
|
1159
1099
|
releaseHeldLock();
|
|
1160
1100
|
releaseHeldSlot();
|
|
1161
1101
|
}
|
|
@@ -1163,6 +1103,8 @@ async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo
|
|
|
1163
1103
|
}
|
|
1164
1104
|
if (!easBuild) {
|
|
1165
1105
|
await prepareCachedArtifact();
|
|
1106
|
+
if (apkPath) lateHit();
|
|
1107
|
+
else if (swapFellBack) miss(reasonForMiss([]).reason);
|
|
1166
1108
|
if (!await buildArtifact()) return refused();
|
|
1167
1109
|
}
|
|
1168
1110
|
return {
|
|
@@ -1186,7 +1128,7 @@ function tail(lines, n = FALLBACK_LINES) {
|
|
|
1186
1128
|
}
|
|
1187
1129
|
//#endregion
|
|
1188
1130
|
//#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 }) {
|
|
1131
|
+
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, buildMachine = "auto", builtOn, offloadedTo = null, offloadFallback = null }) {
|
|
1190
1132
|
return {
|
|
1191
1133
|
platform: PLATFORM,
|
|
1192
1134
|
...slot && slot !== "default" ? { slot } : {},
|
|
@@ -1201,6 +1143,10 @@ function androidFacts({ slot, serial, avdName = null, deviceName = null, systemI
|
|
|
1201
1143
|
metroPort: metroPort ?? null,
|
|
1202
1144
|
cacheHit: cacheLevel(cacheHit),
|
|
1203
1145
|
cacheSkipped: Boolean(cacheSkipped),
|
|
1146
|
+
buildMachine,
|
|
1147
|
+
...builtOn ? { builtOn } : {},
|
|
1148
|
+
...offloadedTo ? { offloadedTo } : {},
|
|
1149
|
+
...offloadFallback ? { offloadFallback } : {},
|
|
1204
1150
|
waitedForBuild: waitedForBuild ? {
|
|
1205
1151
|
pid: waitedForBuild.pid ?? null,
|
|
1206
1152
|
ms: waitedForBuild.ms ?? 0
|
|
@@ -1219,7 +1165,7 @@ function androidFacts({ slot, serial, avdName = null, deviceName = null, systemI
|
|
|
1219
1165
|
...devServer ? { devServer } : {}
|
|
1220
1166
|
};
|
|
1221
1167
|
}
|
|
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 }) {
|
|
1168
|
+
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
1169
|
const record = {
|
|
1224
1170
|
platform: PLATFORM,
|
|
1225
1171
|
avdName: avdName ?? null,
|
|
@@ -1236,6 +1182,10 @@ function lastBuildRecord({ fingerprint, cacheKey, cacheHit, cacheSkipped = false
|
|
|
1236
1182
|
configuration: configuration ?? null
|
|
1237
1183
|
};
|
|
1238
1184
|
if (errorCode) record.errorCode = errorCode;
|
|
1185
|
+
record.buildMachine = buildMachine;
|
|
1186
|
+
if (builtOn) record.builtOn = builtOn;
|
|
1187
|
+
if (offloadedTo) record.offloadedTo = offloadedTo;
|
|
1188
|
+
if (offloadFallback) record.offloadFallback = offloadFallback;
|
|
1239
1189
|
if (missReason && !cacheLevel(cacheHit)) record.missReason = missReason;
|
|
1240
1190
|
const recorded = status === "failed" ? buildDiagnostics(diagnostics) : [];
|
|
1241
1191
|
if (recorded.length) record.diagnostics = recorded;
|
|
@@ -1258,12 +1208,14 @@ async function finishAndroidUpload(uploadPending, remote, phase) {
|
|
|
1258
1208
|
}
|
|
1259
1209
|
return false;
|
|
1260
1210
|
}
|
|
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 }) {
|
|
1211
|
+
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, links }) {
|
|
1212
|
+
const offloadedTo = record.offloadedTo ?? null;
|
|
1262
1213
|
recordRun({
|
|
1263
1214
|
failed: false,
|
|
1264
1215
|
cacheHit: cacheLevel(record.cacheHit),
|
|
1265
1216
|
waited: waitedForBuild,
|
|
1266
|
-
durationMs
|
|
1217
|
+
durationMs,
|
|
1218
|
+
offloadedTo
|
|
1267
1219
|
});
|
|
1268
1220
|
const facts = androidFacts({
|
|
1269
1221
|
slot,
|
|
@@ -1290,18 +1242,23 @@ function reportAndroidResult({ slot, json, useBuildCache, variant, release, metr
|
|
|
1290
1242
|
logs: logsDir,
|
|
1291
1243
|
durationMs,
|
|
1292
1244
|
lease,
|
|
1293
|
-
devServer
|
|
1245
|
+
devServer,
|
|
1246
|
+
buildMachine: record.buildMachine,
|
|
1247
|
+
builtOn: record.builtOn ?? void 0,
|
|
1248
|
+
offloadedTo,
|
|
1249
|
+
offloadFallback: record.offloadFallback ?? null
|
|
1294
1250
|
});
|
|
1295
1251
|
writer.close();
|
|
1296
|
-
if (json) emit(JSON.stringify(
|
|
1252
|
+
if (json) emit(JSON.stringify({
|
|
1297
1253
|
...facts,
|
|
1298
|
-
|
|
1299
|
-
|
|
1254
|
+
...links ? { links } : {},
|
|
1255
|
+
...reclaimed.length ? { reclaimed } : {}
|
|
1256
|
+
}));
|
|
1300
1257
|
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)})`;
|
|
1258
|
+
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
1259
|
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
1260
|
const deviceName = record.avdName || record.deviceName || serial;
|
|
1304
|
-
const cacheResult = useBuildCache ? cacheOutcome(record.cacheHit, remote?.name ?? providerName) :
|
|
1261
|
+
const cacheResult = useBuildCache ? cacheOutcome(record.cacheHit, remote?.name ?? providerName, offloadedTo) : `bypassed; ${cacheOutcome(false, null, offloadedTo)}`;
|
|
1305
1262
|
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
1263
|
emit([
|
|
1307
1264
|
outcome,
|
|
@@ -1312,13 +1269,14 @@ function reportAndroidResult({ slot, json, useBuildCache, variant, release, metr
|
|
|
1312
1269
|
...ccache.status === "not-run" ? [phaseLine("compilation cache", ccacheActivityLine(ccache))] : [],
|
|
1313
1270
|
phaseLine("logs", logsDir || "unavailable (remote device)")
|
|
1314
1271
|
].join("\n"));
|
|
1272
|
+
if (links) console.error(chalk.dim(workspaceLinkLine(links)));
|
|
1315
1273
|
}
|
|
1316
1274
|
return facts;
|
|
1317
1275
|
}
|
|
1318
|
-
function cacheOutcome(cacheHit, providerName = null) {
|
|
1276
|
+
function cacheOutcome(cacheHit, providerName = null, offloadedTo = null) {
|
|
1319
1277
|
if (cacheHit === "remote") return `cache hit from ${providerName || "the remote cache"}`;
|
|
1320
1278
|
if (cacheHit === "local") return "cache hit";
|
|
1321
|
-
return "built";
|
|
1279
|
+
return offloadedTo ? `built on ${offloadedTo}` : "built";
|
|
1322
1280
|
}
|
|
1323
1281
|
function persistLastBuild({ recordBuild = recordFinishedBuild, root, record, startedAt, durationMs, status, errorCode = null, diagnostics = null, out }) {
|
|
1324
1282
|
const lastBuild = lastBuildRecord({
|
|
@@ -1455,13 +1413,15 @@ async function verifyAndroidRun({ root, slot, release, remoteRelease, remoteDevi
|
|
|
1455
1413
|
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
1414
|
return { state: LAUNCH_FATAL };
|
|
1457
1415
|
}
|
|
1458
|
-
const
|
|
1459
|
-
const timeoutMs =
|
|
1460
|
-
if (metroCheck &&
|
|
1416
|
+
const freshEmulator = Boolean((device.created || device.adoptionPending) && device.owned && !remoteDevice);
|
|
1417
|
+
const timeoutMs = freshEmulator ? 6e4 : VERIFY_TIMEOUT_MS;
|
|
1418
|
+
if (metroCheck && freshEmulator) phase("verify", `waiting up to 60s for bundle load (${device.created ? "new" : "adopted"} emulator)`);
|
|
1419
|
+
const siblings = metroCheck ? siblingPlatformSlots(root, "android", slot) : [];
|
|
1461
1420
|
const verification = metroCheck ? await verifyLaunched({
|
|
1462
1421
|
timeoutMs,
|
|
1463
1422
|
requireBundleResponse: true,
|
|
1464
1423
|
slot: launchSlotScope(root, slot),
|
|
1424
|
+
platformShared: siblings.length > 0,
|
|
1465
1425
|
onReadinessPending: () => phase("readiness", "waiting for app readiness (up to 30s after bundle load)"),
|
|
1466
1426
|
logsDir,
|
|
1467
1427
|
since: launchedAt,
|
|
@@ -1493,7 +1453,7 @@ async function verifyAndroidRun({ root, slot, release, remoteRelease, remoteDevi
|
|
|
1493
1453
|
phase("verify", chalk.red(`FATAL after ${formatDuration(verification.waitedMs ?? 0)}: ${reason}`));
|
|
1494
1454
|
for (const line of launchErrorPreview(verification.errors ?? [], root)) phase("", chalk.red(line));
|
|
1495
1455
|
if (verification.processAlive === false && !verification.errors?.some((record) => record.event === "native_crash")) phase("logs", "No attributable native crash report captured yet. Run `stim logs --errors` again for delayed reports, or `stim logs --source device` for available output.");
|
|
1496
|
-
if (nativeFatal || verification.processAlive === false) phase("remedy", chalk.yellow(`Fix the crash, then
|
|
1456
|
+
if (nativeFatal || verification.processAlive === false) phase("remedy", chalk.yellow(`Fix the crash, then run \`${runCommand}\` again; it force-stops a crashed process that remains alive behind the system crash dialog. Metro reload cannot recover it.`));
|
|
1497
1457
|
else if (verification.processAlive === true && metroPort !== null) {
|
|
1498
1458
|
const reloadRemedy = physical ? `run \`agent-device metro reload --metro-port ${metroPort}\`.` : "run `stim reload android`.";
|
|
1499
1459
|
phase("remedy", chalk.yellow(`The native app is still running. ${deliveryFailed ? "Check the Metro logs and device connection, then" : "Fix the JavaScript or TypeScript error, then"} ${reloadRemedy} Do not run \`${runCommand}\` unless native inputs changed or the app process exits.`));
|
|
@@ -1523,6 +1483,20 @@ async function verifyAndroidRun({ root, slot, release, remoteRelease, remoteDevi
|
|
|
1523
1483
|
phase("", chalk.dim("Nothing to do: `stim logs --source metro` shows the build finishing, usually within a minute."));
|
|
1524
1484
|
return { state: LAUNCH_BUNDLING };
|
|
1525
1485
|
}
|
|
1486
|
+
if (verification?.unattributed) {
|
|
1487
|
+
const [headline, ...lines] = unattributedLaunchLines({
|
|
1488
|
+
platform: "android",
|
|
1489
|
+
metroPort,
|
|
1490
|
+
slot,
|
|
1491
|
+
siblings
|
|
1492
|
+
});
|
|
1493
|
+
phase("verify", chalk.yellow(headline));
|
|
1494
|
+
for (const line of lines) phase("", chalk.yellow(line));
|
|
1495
|
+
return {
|
|
1496
|
+
state: LAUNCH_UNVERIFIED,
|
|
1497
|
+
unattributed: true
|
|
1498
|
+
};
|
|
1499
|
+
}
|
|
1526
1500
|
phase("verify", chalk.yellow("UNVERIFIED: no bundle request reached this workspace's Metro"));
|
|
1527
1501
|
for (const line of unverifiedLaunchLines({
|
|
1528
1502
|
platform: PLATFORM,
|
|
@@ -1537,6 +1511,60 @@ async function verifyAndroidRun({ root, slot, release, remoteRelease, remoteDevi
|
|
|
1537
1511
|
})) phase("", chalk.yellow(line));
|
|
1538
1512
|
return { state: LAUNCH_UNVERIFIED };
|
|
1539
1513
|
}
|
|
1514
|
+
const ADOPTED_INSTALL_HEADROOM_BYTES = 536870912;
|
|
1515
|
+
const INSUFFICIENT_STORAGE = /INSTALL_FAILED_INSUFFICIENT_STORAGE/;
|
|
1516
|
+
async function wipeAdoptedAvd(room, free) {
|
|
1517
|
+
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`));
|
|
1518
|
+
const expectedRecord = deviceSlotPlatforms(loadConfig$1()?.projects?.[room.root], room.slot)?.android;
|
|
1519
|
+
if (!expectedRecord?.adoptionPending || expectedRecord.avdName !== room.avdName) throw new Error("the emulator assignment changed before the wipe; its data was kept");
|
|
1520
|
+
const wiped = room.wipeDevice(room.avdName, {
|
|
1521
|
+
wipe: true,
|
|
1522
|
+
owner: {
|
|
1523
|
+
projectPath: room.root,
|
|
1524
|
+
slot: room.slot,
|
|
1525
|
+
expectedRecord
|
|
1526
|
+
},
|
|
1527
|
+
workspace: room.root
|
|
1528
|
+
});
|
|
1529
|
+
if (wiped.status !== "torn-down") throw new Error(`could not wipe it: ${wiped.reason ?? wiped.status}`);
|
|
1530
|
+
const rebooted = await room.rebootDevice();
|
|
1531
|
+
if (!rebooted.ok || !rebooted.serial) throw new Error(`it did not boot after its user data was wiped: ${rebooted.reason ?? "no serial"}`);
|
|
1532
|
+
if (room.resolveAvdSerial(room.avdName).serial !== rebooted.serial) throw new Error(`${rebooted.serial} is not running ${room.avdName} after the wipe`);
|
|
1533
|
+
room.phase("device", `${room.avdName} (${rebooted.serial}) booted with wiped user data`);
|
|
1534
|
+
return rebooted.serial;
|
|
1535
|
+
}
|
|
1536
|
+
async function installOnAdoptedAvd(room, serial, installApk) {
|
|
1537
|
+
const neededBytes = 2 * statSync(room.apkPath).size + ADOPTED_INSTALL_HEADROOM_BYTES;
|
|
1538
|
+
const roomy = (free) => free === null || free >= neededBytes;
|
|
1539
|
+
const outOfSpace = (result) => Boolean(result.failed) && INSUFFICIENT_STORAGE.test(result.reason ?? "");
|
|
1540
|
+
let target = serial;
|
|
1541
|
+
let wiped = false;
|
|
1542
|
+
if (!roomy(room.dataFreeBytes(target))) {
|
|
1543
|
+
room.trimCaches(target, neededBytes);
|
|
1544
|
+
const free = room.dataFreeBytes(target);
|
|
1545
|
+
if (roomy(free)) room.phase("install", `trimmed app caches on ${room.avdName} to make room for the APK`);
|
|
1546
|
+
else {
|
|
1547
|
+
target = await wipeAdoptedAvd(room, free);
|
|
1548
|
+
wiped = true;
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
let installed = installApk(target);
|
|
1552
|
+
if (wiped || !outOfSpace(installed)) return {
|
|
1553
|
+
serial: target,
|
|
1554
|
+
installed
|
|
1555
|
+
};
|
|
1556
|
+
room.trimCaches(target, neededBytes);
|
|
1557
|
+
installed = installApk(target);
|
|
1558
|
+
if (!outOfSpace(installed)) return {
|
|
1559
|
+
serial: target,
|
|
1560
|
+
installed
|
|
1561
|
+
};
|
|
1562
|
+
target = await wipeAdoptedAvd(room, room.dataFreeBytes(target));
|
|
1563
|
+
return {
|
|
1564
|
+
serial: target,
|
|
1565
|
+
installed: installApk(target)
|
|
1566
|
+
};
|
|
1567
|
+
}
|
|
1540
1568
|
async function resolveInstallSerial({ root, slot, device, physical, remoteDevice, serial, resolveAvdSerial, waitForDeviceBoot, phase }) {
|
|
1541
1569
|
if (physical || remoteDevice || !device.owned || !device.avdName) return serial;
|
|
1542
1570
|
const resolved = resolveAvdSerial(device.avdName, { timeoutMs: 5e3 });
|
|
@@ -1559,7 +1587,7 @@ async function resolveInstallSerial({ root, slot, device, physical, remoteDevice
|
|
|
1559
1587
|
});
|
|
1560
1588
|
return resolved.serial;
|
|
1561
1589
|
}
|
|
1562
|
-
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 }) {
|
|
1590
|
+
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 }) {
|
|
1563
1591
|
let androidPackage = initialPackage;
|
|
1564
1592
|
let leaseWarned = false;
|
|
1565
1593
|
const raiseLeaseFor = (boundMs, beforeInstall) => {
|
|
@@ -1575,7 +1603,7 @@ async function finishAndroidRun({ lease, releaseLease, root, slot, json, metroCh
|
|
|
1575
1603
|
}
|
|
1576
1604
|
return null;
|
|
1577
1605
|
};
|
|
1578
|
-
enterPhase("
|
|
1606
|
+
enterPhase("device");
|
|
1579
1607
|
const booted = await bootPromise;
|
|
1580
1608
|
const runCommand = nativeRunCommand("android", slot, {
|
|
1581
1609
|
physical,
|
|
@@ -1619,13 +1647,37 @@ async function finishAndroidRun({ lease, releaseLease, root, slot, json, metroCh
|
|
|
1619
1647
|
}
|
|
1620
1648
|
const lostBeforeInstall = physical ? raiseLeaseFor(ADB_INSTALL_TIMEOUT_MS, true) : null;
|
|
1621
1649
|
if (lostBeforeInstall) return lostBeforeInstall;
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1650
|
+
enterPhase("install");
|
|
1651
|
+
let installTimer = stepTimer(now);
|
|
1652
|
+
const installApk = (target) => {
|
|
1653
|
+
installTimer = stepTimer(now);
|
|
1654
|
+
return install({
|
|
1655
|
+
serial: target,
|
|
1656
|
+
apkPath,
|
|
1657
|
+
packageName: androidPackage,
|
|
1658
|
+
allowUninstall: release || adopting
|
|
1659
|
+
});
|
|
1660
|
+
};
|
|
1661
|
+
let installed;
|
|
1662
|
+
if (adopting && device.avdName && apkPath) {
|
|
1663
|
+
const room = {
|
|
1664
|
+
root,
|
|
1665
|
+
slot,
|
|
1666
|
+
avdName: device.avdName,
|
|
1667
|
+
apkPath,
|
|
1668
|
+
phase,
|
|
1669
|
+
rebootDevice,
|
|
1670
|
+
dataFreeBytes,
|
|
1671
|
+
trimCaches,
|
|
1672
|
+
wipeDevice,
|
|
1673
|
+
resolveAvdSerial
|
|
1674
|
+
};
|
|
1675
|
+
try {
|
|
1676
|
+
({serial, installed} = await installOnAdoptedAvd(room, serial, installApk));
|
|
1677
|
+
} catch (error) {
|
|
1678
|
+
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.`);
|
|
1679
|
+
}
|
|
1680
|
+
} else installed = installApk(serial);
|
|
1629
1681
|
if (installed.failed) {
|
|
1630
1682
|
const installRemedy = remoteDevice?.failureRemedy() ?? localInstallRemedy({
|
|
1631
1683
|
reason: installed.reason,
|
|
@@ -1713,9 +1765,9 @@ async function finishAndroidRun({ lease, releaseLease, root, slot, json, metroCh
|
|
|
1713
1765
|
src: "build",
|
|
1714
1766
|
level: "info",
|
|
1715
1767
|
event: "app_launched",
|
|
1716
|
-
msg: release ? `launched ${androidPackage} on ${serial} (${variant}, embedded JS bundle, no Metro)` : `launched ${androidPackage} on ${serial} against Metro port ${metroPort}`
|
|
1768
|
+
msg: (release ? `launched ${androidPackage} on ${serial} (${variant}, embedded JS bundle, no Metro)` : `launched ${androidPackage} on ${serial} against Metro port ${metroPort}`) + restartedAppNote(launched.restartedPid, "; ")
|
|
1717
1769
|
});
|
|
1718
|
-
phase("launch", `${androidPackage} ${launchTimer()}`);
|
|
1770
|
+
phase("launch", `${androidPackage}${restartedAppNote(launched.restartedPid)} ${launchTimer()}`);
|
|
1719
1771
|
if (!physical && !remoteDevice) try {
|
|
1720
1772
|
writeLaunch(root, "android", {
|
|
1721
1773
|
appId: androidPackage,
|
|
@@ -1788,7 +1840,7 @@ async function finishAndroidRun({ lease, releaseLease, root, slot, json, metroCh
|
|
|
1788
1840
|
phase("logs", `${displayPath(root, logsDir)}${collectorPid ? ` (collector pid ${collectorPid})` : ""}`);
|
|
1789
1841
|
}
|
|
1790
1842
|
if (physical) raiseLeaseFor(release ? RELEASE_VERIFY_WAIT_MS : DEBUG_VERIFY_STEP_MS, false);
|
|
1791
|
-
const { state: launchState, warning: launchWarning } = await verifyAndroidRun({
|
|
1843
|
+
const { state: launchState, warning: launchWarning, unattributed } = await verifyAndroidRun({
|
|
1792
1844
|
root,
|
|
1793
1845
|
slot,
|
|
1794
1846
|
release,
|
|
@@ -1819,7 +1871,8 @@ async function finishAndroidRun({ lease, releaseLease, root, slot, json, metroCh
|
|
|
1819
1871
|
release,
|
|
1820
1872
|
bundleId: androidPackage,
|
|
1821
1873
|
configuration: variant,
|
|
1822
|
-
metroPort
|
|
1874
|
+
metroPort,
|
|
1875
|
+
unattributed
|
|
1823
1876
|
}));
|
|
1824
1877
|
const leaseFacts = lease?.facts() ?? null;
|
|
1825
1878
|
releaseLease();
|
|
@@ -1850,7 +1903,11 @@ async function finishAndroidRun({ lease, releaseLease, root, slot, json, metroCh
|
|
|
1850
1903
|
lease: physical ? leaseFacts : void 0,
|
|
1851
1904
|
recordRun,
|
|
1852
1905
|
reclaimed,
|
|
1853
|
-
devServer
|
|
1906
|
+
devServer,
|
|
1907
|
+
links: workspaceLinks(root, {
|
|
1908
|
+
platform: "android",
|
|
1909
|
+
slot
|
|
1910
|
+
})
|
|
1854
1911
|
});
|
|
1855
1912
|
if (remoteWasAbandoned || uploadWasAbandoned) exitAfterFlush(0);
|
|
1856
1913
|
return {
|
|
@@ -1987,10 +2044,12 @@ function resolveAndroidRunPlan({ settings, settingsContext, slot, easProfile, va
|
|
|
1987
2044
|
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.");
|
|
1988
2045
|
const waitSeconds = waitParsed.seconds;
|
|
1989
2046
|
const remoteBackend = physical ? null : commandRemoteBackend ?? remoteAndroidSetting(settings);
|
|
2047
|
+
const settingsLayersForOrigin = settingsLayers(settingsContext);
|
|
1990
2048
|
const imageRefusal = systemImageRefusal({
|
|
1991
2049
|
slot,
|
|
1992
2050
|
flag: systemImageFlag,
|
|
1993
2051
|
resolved: systemImage,
|
|
2052
|
+
origin: settingOriginScope(settingsLayersForOrigin, "android.systemImage"),
|
|
1994
2053
|
physical,
|
|
1995
2054
|
remoteBackend,
|
|
1996
2055
|
listImages: listSystemImages
|
|
@@ -1999,11 +2058,18 @@ function resolveAndroidRunPlan({ settings, settingsContext, slot, easProfile, va
|
|
|
1999
2058
|
const profileRefusal = deviceProfileRefusal({
|
|
2000
2059
|
flag: deviceProfileFlag,
|
|
2001
2060
|
resolved: deviceProfile,
|
|
2061
|
+
origin: settingOriginScope(settingsLayersForOrigin, "android.deviceProfile"),
|
|
2002
2062
|
physical,
|
|
2003
2063
|
remoteBackend,
|
|
2004
2064
|
listProfiles: listDeviceProfiles
|
|
2005
2065
|
});
|
|
2006
2066
|
if (profileRefusal) return fail(profileRefusal.code, profileRefusal.message, profileRefusal.remedy);
|
|
2067
|
+
const avdFlagRefusal = remoteAvdFlagRefusal({
|
|
2068
|
+
systemImageFlag,
|
|
2069
|
+
deviceProfileFlag,
|
|
2070
|
+
remoteBackend
|
|
2071
|
+
});
|
|
2072
|
+
if (avdFlagRefusal) return fail(avdFlagRefusal.code, avdFlagRefusal.message, avdFlagRefusal.remedy);
|
|
2007
2073
|
if (!physical && !remoteBackend) {
|
|
2008
2074
|
const flagImage = typeof systemImageFlag === "string" && systemImageFlag.trim() ? systemImageFlag.trim() : null;
|
|
2009
2075
|
const existing = ownedAvd(root, slot);
|
|
@@ -2060,7 +2126,7 @@ function resolveAndroidRunPlan({ settings, settingsContext, slot, easProfile, va
|
|
|
2060
2126
|
//#endregion
|
|
2061
2127
|
//#region src/commands/android/next-build.ts
|
|
2062
2128
|
const DEFAULT_PLAN_DEPS = {
|
|
2063
|
-
findRoot: findProjectRoot,
|
|
2129
|
+
findRoot: findProjectRoot$1,
|
|
2064
2130
|
fingerprint: fingerprintProject,
|
|
2065
2131
|
listSystemImages: listInstalledSystemImages,
|
|
2066
2132
|
avdSystemImage: ownedAvdSystemImage,
|
|
@@ -2163,7 +2229,7 @@ async function planAndroid(opts, overrides = {}) {
|
|
|
2163
2229
|
platform: PLATFORM,
|
|
2164
2230
|
slot,
|
|
2165
2231
|
root,
|
|
2166
|
-
projectKey: statsProjectKey({
|
|
2232
|
+
projectKey: statsProjectKey$1({
|
|
2167
2233
|
root,
|
|
2168
2234
|
commonDir: settingsContext.gitCommonDir,
|
|
2169
2235
|
repoRoot: settingsContext.repoRoot
|
|
@@ -2250,12 +2316,12 @@ function androidCommand(program) {
|
|
|
2250
2316
|
registerAndroid(program);
|
|
2251
2317
|
}
|
|
2252
2318
|
function registerAndroid(program) {
|
|
2253
|
-
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) => {
|
|
2319
|
+
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) => {
|
|
2254
2320
|
if (REMOTE_DEVICE_BACKENDS.includes(value)) return value;
|
|
2255
2321
|
throw new InvalidArgumentError(`expected one of: ${REMOTE_DEVICE_BACKENDS.join(", ")}`);
|
|
2256
2322
|
}).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) => {
|
|
2257
2323
|
if (opts.plan) return planAndroid(opts);
|
|
2258
|
-
const root = findProjectRoot(process.cwd());
|
|
2324
|
+
const root = findProjectRoot$1(process.cwd());
|
|
2259
2325
|
if (!root) {
|
|
2260
2326
|
refuseNoProject({ json: Boolean(opts.json) });
|
|
2261
2327
|
return;
|
|
@@ -2276,6 +2342,7 @@ function registerAndroid(program) {
|
|
|
2276
2342
|
try {
|
|
2277
2343
|
return await runAndroid({
|
|
2278
2344
|
root,
|
|
2345
|
+
buildMachine: opts.buildMachine,
|
|
2279
2346
|
slot: opts.slot,
|
|
2280
2347
|
easProfile: opts.easProfile,
|
|
2281
2348
|
json: Boolean(opts.json),
|
|
@@ -2296,7 +2363,7 @@ function registerAndroid(program) {
|
|
|
2296
2363
|
}, { write: (line) => console.error(chalk.dim(phaseLine("lock", line))) })).ok) process.exit(runCancellation() ? 130 : 1);
|
|
2297
2364
|
});
|
|
2298
2365
|
}
|
|
2299
|
-
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) } = {}) {
|
|
2366
|
+
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) } = {}) {
|
|
2300
2367
|
return {
|
|
2301
2368
|
root,
|
|
2302
2369
|
json,
|
|
@@ -2337,6 +2404,9 @@ function resolveRunAndroidOptions({ root, json = false, remoteDevice: commandRem
|
|
|
2337
2404
|
ensureDeviceBooted,
|
|
2338
2405
|
resolveAvdSerial,
|
|
2339
2406
|
waitForDeviceBoot,
|
|
2407
|
+
dataFreeBytes,
|
|
2408
|
+
trimCaches,
|
|
2409
|
+
wipeDevice,
|
|
2340
2410
|
resolveMetro,
|
|
2341
2411
|
prewarmMetro,
|
|
2342
2412
|
startServer,
|
|
@@ -2436,7 +2506,7 @@ function avdSetupFailure(error, root, logFile) {
|
|
|
2436
2506
|
};
|
|
2437
2507
|
}
|
|
2438
2508
|
async function runAndroid(options = {}) {
|
|
2439
|
-
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);
|
|
2509
|
+
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);
|
|
2440
2510
|
const progress = options.progress ?? NO_BUILD_PROGRESS;
|
|
2441
2511
|
const slot = validateDeviceSlot(options.slot);
|
|
2442
2512
|
const started = now();
|
|
@@ -2484,10 +2554,13 @@ async function runAndroid(options = {}) {
|
|
|
2484
2554
|
}
|
|
2485
2555
|
const logsDir = workspaceLogsDir(root);
|
|
2486
2556
|
const buildLog = join(logsDir, `build-${deviceSlotFileKey("android", slot)}.ndjson`);
|
|
2487
|
-
const writer = createWriter(buildLog, {
|
|
2557
|
+
const writer = tapBuildLog(createWriter(buildLog, {
|
|
2488
2558
|
truncate: true,
|
|
2489
|
-
fields: {
|
|
2490
|
-
|
|
2559
|
+
fields: {
|
|
2560
|
+
platform: PLATFORM,
|
|
2561
|
+
slot
|
|
2562
|
+
}
|
|
2563
|
+
}), progress);
|
|
2491
2564
|
const record = {
|
|
2492
2565
|
fingerprint: null,
|
|
2493
2566
|
cacheKey: null,
|
|
@@ -2503,12 +2576,13 @@ async function runAndroid(options = {}) {
|
|
|
2503
2576
|
write: recordStats,
|
|
2504
2577
|
now,
|
|
2505
2578
|
note: (line) => out(phaseLine("stats", chalk.dim(line))),
|
|
2506
|
-
phases: () => progress.durations()
|
|
2579
|
+
phases: () => progress.durations(),
|
|
2580
|
+
deviceSetup: () => progress.deviceSetupKnown()
|
|
2507
2581
|
});
|
|
2508
2582
|
const recordRun = stats.record;
|
|
2509
2583
|
let ccacheActivity = CCACHE_NOT_RUN;
|
|
2510
2584
|
let reclaimed = [];
|
|
2511
|
-
const fail = (code, message, remedy, { lastBuildStatus = false, diagnostics = [], buildDiagnostics: rawDiagnostics = [], lines = [], logPath = null, lease } = {}) => {
|
|
2585
|
+
const fail = (code, message, remedy, { lastBuildStatus = false, setup = false, diagnostics = [], buildDiagnostics: rawDiagnostics = [], lines = [], logPath = null, lease } = {}) => {
|
|
2512
2586
|
const cancellation = cancelledFailure(PLATFORM, {
|
|
2513
2587
|
code,
|
|
2514
2588
|
message
|
|
@@ -2530,7 +2604,7 @@ async function runAndroid(options = {}) {
|
|
|
2530
2604
|
for (const line of lines) out(phaseLine("", chalk.dim(line)));
|
|
2531
2605
|
if (remedy) out(phaseLine("remedy", remedy));
|
|
2532
2606
|
if (logPath) out(phaseLine("log", logPath));
|
|
2533
|
-
recordRun({
|
|
2607
|
+
if (!setup) recordRun({
|
|
2534
2608
|
failed: true,
|
|
2535
2609
|
durationMs: now() - started
|
|
2536
2610
|
});
|
|
@@ -2559,18 +2633,25 @@ async function runAndroid(options = {}) {
|
|
|
2559
2633
|
gitCommonDir: gitCommonDir(root),
|
|
2560
2634
|
repoRoot: settingsRepoRoot
|
|
2561
2635
|
};
|
|
2562
|
-
const projectKey = statsProjectKey({
|
|
2636
|
+
const projectKey = statsProjectKey$1({
|
|
2563
2637
|
root,
|
|
2564
2638
|
commonDir: settingsContext.gitCommonDir,
|
|
2565
2639
|
repoRoot: settingsRepoRoot
|
|
2566
2640
|
});
|
|
2567
2641
|
stats.setProject(projectKey);
|
|
2642
|
+
progress.estimate(projectKey);
|
|
2568
2643
|
let estimatesRead = null;
|
|
2569
2644
|
const estimates = () => estimatesRead ??= readEstimates({
|
|
2570
2645
|
projectKey,
|
|
2571
2646
|
platform: PLATFORM
|
|
2572
2647
|
});
|
|
2573
2648
|
const settings = resolveSettingsFor(settingsContext);
|
|
2649
|
+
const placement = resolveBuildPlacement(options.buildMachine);
|
|
2650
|
+
record.buildMachine = placement.selected;
|
|
2651
|
+
if (placement.failure) {
|
|
2652
|
+
const { code, message, remedy } = placement.failure;
|
|
2653
|
+
return fail(code, message, remedy, { setup: true });
|
|
2654
|
+
}
|
|
2574
2655
|
const planned = resolveAndroidRunPlan({
|
|
2575
2656
|
settings,
|
|
2576
2657
|
settingsContext,
|
|
@@ -2660,8 +2741,16 @@ async function runAndroid(options = {}) {
|
|
|
2660
2741
|
devServer = gate.devServer;
|
|
2661
2742
|
phase("metro", `port ${metroPort} (${devServer ? `started: ${devServer.reason}` : `pid ${gate.pid ?? "unknown, started outside Stim"}`})`);
|
|
2662
2743
|
return true;
|
|
2663
|
-
} else
|
|
2664
|
-
|
|
2744
|
+
} else {
|
|
2745
|
+
const pin = metroPortSetting(root);
|
|
2746
|
+
if (pin.error) {
|
|
2747
|
+
phaseFailure = fail("STIM_BAD_ARG", pin.error, SETTING_SHAPE_REMEDY);
|
|
2748
|
+
return false;
|
|
2749
|
+
}
|
|
2750
|
+
metroPort = pin.port ?? reservedPort ?? 8081;
|
|
2751
|
+
phase("metro", pin.port ?? reservedPort ? `port ${metroPort} (not checked)` : `no reservation; using ${DEFAULT_METRO_PORT} (not checked)`);
|
|
2752
|
+
}
|
|
2753
|
+
if (release) metroPort = null;
|
|
2665
2754
|
return true;
|
|
2666
2755
|
}
|
|
2667
2756
|
if (!await resolveMetroPort()) return phaseFailure;
|
|
@@ -2674,7 +2763,7 @@ async function runAndroid(options = {}) {
|
|
|
2674
2763
|
isExpo,
|
|
2675
2764
|
tunnelMode: tunnelModeSetting(settings) ?? void 0,
|
|
2676
2765
|
publicUrl: publicUrlSetting(settings),
|
|
2677
|
-
available: detectRemoteProviders(binOnPath)
|
|
2766
|
+
available: detectRemoteProviders(binOnPath, tunnelModeSetting(settings) ?? "auto")
|
|
2678
2767
|
});
|
|
2679
2768
|
if ("failed" in reachable) return fail(reachable.code ?? "STIM_NO_REMOTE_SESSION", reachable.failed, reachable.remedy);
|
|
2680
2769
|
}
|
|
@@ -2753,6 +2842,7 @@ async function runAndroid(options = {}) {
|
|
|
2753
2842
|
const failure = avdSetupFailure(err, root, emuLog);
|
|
2754
2843
|
return fail(failure.code, failure.message, failure.remedy, failure.extra);
|
|
2755
2844
|
}
|
|
2845
|
+
progress.deviceSetup(didSetUpDevice(device, Boolean(remoteDevice)));
|
|
2756
2846
|
const prepareMs = prepare();
|
|
2757
2847
|
if (device.created || prepareMs >= 2e3) phase("device", `${device.avdName || device.deviceName || label} ${device.created ? "created" : "prepared"} (${formatDuration(prepareMs)})`);
|
|
2758
2848
|
const boot = () => Promise.resolve(ensureDeviceBooted({
|
|
@@ -2808,6 +2898,7 @@ async function runAndroid(options = {}) {
|
|
|
2808
2898
|
isExpo,
|
|
2809
2899
|
device,
|
|
2810
2900
|
physical,
|
|
2901
|
+
remote: Boolean(remoteDevice),
|
|
2811
2902
|
buildPlan,
|
|
2812
2903
|
cacheProviderConfig,
|
|
2813
2904
|
requestedBuildCache,
|
|
@@ -2820,7 +2911,11 @@ async function runAndroid(options = {}) {
|
|
|
2820
2911
|
out,
|
|
2821
2912
|
estimates,
|
|
2822
2913
|
stats,
|
|
2823
|
-
step: progress.step
|
|
2914
|
+
step: progress.step,
|
|
2915
|
+
miss: progress.miss,
|
|
2916
|
+
hit: progress.hit,
|
|
2917
|
+
place: progress.place,
|
|
2918
|
+
waitingOn: progress.waitingOn
|
|
2824
2919
|
}
|
|
2825
2920
|
}, {
|
|
2826
2921
|
deviceAbi,
|
|
@@ -2860,7 +2955,7 @@ async function runAndroid(options = {}) {
|
|
|
2860
2955
|
record.appPath = apkPath;
|
|
2861
2956
|
if (runCancellation()) return fail("STIM_CANCELLED", "before install");
|
|
2862
2957
|
if (startRemoteBoot) {
|
|
2863
|
-
progress.step("
|
|
2958
|
+
progress.step("device");
|
|
2864
2959
|
bootPromise = startRemoteBoot();
|
|
2865
2960
|
const booted = await bootPromise;
|
|
2866
2961
|
if (booted.failed) {
|
|
@@ -2892,6 +2987,7 @@ async function runAndroid(options = {}) {
|
|
|
2892
2987
|
}
|
|
2893
2988
|
};
|
|
2894
2989
|
if (physical) {
|
|
2990
|
+
progress.step("device");
|
|
2895
2991
|
const acquired = await acquireLease({
|
|
2896
2992
|
root,
|
|
2897
2993
|
platform: PLATFORM,
|
|
@@ -2939,6 +3035,9 @@ async function runAndroid(options = {}) {
|
|
|
2939
3035
|
bootPromise,
|
|
2940
3036
|
resolveAvdSerial,
|
|
2941
3037
|
waitForDeviceBoot,
|
|
3038
|
+
dataFreeBytes,
|
|
3039
|
+
trimCaches,
|
|
3040
|
+
wipeDevice,
|
|
2942
3041
|
bootDuration: () => bootDuration,
|
|
2943
3042
|
apkPath,
|
|
2944
3043
|
androidPackage,
|
|
@@ -2975,7 +3074,20 @@ async function runAndroid(options = {}) {
|
|
|
2975
3074
|
recordRun,
|
|
2976
3075
|
reclaimed,
|
|
2977
3076
|
devServer,
|
|
2978
|
-
enterPhase: progress.step
|
|
3077
|
+
enterPhase: progress.step,
|
|
3078
|
+
rebootDevice: () => Promise.resolve(ensureDeviceBooted({
|
|
3079
|
+
platform: PLATFORM,
|
|
3080
|
+
device: {
|
|
3081
|
+
...device,
|
|
3082
|
+
serial: void 0
|
|
3083
|
+
},
|
|
3084
|
+
projectPath: root,
|
|
3085
|
+
out,
|
|
3086
|
+
logFile: emuLog
|
|
3087
|
+
})).catch((e) => ({
|
|
3088
|
+
failed: true,
|
|
3089
|
+
reason: String(e?.message || e)
|
|
3090
|
+
}))
|
|
2979
3091
|
});
|
|
2980
3092
|
} finally {
|
|
2981
3093
|
releaseLease();
|