stim 1.14.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-CYVDcrt3.mjs → activity-B2-kb7k0.mjs} +281 -41
- package/dist/{android-XpvCrV7q.mjs → android-CYmq0jNi.mjs} +2 -2
- package/dist/{android-LbPFRWVL.mjs → android-Cu_L4RpL.mjs} +502 -407
- package/dist/{android-d6rGq1pA.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-BpgSgkJj.mjs → app-install-CAAplXAt.mjs} +95 -161
- package/dist/{budget-CGDkhvv_.mjs → budget-CMiqghig.mjs} +1038 -73
- package/dist/{build-plan-Dcr-3ie0.mjs → build-plan-aeGMFX7e.mjs} +193 -653
- package/dist/build-progress-C2Jv1AtA.mjs +872 -0
- package/dist/build-slots-Df1ApSuH.mjs +127 -0
- package/dist/{cache-manifest-C68QQ0ua.mjs → cache-manifest-DnUHBJaU.mjs} +2 -2
- package/dist/cache-manifest.mjs +1 -1
- package/dist/{chrome-BPCiC3ZX.mjs → chrome-BrcVWQyD.mjs} +1 -1
- package/dist/cli-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-DVcpw6eE.mjs → command-output-CliHHn7a.mjs} +1 -1
- package/dist/{config-lvc7d3C2.mjs → config-D0DbpZtM.mjs} +28 -29
- package/dist/{created-devices-CllH_8eb.mjs → created-devices-BwVb3liH.mjs} +2 -2
- package/dist/{dependency-state-DsT7aKeo.mjs → dependency-state-BmsjKZ2H.mjs} +2 -2
- package/dist/{deps-D9IcKqXS.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-CjuuAJjQ.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-5c3WrVRm.mjs → device-lease-W5Pi1q38.mjs} +43 -36
- package/dist/{device-pool-BiyDyPP-.mjs → device-pool-B2r38YjS.mjs} +4 -4
- package/dist/{device-remote-D87zEzaq.mjs → device-remote-9ulPsdhY.mjs} +68 -162
- package/dist/{doctor-x9pnU9fq.mjs → doctor-BcE5xsBn.mjs} +191 -99
- package/dist/{error-diagnostics-C5HzcwCW.mjs → error-diagnostics-Cb0P1WLS.mjs} +12 -11
- package/dist/{exec-DK8KaVHK.mjs → exec-CaErVv7t.mjs} +5 -1
- package/dist/{gc-ko6mUAYD.mjs → gc-BuL1Genr.mjs} +429 -101
- package/dist/{android-cas-iZK3DOCr.mjs → gradle-CmdOBq1d.mjs} +8 -294
- package/dist/{guide-6R0VNFqI.mjs → guide-cCSGRFd3.mjs} +1959 -222
- package/dist/{guide-status-BZrFPIA_.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-BwDPrnYQ.mjs → ios-3CtCRhwr.mjs} +201 -10
- package/dist/{ios-P1f5nm2n.mjs → ios-Chob8ny7.mjs} +512 -263
- package/dist/{ios-device-DGglU3E_.mjs → ios-device-DERmMmk-.mjs} +12 -7
- package/dist/{ios-device-BiOsSVzd.mjs → ios-device-Ee7sDXfp.mjs} +1 -1
- package/dist/{launch-verify-Deg4tB3Z.mjs → launch-verify-CLlofxRt.mjs} +4 -4
- package/dist/{logs-Civ4gbBH.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-CcEo8Fl8.mjs → metro-Di8EN2Tb.mjs} +20 -120
- package/dist/{named-ports-Blg8IxOh.mjs → named-ports-Dj_XBNaL.mjs} +6 -59
- package/dist/native-run-DFDoIRbY.mjs +147 -0
- package/dist/{native-runtime-DjBHRg5m.mjs → native-runtime-i6OkVg-w.mjs} +9 -5
- package/dist/offload-worker.d.mts +121 -0
- package/dist/offload-worker.mjs +601 -0
- package/dist/{ownership-DyYDF5ww.mjs → ownership-BkYxUtuj.mjs} +3 -2
- package/dist/{page-BHUzTDFt.mjs → page-QF_Ke10s.mjs} +7 -2
- package/dist/{ports-BguS83ex.mjs → ports-DddwttKM.mjs} +3 -3
- package/dist/prebuild-BvxDlD6C.mjs +213 -0
- package/dist/process-identity-BmRm1qV0.mjs +12 -0
- package/dist/{project-CWlvEaeN.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-BiniwsSZ.mjs → reload-ChPiwvot.mjs} +70 -22
- package/dist/{remote-cache-CcR6o6lI.mjs → remote-cache-CO2elm_b.mjs} +4 -4
- package/dist/run-B-xqRrsg.mjs +119 -0
- package/dist/{server-bare-DGfgyJ5J.mjs → server-bare-X7PUgizL.mjs} +33 -15
- package/dist/{server-expo-BVuwQ65v.mjs → server-expo-DhtX6bom.mjs} +6 -10
- package/dist/server-expo-KJeD109I.mjs +2 -0
- package/dist/{settings-D61iNOsB.mjs → settings-CduVcoxj.mjs} +77 -26
- package/dist/{settings-B-fsC-Om.mjs → settings-DbL6uhH8.mjs} +16 -10
- package/dist/settings.schema.json +357 -2
- package/dist/{simslim-DrDN4WuV.mjs → simslim-Ciluxlq4.mjs} +8 -132
- package/dist/{slot-launch-D5t8EiMf.mjs → slot-launch-BY0BrOLh.mjs} +6 -6
- package/dist/{spawn-claims-Drw_o-Fb.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-s4RXp1wy.mjs → start-CUD9nasA.mjs} +108 -61
- package/dist/state-B4PQFXMW.d.mts +12 -0
- package/dist/state-BxCgeTQu.mjs +41 -0
- package/dist/{state-DHqvsbqC.mjs → state-CaeaHbDA.mjs} +7 -4
- package/dist/{state-Dk9RVXI7.mjs → state-DFm77wRn.mjs} +67 -5
- package/dist/stats-DBLLFmKd.mjs +66 -0
- package/dist/{status-BsPAnfOI.mjs → status-DYXxNOT0.mjs} +423 -268
- package/dist/{status-BgAuxESg.mjs → status-l84Gbvp2.mjs} +116 -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-BzreOWJp.mjs → stop-Bad61z_2.mjs} +38 -12
- package/dist/stop-Bvf1MflS.mjs +48 -0
- package/dist/{stop-BWIF_6BK.mjs → stop-CceYh1yG.mjs} +2 -2
- package/dist/stop-cause-CZ_Mr-v9.mjs +665 -0
- package/dist/supervisor-run.d.mts +43 -11
- package/dist/supervisor-run.mjs +227 -19
- package/dist/support-CDQbDkL5.mjs +315 -0
- package/dist/support-WLOzVKUv.mjs +176 -0
- package/dist/teardown-BYuQafiA.mjs +718 -0
- package/dist/{xcode-Cp5KvK9x.mjs → toolchain-DmeYjXD9.mjs} +363 -13
- package/dist/warm-progress-DVHLN4uC.mjs +463 -0
- package/dist/{idle-DXtx3RbX.mjs → watchman-Bx0wceoF.mjs} +55 -8
- package/dist/{web-BsQs1WlO.mjs → web-k6w8-HnE.mjs} +37 -20
- package/dist/web-run.mjs +309 -12
- 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-BVrHsYZG.mjs → worktree-C0c5Udwa.mjs} +55 -54
- package/dist/{worktree-UrQWXSeb.mjs → worktree-qXKzADSC.mjs} +154 -252
- package/package.json +10 -5
- package/shim/bundle-response.cjs +28 -12
- package/dist/build-progress-DxKpwnaS.mjs +0 -247
- package/dist/in-use-DlqBDKqY.mjs +0 -249
- package/dist/server-expo-DIHDhvGg.mjs +0 -2
- package/dist/start-Cetfvy8l.mjs +0 -2
- package/dist/stats-CFpFGlJV.mjs +0 -62
- package/dist/stats-CX1Z8Tca.mjs +0 -368
- package/dist/stim-desktop-CXt8-Z3J.mjs +0 -40
- package/dist/stop-cause-hjGBOZTG.mjs +0 -1231
- package/dist/worktree-CHWWvm5v.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,9 +1413,9 @@ 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)`);
|
|
1461
1419
|
const siblings = metroCheck ? siblingPlatformSlots(root, "android", slot) : [];
|
|
1462
1420
|
const verification = metroCheck ? await verifyLaunched({
|
|
1463
1421
|
timeoutMs,
|
|
@@ -1553,6 +1511,60 @@ async function verifyAndroidRun({ root, slot, release, remoteRelease, remoteDevi
|
|
|
1553
1511
|
})) phase("", chalk.yellow(line));
|
|
1554
1512
|
return { state: LAUNCH_UNVERIFIED };
|
|
1555
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
|
+
}
|
|
1556
1568
|
async function resolveInstallSerial({ root, slot, device, physical, remoteDevice, serial, resolveAvdSerial, waitForDeviceBoot, phase }) {
|
|
1557
1569
|
if (physical || remoteDevice || !device.owned || !device.avdName) return serial;
|
|
1558
1570
|
const resolved = resolveAvdSerial(device.avdName, { timeoutMs: 5e3 });
|
|
@@ -1575,7 +1587,7 @@ async function resolveInstallSerial({ root, slot, device, physical, remoteDevice
|
|
|
1575
1587
|
});
|
|
1576
1588
|
return resolved.serial;
|
|
1577
1589
|
}
|
|
1578
|
-
async function finishAndroidRun({ lease, releaseLease, root, slot, json, metroCheck, useBuildCache, variant, release, isExpo, metroPort, logsDir, emuLog, device, physical, remoteDevice, bootPromise, resolveAvdSerial, waitForDeviceBoot, bootDuration, apkPath, androidPackage: initialPackage, swapDir, record, waitedForBuild, ccache, uploadPending, providerUpload, providerName, remote, abandonedRemote: remoteWasAbandoned, started, startedAt, writer, phase, fail, readApkPackage, install, launch, launchRelease, resolveDevClientScheme, verifyLaunched, verifyReleaseLaunched, spawn, kill, pidAlive, verifyCollector, writeLaunch, recordBuild, now, out, emit, recordRun, reclaimed, devServer, enterPhase }) {
|
|
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 }) {
|
|
1579
1591
|
let androidPackage = initialPackage;
|
|
1580
1592
|
let leaseWarned = false;
|
|
1581
1593
|
const raiseLeaseFor = (boundMs, beforeInstall) => {
|
|
@@ -1591,7 +1603,7 @@ async function finishAndroidRun({ lease, releaseLease, root, slot, json, metroCh
|
|
|
1591
1603
|
}
|
|
1592
1604
|
return null;
|
|
1593
1605
|
};
|
|
1594
|
-
enterPhase("
|
|
1606
|
+
enterPhase("device");
|
|
1595
1607
|
const booted = await bootPromise;
|
|
1596
1608
|
const runCommand = nativeRunCommand("android", slot, {
|
|
1597
1609
|
physical,
|
|
@@ -1635,13 +1647,37 @@ async function finishAndroidRun({ lease, releaseLease, root, slot, json, metroCh
|
|
|
1635
1647
|
}
|
|
1636
1648
|
const lostBeforeInstall = physical ? raiseLeaseFor(ADB_INSTALL_TIMEOUT_MS, true) : null;
|
|
1637
1649
|
if (lostBeforeInstall) return lostBeforeInstall;
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
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);
|
|
1645
1681
|
if (installed.failed) {
|
|
1646
1682
|
const installRemedy = remoteDevice?.failureRemedy() ?? localInstallRemedy({
|
|
1647
1683
|
reason: installed.reason,
|
|
@@ -1867,7 +1903,11 @@ async function finishAndroidRun({ lease, releaseLease, root, slot, json, metroCh
|
|
|
1867
1903
|
lease: physical ? leaseFacts : void 0,
|
|
1868
1904
|
recordRun,
|
|
1869
1905
|
reclaimed,
|
|
1870
|
-
devServer
|
|
1906
|
+
devServer,
|
|
1907
|
+
links: workspaceLinks(root, {
|
|
1908
|
+
platform: "android",
|
|
1909
|
+
slot
|
|
1910
|
+
})
|
|
1871
1911
|
});
|
|
1872
1912
|
if (remoteWasAbandoned || uploadWasAbandoned) exitAfterFlush(0);
|
|
1873
1913
|
return {
|
|
@@ -2004,10 +2044,12 @@ function resolveAndroidRunPlan({ settings, settingsContext, slot, easProfile, va
|
|
|
2004
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.");
|
|
2005
2045
|
const waitSeconds = waitParsed.seconds;
|
|
2006
2046
|
const remoteBackend = physical ? null : commandRemoteBackend ?? remoteAndroidSetting(settings);
|
|
2047
|
+
const settingsLayersForOrigin = settingsLayers(settingsContext);
|
|
2007
2048
|
const imageRefusal = systemImageRefusal({
|
|
2008
2049
|
slot,
|
|
2009
2050
|
flag: systemImageFlag,
|
|
2010
2051
|
resolved: systemImage,
|
|
2052
|
+
origin: settingOriginScope(settingsLayersForOrigin, "android.systemImage"),
|
|
2011
2053
|
physical,
|
|
2012
2054
|
remoteBackend,
|
|
2013
2055
|
listImages: listSystemImages
|
|
@@ -2016,11 +2058,18 @@ function resolveAndroidRunPlan({ settings, settingsContext, slot, easProfile, va
|
|
|
2016
2058
|
const profileRefusal = deviceProfileRefusal({
|
|
2017
2059
|
flag: deviceProfileFlag,
|
|
2018
2060
|
resolved: deviceProfile,
|
|
2061
|
+
origin: settingOriginScope(settingsLayersForOrigin, "android.deviceProfile"),
|
|
2019
2062
|
physical,
|
|
2020
2063
|
remoteBackend,
|
|
2021
2064
|
listProfiles: listDeviceProfiles
|
|
2022
2065
|
});
|
|
2023
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);
|
|
2024
2073
|
if (!physical && !remoteBackend) {
|
|
2025
2074
|
const flagImage = typeof systemImageFlag === "string" && systemImageFlag.trim() ? systemImageFlag.trim() : null;
|
|
2026
2075
|
const existing = ownedAvd(root, slot);
|
|
@@ -2077,7 +2126,7 @@ function resolveAndroidRunPlan({ settings, settingsContext, slot, easProfile, va
|
|
|
2077
2126
|
//#endregion
|
|
2078
2127
|
//#region src/commands/android/next-build.ts
|
|
2079
2128
|
const DEFAULT_PLAN_DEPS = {
|
|
2080
|
-
findRoot: findProjectRoot,
|
|
2129
|
+
findRoot: findProjectRoot$1,
|
|
2081
2130
|
fingerprint: fingerprintProject,
|
|
2082
2131
|
listSystemImages: listInstalledSystemImages,
|
|
2083
2132
|
avdSystemImage: ownedAvdSystemImage,
|
|
@@ -2180,7 +2229,7 @@ async function planAndroid(opts, overrides = {}) {
|
|
|
2180
2229
|
platform: PLATFORM,
|
|
2181
2230
|
slot,
|
|
2182
2231
|
root,
|
|
2183
|
-
projectKey: statsProjectKey({
|
|
2232
|
+
projectKey: statsProjectKey$1({
|
|
2184
2233
|
root,
|
|
2185
2234
|
commonDir: settingsContext.gitCommonDir,
|
|
2186
2235
|
repoRoot: settingsContext.repoRoot
|
|
@@ -2267,12 +2316,12 @@ function androidCommand(program) {
|
|
|
2267
2316
|
registerAndroid(program);
|
|
2268
2317
|
}
|
|
2269
2318
|
function registerAndroid(program) {
|
|
2270
|
-
program.command("android").description("Build (or install from the shared cache), install and launch this workspace's Android app on its owned emulator, wired to the reserved Metro port. A Debug run starts the dev server when it is not running.").option("--eas-profile <name>", "Download a matching EAS development build; on a miss, print the build command without running it").option("--slot <name>", "Reusable device slot within this workspace (default: default)", parseDeviceSlotOption).option("--json", "Emit the facts as a single JSON line on stdout; every other line goes to stderr").option("--plan", "Predict the next build without building, booting or installing: resolve the fingerprint, check the local then remote cache, and print the expected cache result and duration. Writes no Stim state.").option("--no-metro-check", "Skip the reserved-port Metro health check and do not start it (the app will load no bundle unless something else serves it)").option("--no-build-cache", "Build fresh, ignoring cached artifacts (local and the project's build-cache provider); the fresh build still replaces the cache entry").option("--variant <name>", "Gradle variant to assemble and install (e.g. productionDebug on a flavored project); overrides the android.variant setting. A variant ending in Release embeds the JS bundle and skips Metro entirely. Default: debug").option("--system-image <id>", "Android system image to create this workspace's owned AVD from, as the sdkmanager package id (e.g. \"system-images;android-36;google_apis;arm64-v8a\"). Overrides the android.systemImage setting for this invocation. An unknown id refuses with STIM_BAD_ARG and prints the installed images.").option("--device-profile <id>", "Hardware profile to create this workspace's owned AVD with, as an `avdmanager list device -c` id (e.g. \"pixel_fold\", \"pixel_tablet\"). Overrides the android.deviceProfile setting for this invocation. An unknown id refuses with STIM_BAD_ARG and prints the available ids.").option("--device [serial]", "Install and launch on a connected physical device instead of this workspace's owned emulator. With no serial, the first connected device this workspace can lease is used. Stim never creates, boots, or deletes a physical device.").option("--remote <backend>", "Install and launch on a remote device with proxy or EAS. Builds are local unless --eas-profile selects an existing EAS build.", (value) => {
|
|
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) => {
|
|
2271
2320
|
if (REMOTE_DEVICE_BACKENDS.includes(value)) return value;
|
|
2272
2321
|
throw new InvalidArgumentError(`expected one of: ${REMOTE_DEVICE_BACKENDS.join(", ")}`);
|
|
2273
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) => {
|
|
2274
2323
|
if (opts.plan) return planAndroid(opts);
|
|
2275
|
-
const root = findProjectRoot(process.cwd());
|
|
2324
|
+
const root = findProjectRoot$1(process.cwd());
|
|
2276
2325
|
if (!root) {
|
|
2277
2326
|
refuseNoProject({ json: Boolean(opts.json) });
|
|
2278
2327
|
return;
|
|
@@ -2293,6 +2342,7 @@ function registerAndroid(program) {
|
|
|
2293
2342
|
try {
|
|
2294
2343
|
return await runAndroid({
|
|
2295
2344
|
root,
|
|
2345
|
+
buildMachine: opts.buildMachine,
|
|
2296
2346
|
slot: opts.slot,
|
|
2297
2347
|
easProfile: opts.easProfile,
|
|
2298
2348
|
json: Boolean(opts.json),
|
|
@@ -2313,7 +2363,7 @@ function registerAndroid(program) {
|
|
|
2313
2363
|
}, { write: (line) => console.error(chalk.dim(phaseLine("lock", line))) })).ok) process.exit(runCancellation() ? 130 : 1);
|
|
2314
2364
|
});
|
|
2315
2365
|
}
|
|
2316
|
-
function resolveRunAndroidOptions({ root, json = false, remoteDevice: commandRemoteBackend = null, resolveSettingsFor = resolveSettings, resolveRemoteDeviceContext = resolveRemoteContext, remoteDeviceDeps: makeRemoteDeviceDeps = remoteAndroidDeps, resolveEasBin = resolveEasCliBin, ensureMetroReachable: ensureRemoteMetro = ensureMetroReachable, ensureRemoteBootOwned: ensureRemoteOwned = ensureRemoteBootOwned, detectRemoteProviders = detectProviders, metroCheck = true, useBuildCache = true, easProfile, resolveEasDevelopmentBuild: resolveEasBuild = resolveEasDevelopmentBuild, variant: variantFlag = null, systemImage: systemImageFlag = null, deviceProfile: deviceProfileFlag, device: deviceFlag = null, wait: waitFlag = void 0, waitConflict = false, acquireLease = acquireRunLease, makeRunLease = runLease, selectPool = selectFromPool, onLeaseSignal = releaseLeaseOnSignal, listDevices = listAdbDevices, deviceModel = physicalDeviceModel, deviceAbi = androidDeviceAbi, isEmulatorDevice = probeEmulatorSerial, readApkPackage = (apkPath) => apkPackage(dumpApkManifest(apkPath)), getLimits = getConcurrencyLimits, checkCapacity = checkDeviceCapacity, acquireSlot = acquireBuildSlot, releaseSlot = releaseBuildSlot, ensureDevice = ensureOwnedDevice, listSystemImages = listInstalledSystemImages, listDeviceProfiles, ensureDeviceBooted = ensureBooted, resolveAvdSerial = resolveOwnedAvdSerial, waitForDeviceBoot = waitForBoot, resolveMetro = resolveProjectMetro, warmMetro: prewarmMetro = warmMetro, startServer = startDevServer, readState = readWorkspaceState$1, pidAlive = pidExists, verifyCollector = verifyCollectorOwnership, verifyLaunched = verifyLaunch, ensureStorage = ensureWorkspaceStorageSafely, fingerprint = fingerprintProject, untracked = untrackedNativeFiles, resolveCached = resolveBuild, storeCached = storeBuild, storedAssets = storedAssetManifest, captureAssets = captureAssetManifest, acquireLock = acquireBuildLock, releaseLock = releaseBuildLock, waitForBuild: waitForBuild$1 = waitForBuild, loadProvider = loadProjectProvider, easAuth = checkEasAuth, resolveRemoteBuild = resolveRemote, uploadRemoteBuild = uploadRemote, resolveCacheProvider = resolveCacheProviderConfig, loadCacheProviderModule = loadCacheProvider, planPrebuildFor = planPrebuild, prebuild = runPrebuild, build = buildAndroid, ccacheFor = resolveCcache, install = installAndroidApp, launch = launchAndroidApp, launchRelease = launchAndroidReleaseApp, verifyReleaseLaunched = verifyAndroidReleaseLaunch, swapApk = swapApkBundle, resolveDevClientScheme = androidDevClientScheme, spawn = (cmd, args, opts) => getExecutor().spawn(cmd, args, opts), kill = (pid, signal) => process.kill(pid, signal), createWriter = createNdjsonWriter, writeLaunch = writeWorkspaceLaunch, writeState = writeWorkspaceState, recordBuild, recordStats = recordRunStats, readEstimates = readRunEstimates, now = Date.now, out = (line) => console.error(line), emit = (line) => console.log(line) } = {}) {
|
|
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) } = {}) {
|
|
2317
2367
|
return {
|
|
2318
2368
|
root,
|
|
2319
2369
|
json,
|
|
@@ -2354,6 +2404,9 @@ function resolveRunAndroidOptions({ root, json = false, remoteDevice: commandRem
|
|
|
2354
2404
|
ensureDeviceBooted,
|
|
2355
2405
|
resolveAvdSerial,
|
|
2356
2406
|
waitForDeviceBoot,
|
|
2407
|
+
dataFreeBytes,
|
|
2408
|
+
trimCaches,
|
|
2409
|
+
wipeDevice,
|
|
2357
2410
|
resolveMetro,
|
|
2358
2411
|
prewarmMetro,
|
|
2359
2412
|
startServer,
|
|
@@ -2453,7 +2506,7 @@ function avdSetupFailure(error, root, logFile) {
|
|
|
2453
2506
|
};
|
|
2454
2507
|
}
|
|
2455
2508
|
async function runAndroid(options = {}) {
|
|
2456
|
-
let { root, json, commandRemoteBackend, resolveSettingsFor, resolveRemoteDeviceContext, makeRemoteDeviceDeps, resolveEasBin, ensureRemoteMetro, ensureRemoteOwned, detectRemoteProviders, metroCheck, useBuildCache: requestedBuildCache, easProfile, resolveEasBuild, variantFlag, systemImageFlag, deviceProfileFlag, deviceFlag, waitFlag, waitConflict, acquireLease, makeRunLease, selectPool, onLeaseSignal, listDevices, deviceModel, deviceAbi, isEmulatorDevice, readApkPackage, getLimits, checkCapacity, acquireSlot, releaseSlot, ensureDevice, listSystemImages, listDeviceProfiles, ensureDeviceBooted, resolveAvdSerial, waitForDeviceBoot, resolveMetro, prewarmMetro, startServer, readState, pidAlive, verifyCollector, verifyLaunched, ensureStorage, fingerprint, untracked, resolveCached, storeCached, storedAssets, captureAssets, acquireLock, releaseLock, waitForBuild, loadProvider, easAuth, resolveRemoteBuild, uploadRemoteBuild, resolveCacheProvider, loadCacheProviderModule, planPrebuildFor, prebuild, build, ccacheFor, install, launch, launchRelease, verifyReleaseLaunched, swapApk, resolveDevClientScheme, spawn, kill, createWriter, writeLaunch, writeState, recordBuild, recordStats, readEstimates, now, out, emit } = androidSlotOptions(options);
|
|
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);
|
|
2457
2510
|
const progress = options.progress ?? NO_BUILD_PROGRESS;
|
|
2458
2511
|
const slot = validateDeviceSlot(options.slot);
|
|
2459
2512
|
const started = now();
|
|
@@ -2501,10 +2554,13 @@ async function runAndroid(options = {}) {
|
|
|
2501
2554
|
}
|
|
2502
2555
|
const logsDir = workspaceLogsDir(root);
|
|
2503
2556
|
const buildLog = join(logsDir, `build-${deviceSlotFileKey("android", slot)}.ndjson`);
|
|
2504
|
-
const writer = createWriter(buildLog, {
|
|
2557
|
+
const writer = tapBuildLog(createWriter(buildLog, {
|
|
2505
2558
|
truncate: true,
|
|
2506
|
-
fields: {
|
|
2507
|
-
|
|
2559
|
+
fields: {
|
|
2560
|
+
platform: PLATFORM,
|
|
2561
|
+
slot
|
|
2562
|
+
}
|
|
2563
|
+
}), progress);
|
|
2508
2564
|
const record = {
|
|
2509
2565
|
fingerprint: null,
|
|
2510
2566
|
cacheKey: null,
|
|
@@ -2520,12 +2576,13 @@ async function runAndroid(options = {}) {
|
|
|
2520
2576
|
write: recordStats,
|
|
2521
2577
|
now,
|
|
2522
2578
|
note: (line) => out(phaseLine("stats", chalk.dim(line))),
|
|
2523
|
-
phases: () => progress.durations()
|
|
2579
|
+
phases: () => progress.durations(),
|
|
2580
|
+
deviceSetup: () => progress.deviceSetupKnown()
|
|
2524
2581
|
});
|
|
2525
2582
|
const recordRun = stats.record;
|
|
2526
2583
|
let ccacheActivity = CCACHE_NOT_RUN;
|
|
2527
2584
|
let reclaimed = [];
|
|
2528
|
-
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 } = {}) => {
|
|
2529
2586
|
const cancellation = cancelledFailure(PLATFORM, {
|
|
2530
2587
|
code,
|
|
2531
2588
|
message
|
|
@@ -2547,7 +2604,7 @@ async function runAndroid(options = {}) {
|
|
|
2547
2604
|
for (const line of lines) out(phaseLine("", chalk.dim(line)));
|
|
2548
2605
|
if (remedy) out(phaseLine("remedy", remedy));
|
|
2549
2606
|
if (logPath) out(phaseLine("log", logPath));
|
|
2550
|
-
recordRun({
|
|
2607
|
+
if (!setup) recordRun({
|
|
2551
2608
|
failed: true,
|
|
2552
2609
|
durationMs: now() - started
|
|
2553
2610
|
});
|
|
@@ -2576,18 +2633,25 @@ async function runAndroid(options = {}) {
|
|
|
2576
2633
|
gitCommonDir: gitCommonDir(root),
|
|
2577
2634
|
repoRoot: settingsRepoRoot
|
|
2578
2635
|
};
|
|
2579
|
-
const projectKey = statsProjectKey({
|
|
2636
|
+
const projectKey = statsProjectKey$1({
|
|
2580
2637
|
root,
|
|
2581
2638
|
commonDir: settingsContext.gitCommonDir,
|
|
2582
2639
|
repoRoot: settingsRepoRoot
|
|
2583
2640
|
});
|
|
2584
2641
|
stats.setProject(projectKey);
|
|
2642
|
+
progress.estimate(projectKey);
|
|
2585
2643
|
let estimatesRead = null;
|
|
2586
2644
|
const estimates = () => estimatesRead ??= readEstimates({
|
|
2587
2645
|
projectKey,
|
|
2588
2646
|
platform: PLATFORM
|
|
2589
2647
|
});
|
|
2590
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
|
+
}
|
|
2591
2655
|
const planned = resolveAndroidRunPlan({
|
|
2592
2656
|
settings,
|
|
2593
2657
|
settingsContext,
|
|
@@ -2677,8 +2741,16 @@ async function runAndroid(options = {}) {
|
|
|
2677
2741
|
devServer = gate.devServer;
|
|
2678
2742
|
phase("metro", `port ${metroPort} (${devServer ? `started: ${devServer.reason}` : `pid ${gate.pid ?? "unknown, started outside Stim"}`})`);
|
|
2679
2743
|
return true;
|
|
2680
|
-
} else
|
|
2681
|
-
|
|
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;
|
|
2682
2754
|
return true;
|
|
2683
2755
|
}
|
|
2684
2756
|
if (!await resolveMetroPort()) return phaseFailure;
|
|
@@ -2691,7 +2763,7 @@ async function runAndroid(options = {}) {
|
|
|
2691
2763
|
isExpo,
|
|
2692
2764
|
tunnelMode: tunnelModeSetting(settings) ?? void 0,
|
|
2693
2765
|
publicUrl: publicUrlSetting(settings),
|
|
2694
|
-
available: detectRemoteProviders(binOnPath)
|
|
2766
|
+
available: detectRemoteProviders(binOnPath, tunnelModeSetting(settings) ?? "auto")
|
|
2695
2767
|
});
|
|
2696
2768
|
if ("failed" in reachable) return fail(reachable.code ?? "STIM_NO_REMOTE_SESSION", reachable.failed, reachable.remedy);
|
|
2697
2769
|
}
|
|
@@ -2770,6 +2842,7 @@ async function runAndroid(options = {}) {
|
|
|
2770
2842
|
const failure = avdSetupFailure(err, root, emuLog);
|
|
2771
2843
|
return fail(failure.code, failure.message, failure.remedy, failure.extra);
|
|
2772
2844
|
}
|
|
2845
|
+
progress.deviceSetup(didSetUpDevice(device, Boolean(remoteDevice)));
|
|
2773
2846
|
const prepareMs = prepare();
|
|
2774
2847
|
if (device.created || prepareMs >= 2e3) phase("device", `${device.avdName || device.deviceName || label} ${device.created ? "created" : "prepared"} (${formatDuration(prepareMs)})`);
|
|
2775
2848
|
const boot = () => Promise.resolve(ensureDeviceBooted({
|
|
@@ -2825,6 +2898,7 @@ async function runAndroid(options = {}) {
|
|
|
2825
2898
|
isExpo,
|
|
2826
2899
|
device,
|
|
2827
2900
|
physical,
|
|
2901
|
+
remote: Boolean(remoteDevice),
|
|
2828
2902
|
buildPlan,
|
|
2829
2903
|
cacheProviderConfig,
|
|
2830
2904
|
requestedBuildCache,
|
|
@@ -2837,7 +2911,11 @@ async function runAndroid(options = {}) {
|
|
|
2837
2911
|
out,
|
|
2838
2912
|
estimates,
|
|
2839
2913
|
stats,
|
|
2840
|
-
step: progress.step
|
|
2914
|
+
step: progress.step,
|
|
2915
|
+
miss: progress.miss,
|
|
2916
|
+
hit: progress.hit,
|
|
2917
|
+
place: progress.place,
|
|
2918
|
+
waitingOn: progress.waitingOn
|
|
2841
2919
|
}
|
|
2842
2920
|
}, {
|
|
2843
2921
|
deviceAbi,
|
|
@@ -2877,7 +2955,7 @@ async function runAndroid(options = {}) {
|
|
|
2877
2955
|
record.appPath = apkPath;
|
|
2878
2956
|
if (runCancellation()) return fail("STIM_CANCELLED", "before install");
|
|
2879
2957
|
if (startRemoteBoot) {
|
|
2880
|
-
progress.step("
|
|
2958
|
+
progress.step("device");
|
|
2881
2959
|
bootPromise = startRemoteBoot();
|
|
2882
2960
|
const booted = await bootPromise;
|
|
2883
2961
|
if (booted.failed) {
|
|
@@ -2909,6 +2987,7 @@ async function runAndroid(options = {}) {
|
|
|
2909
2987
|
}
|
|
2910
2988
|
};
|
|
2911
2989
|
if (physical) {
|
|
2990
|
+
progress.step("device");
|
|
2912
2991
|
const acquired = await acquireLease({
|
|
2913
2992
|
root,
|
|
2914
2993
|
platform: PLATFORM,
|
|
@@ -2956,6 +3035,9 @@ async function runAndroid(options = {}) {
|
|
|
2956
3035
|
bootPromise,
|
|
2957
3036
|
resolveAvdSerial,
|
|
2958
3037
|
waitForDeviceBoot,
|
|
3038
|
+
dataFreeBytes,
|
|
3039
|
+
trimCaches,
|
|
3040
|
+
wipeDevice,
|
|
2959
3041
|
bootDuration: () => bootDuration,
|
|
2960
3042
|
apkPath,
|
|
2961
3043
|
androidPackage,
|
|
@@ -2992,7 +3074,20 @@ async function runAndroid(options = {}) {
|
|
|
2992
3074
|
recordRun,
|
|
2993
3075
|
reclaimed,
|
|
2994
3076
|
devServer,
|
|
2995
|
-
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
|
+
}))
|
|
2996
3091
|
});
|
|
2997
3092
|
} finally {
|
|
2998
3093
|
releaseLease();
|