stim 1.14.0 → 1.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/activity-BxwdD81S.mjs +609 -0
- package/dist/agent-device-usage-output-Dpih1Tfa.mjs +411 -0
- package/dist/{android-d6rGq1pA.mjs → android-B_NumVyQ.mjs} +169 -81
- package/dist/{android-LbPFRWVL.mjs → android-Ift4kVvt.mjs} +517 -413
- package/dist/android-cas-8U4DPqeR.mjs +167 -0
- package/dist/android-cas-compiler.mjs +2 -2
- package/dist/{android-XpvCrV7q.mjs → android-r2rBL8kV.mjs} +2 -2
- package/dist/{app-install-BpgSgkJj.mjs → app-install-CKWdbXIX.mjs} +128 -151
- package/dist/attempt-Bk2ouQVL.mjs +39 -0
- package/dist/budget-DjrURfQn.mjs +2700 -0
- package/dist/{build-plan-Dcr-3ie0.mjs → build-plan-DjeSb2Xl.mjs} +192 -653
- package/dist/build-slots-rUk77xqh.mjs +132 -0
- package/dist/{cache-manifest-C68QQ0ua.mjs → cache-manifest-DnUHBJaU.mjs} +2 -2
- package/dist/cache-manifest.mjs +1 -1
- package/dist/{chrome-BPCiC3ZX.mjs → chrome-BrcVWQyD.mjs} +1 -1
- package/dist/cli-BkYZ_6hy.mjs +51 -0
- package/dist/cli.mjs +6 -42
- package/dist/client-BTY686R6.mjs +1368 -0
- package/dist/collector-run.mjs +9 -195
- package/dist/{command-output-DVcpw6eE.mjs → command-output-DnJMdEY-.mjs} +1 -1
- package/dist/{config-lvc7d3C2.mjs → config-D0DbpZtM.mjs} +28 -29
- package/dist/{created-devices-CllH_8eb.mjs → created-devices-D5owjN4C.mjs} +2 -2
- package/dist/{dependency-state-DsT7aKeo.mjs → dependency-state-B6cMXZab.mjs} +2 -2
- package/dist/{deps-D9IcKqXS.mjs → deps-urbmfXig.mjs} +3 -3
- package/dist/{detached-entry-DJHoca49.mjs → detached-entry-CiTEgMR1.mjs} +1 -1
- package/dist/dev-client-CroAIjjj.mjs +43 -0
- package/dist/{device-CjuuAJjQ.mjs → device-DZQjF7H5.mjs} +8 -7
- package/dist/device-capacity-BHwxNBlu.mjs +136 -0
- package/dist/device-host-worker.d.mts +1 -0
- package/dist/device-host-worker.mjs +964 -0
- package/dist/device-ios-BadmeL2V.mjs +381 -0
- package/dist/{device-lease-5c3WrVRm.mjs → device-lease-D-hLqx1b.mjs} +43 -36
- package/dist/{device-pool-BiyDyPP-.mjs → device-lease-run-CqjShqWW.mjs} +6 -126
- package/dist/device-pool-CUaEWMMm.mjs +125 -0
- package/dist/{device-remote-D87zEzaq.mjs → device-remote-D5WVWqOq.mjs} +69 -163
- package/dist/{doctor-x9pnU9fq.mjs → doctor-Cimf-gGB.mjs} +241 -103
- package/dist/{error-diagnostics-C5HzcwCW.mjs → error-diagnostics-B9L4Ae0x.mjs} +15 -18
- package/dist/{exec-DK8KaVHK.mjs → exec-CaErVv7t.mjs} +5 -1
- package/dist/{gc-ko6mUAYD.mjs → gc-Bqy4bjEu.mjs} +599 -108
- package/dist/{android-cas-iZK3DOCr.mjs → gradle-BsO1MdsH.mjs} +8 -294
- package/dist/{guide-6R0VNFqI.mjs → guide-B_uyKTxW.mjs} +2511 -239
- package/dist/{guide-status-BZrFPIA_.mjs → guide-status-CjEZMAD4.mjs} +3 -3
- package/dist/hosted-client-a-WmqE2L.mjs +197 -0
- package/dist/hosted-ios-CIWelIqf.mjs +220 -0
- package/dist/hosted-logs-D5g041Q3.mjs +187 -0
- package/dist/hosted-macos-BSZyIb3y.mjs +135 -0
- package/dist/idle-DYVymG92.mjs +1099 -0
- package/dist/idle-shutdown-CTsDnAde.mjs +122 -0
- package/dist/in-use-DvBw0Kxo.mjs +130 -0
- package/dist/{ios-BwDPrnYQ.mjs → ios-BaInn3sW.mjs} +201 -10
- package/dist/ios-Cr1h5gnO.mjs +189 -0
- package/dist/{ios-P1f5nm2n.mjs → ios-aq3QoGI3.mjs} +1159 -633
- package/dist/{ios-device-BiOsSVzd.mjs → ios-device-Ee7sDXfp.mjs} +1 -1
- package/dist/{ios-device-DGglU3E_.mjs → ios-device-FfCN_C8p.mjs} +12 -7
- package/dist/ios-state-DS5UpYTH.mjs +48 -0
- package/dist/{launch-verify-Deg4tB3Z.mjs → launch-verify-2DBRizjo.mjs} +5 -5
- package/dist/logs-CHEVK8zD.mjs +371 -0
- package/dist/machines-BIBUdhpj.mjs +265 -0
- package/dist/macos-CSYEAFmF.mjs +473 -0
- package/dist/macos-run.d.mts +5 -0
- package/dist/macos-run.mjs +2 -0
- package/dist/maintenance-run.d.mts +4 -0
- package/dist/maintenance-run.mjs +254 -0
- package/dist/{metro-CcEo8Fl8.mjs → metro-BeK1ZPz1.mjs} +20 -120
- package/dist/metro-gateway-BJ-rxQAB.mjs +84 -0
- package/dist/{named-ports-Blg8IxOh.mjs → named-ports-Bk_YXEmE.mjs} +6 -59
- package/dist/native-run-NaBrRFtQ.mjs +147 -0
- package/dist/{native-runtime-DjBHRg5m.mjs → native-runtime-DBzRnEKS.mjs} +9 -5
- package/dist/{ndjson-CDY9IkVC.mjs → ndjson-PclOIwVY.mjs} +2 -2
- package/dist/offload-worker.d.mts +122 -0
- package/dist/offload-worker.mjs +606 -0
- package/dist/{ownership-DyYDF5ww.mjs → ownership-DiA16AsN.mjs} +3 -2
- package/dist/ownership-hCkPzGmr.mjs +571 -0
- package/dist/{page-BHUzTDFt.mjs → page-QF_Ke10s.mjs} +7 -2
- package/dist/{ports-BguS83ex.mjs → ports-R2k4Knz3.mjs} +3 -3
- package/dist/prebuild-D2fsca_Z.mjs +213 -0
- package/dist/preview-DhVbvPAZ.mjs +318 -0
- package/dist/process-identity-BmRm1qV0.mjs +12 -0
- package/dist/{project-CWlvEaeN.mjs → project-Du91Ilvc.mjs} +74 -19
- package/dist/pull-request-pxnDCQzn.mjs +322 -0
- package/dist/pull-requests.d.mts +120 -0
- package/dist/pull-requests.mjs +2 -0
- package/dist/recordings-BIXOzVnr.mjs +37 -0
- package/dist/{reload-BiniwsSZ.mjs → reload-rM9jV1fz.mjs} +109 -24
- package/dist/{remote-cache-CcR6o6lI.mjs → remote-cache-C8-V13XN.mjs} +5 -5
- package/dist/run-f4vI6EnK.mjs +119 -0
- package/dist/{server-bare-DGfgyJ5J.mjs → server-bare-CpPGmnmO.mjs} +33 -15
- package/dist/server-expo-CVqO2c35.mjs +2 -0
- package/dist/{server-expo-BVuwQ65v.mjs → server-expo-MC0rHlOC.mjs} +8 -12
- package/dist/settings-BBpMcDbD.mjs +25 -0
- package/dist/{settings-B-fsC-Om.mjs → settings-ZkDzkzvB.mjs} +22 -10
- package/dist/settings-oTSkkNrM.mjs +1439 -0
- package/dist/settings.schema.json +845 -97
- package/dist/{simslim-DrDN4WuV.mjs → simslim-B8-7AoTI.mjs} +8 -132
- package/dist/{slot-launch-D5t8EiMf.mjs → slot-launch-B8VrHxn1.mjs} +11 -8
- package/dist/{spawn-claims-Drw_o-Fb.mjs → spawn-claims-D0XfSe8u.mjs} +1 -1
- package/dist/spawn-entry-jw19m8Nl.mjs +17 -0
- package/dist/stage-CzaiwFsI.mjs +143 -0
- package/dist/start-CX8Ig-U2.mjs +2 -0
- package/dist/{start-s4RXp1wy.mjs → start-ClDfwXa4.mjs} +116 -65
- package/dist/state-3DwSQIBK.mjs +41 -0
- package/dist/state-B4PQFXMW.d.mts +12 -0
- package/dist/{state-Dk9RVXI7.mjs → state-C1jB7Q8q.mjs} +70 -8
- package/dist/{state-DHqvsbqC.mjs → state-DcW6vOd_.mjs} +7 -4
- package/dist/stats-CEZ_i5je.mjs +77 -0
- package/dist/{status-BgAuxESg.mjs → status-BuvBFeII.mjs} +118 -20
- package/dist/{status-BsPAnfOI.mjs → status-Cs7VLg9D.mjs} +542 -283
- package/dist/status-DlKvVJBE.mjs +49 -0
- package/dist/stdout-reader-C8yjre1x.mjs +23 -0
- package/dist/stim-desktop-Cnz8CPnl.mjs +63 -0
- package/dist/stim-footprint.swift +32 -0
- package/dist/{stim-installations-CINXy_su.mjs → stim-installations-QMEI32v1.mjs} +1 -1
- package/dist/stop-B0Yxtmiy.mjs +48 -0
- package/dist/{stop-BzreOWJp.mjs → stop-Bffozsac.mjs} +83 -16
- package/dist/{stop-BWIF_6BK.mjs → stop-Dt_dM16A.mjs} +2 -2
- package/dist/stop-cause-CidxIqup.mjs +97 -0
- package/dist/supervisor-run.d.mts +43 -11
- package/dist/supervisor-run.mjs +363 -24
- package/dist/support-CAP8qDUF.mjs +316 -0
- package/dist/support-D4aYJniO.mjs +201 -0
- package/dist/teardown-CL6vDs2Y.mjs +727 -0
- package/dist/{xcode-Cp5KvK9x.mjs → toolchain-DkUflduf.mjs} +364 -14
- package/dist/trigger-Bemu8ytw.mjs +65 -0
- package/dist/trigger-CXts6qt1.mjs +2 -0
- package/dist/warm-progress-CpGv3Wb6.mjs +732 -0
- package/dist/{web-BsQs1WlO.mjs → web-DWUaMp8P.mjs} +37 -21
- package/dist/web-run.mjs +310 -13
- package/dist/workspace-process-lock-BL7Q0HCO.mjs +57 -0
- package/dist/workspace-state-B8-aJ1_5.mjs +371 -0
- package/dist/workspaces-BV4pdHFz.mjs +306 -0
- package/dist/{worktree-UrQWXSeb.mjs → worktree-K-xj9Db0.mjs} +625 -256
- package/dist/worktree-jnG8Iph5.mjs +2 -0
- package/package.json +10 -5
- package/shim/bundle-response.cjs +28 -12
- package/dist/activity-CYVDcrt3.mjs +0 -356
- package/dist/budget-CGDkhvv_.mjs +0 -1745
- package/dist/build-progress-DxKpwnaS.mjs +0 -247
- package/dist/idle-DXtx3RbX.mjs +0 -191
- package/dist/in-use-DlqBDKqY.mjs +0 -249
- package/dist/logs-Civ4gbBH.mjs +0 -486
- package/dist/merge-state-ytFQCeh4.mjs +0 -262
- package/dist/server-expo-DIHDhvGg.mjs +0 -2
- package/dist/settings-D61iNOsB.mjs +0 -629
- package/dist/start-Cetfvy8l.mjs +0 -2
- package/dist/stats-CFpFGlJV.mjs +0 -62
- package/dist/stats-CX1Z8Tca.mjs +0 -368
- package/dist/stim-desktop-CXt8-Z3J.mjs +0 -40
- package/dist/stop-cause-hjGBOZTG.mjs +0 -1231
- package/dist/worktree-BVrHsYZG.mjs +0 -752
- package/dist/worktree-CHWWvm5v.mjs +0 -2
- package/dist/{metro-store-DzeWkfqW.mjs → metro-store-CDDZHtPl.mjs} +1 -1
|
@@ -1,39 +1,52 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import { a as
|
|
17
|
-
import {
|
|
18
|
-
import { c as
|
|
19
|
-
import { c as
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import { t as
|
|
25
|
-
import { a as
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
30
|
-
import { a as
|
|
31
|
-
import { a as
|
|
1
|
+
import { t as getExecutor } from "./exec-CaErVv7t.mjs";
|
|
2
|
+
import { E as upsertProject, L as deviceSlotFileKey, R as deviceSlotKey, V as parseDeviceSlotOption, W as validateDeviceSlot, p as getProject, u as getConcurrencyLimits, v as recordIosSchemeApprovals, z as deviceSlotPlatforms } from "./config-D0DbpZtM.mjs";
|
|
3
|
+
import { n as ownership_claim_exports } from "./process-identity-BmRm1qV0.mjs";
|
|
4
|
+
import { _ as stepTimer, c as launchErrorReport, g as stepClock, h as shortUdid, i as formatDuration, l as phaseLine, m as shortHash, n as appReadinessMessage, v as launchErrorPreview } from "./command-output-DnJMdEY-.mjs";
|
|
5
|
+
import { B as settingsLayers, E as parseIosRemote, G as detectProviders, H as unknownSettingKeys, J as artifactCachePolicy, L as settingOriginScope, M as remoteIosSetting, N as resolveCacheProviderConfig, P as resolveSettings, R as settingShapeErrors, S as metroPortSetting, Tt as removeTemporaryEntry, V as tunnelModeSetting, X as optimizationBuildProfile, Z as resolveOptimizations, _ as iosSigningIdentitySha1Setting, a as SETTING_SHAPE_REMEDY, d as cacheProviderSettingError, g as iosSigningIdentitySettingError, gt as repoRoot, h as iosSigningIdentitySetting, it as gitCommonDir, k as publicUrlSetting, m as iosLanHostSettingError, p as iosLanHostSetting, v as iosSigningIdentitySha1SettingError, w as metroWarmupUrlSetting, wt as makeTemporaryDirectory } from "./settings-oTSkkNrM.mjs";
|
|
6
|
+
import { n as appProjectProblem, o as findProjectRoot$1, r as detectIsExpo, t as NO_PROJECT_REFUSAL } from "./project-Du91Ilvc.mjs";
|
|
7
|
+
import { l as resolveProjectMetro, o as pidExists, u as signalProcessTree } from "./metro-BeK1ZPz1.mjs";
|
|
8
|
+
import { c as writeWorkspaceState, g as workspaceAgentDeviceDir, i as recordWorkspaceUse, r as readWorkspaceState$1, v as workspaceDir, x as workspaceLogsDir$1 } from "./workspace-state-B8-aJ1_5.mjs";
|
|
9
|
+
import { t as budgetGate } from "./budget-DjrURfQn.mjs";
|
|
10
|
+
import { A as providerDownloadPath, D as fingerprintProject, E as fingerprintDiffRecord, L as untrackedMissLine, M as refingerprintAfterMutation, N as resolveBuild, O as inputsChangedDuringBuild, P as storeBuild, R as untrackedNativeFiles, S as configInputsChanged, T as filesystemBuildCapability, _ as waitForBuild, b as buildCacheKey, g as releaseBuildLock, j as providerUploadOutcome, k as prepareProviderDownloadDir, m as acquireBuildLock, v as waitForSharedBuild, x as changedDuringBuildLine } from "./device-lease-D-hLqx1b.mjs";
|
|
11
|
+
import { r as releaseBuildSlot, t as acquireBuildSlot } from "./build-slots-rUk77xqh.mjs";
|
|
12
|
+
import { m as writeWorkspaceLaunch, n as MODE_EXPO, t as MODE_BARE } from "./state-DcW6vOd_.mjs";
|
|
13
|
+
import { S as statsProjectKey$1, _ as tapBuildLog, g as startBuildProgress, h as recordFinishedBuild, l as NO_BUILD_PROGRESS, v as createRunRecorder, x as recordRunStats, y as readRunEstimates } from "./idle-DYVymG92.mjs";
|
|
14
|
+
import { ft as parkedMaxSetting, ot as POOL_SETTING_REMEDY } from "./android-B_NumVyQ.mjs";
|
|
15
|
+
import { p as listIosRuntimes, r as clearIosAppData, u as listAllIosSims } from "./ios-BaInn3sW.mjs";
|
|
16
|
+
import { a as workspaceLinks, i as workspaceLinkLine } from "./stim-desktop-Cnz8CPnl.mjs";
|
|
17
|
+
import { r as createNdjsonWriter } from "./ndjson-PclOIwVY.mjs";
|
|
18
|
+
import { S as bundlerPin, _ as readBundleId, c as COMPILATION_CACHE_NOT_RUN, d as buildIos, g as readBundleExecutable, h as discoverXcodeProject, p as compilationCacheActivityLine, r as iosToolchain, v as resolveScheme } from "./toolchain-DkUflduf.mjs";
|
|
19
|
+
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, u as simulatorRuntime } from "./client-BTY686R6.mjs";
|
|
20
|
+
import { n as verifyCollectorOwnership } from "./ownership-DiA16AsN.mjs";
|
|
21
|
+
import { C as pickDevClientScheme, S as restartedAppNote, T as schemesFromInfoPlist, _ as installIosApp, a as IOS_DEV_MENU_OFF_DEFAULTS_PLIST, d as devClientUrl, l as clearOtherUserApps, r as DEFAULT_METRO_PORT, v as iosAppProcess, x as launchIosApp } from "./app-install-CKWdbXIX.mjs";
|
|
22
|
+
import { t as checkDeviceCapacity } from "./device-capacity-BHwxNBlu.mjs";
|
|
23
|
+
import { n as devClientTakesDevMenuParams, t as devClientScheme } from "./dev-client-CroAIjjj.mjs";
|
|
24
|
+
import { t as spawnEntry } from "./spawn-entry-jw19m8Nl.mjs";
|
|
25
|
+
import { a as runPodInstall, n as podsAreStale, r as readPodState } from "./deps-urbmfXig.mjs";
|
|
26
|
+
import { i as staleNativeDirRefusal, n as recordPrebuild, r as runPrebuild, t as planPrebuild } from "./prebuild-D2fsca_Z.mjs";
|
|
27
|
+
import { c as readMetroRecords, f as verifyLaunch, i as RELEASE_VERIFY_WAIT_MS, l as unattributedLaunchLines, n as LAUNCH_FATAL, o as isBundleProof, p as verifyReleaseLaunch, r as LAUNCH_UNVERIFIED, s as readCollectorRecords, t as LAUNCH_BUNDLING, u as unverifiedLaunchLines } from "./launch-verify-2DBRizjo.mjs";
|
|
28
|
+
import { a as acquireRunLease, c as lostRefusal, d as runLease, f as waitFlagConflict, o as leaseExpiryText, s as lostLine, t as DEBUG_VERIFY_STEP_MS, u as releaseLeaseOnSignal } from "./device-lease-run-CqjShqWW.mjs";
|
|
29
|
+
import { n as clearIosAdoptionPending } from "./device-ios-BadmeL2V.mjs";
|
|
30
|
+
import { _ as simulatorBuildArch, a as deviceShortName, c as isReleaseConfiguration, d as printDiagnostics, f as resolveConfiguration, g as resolveSimulatorAppFlag, h as resolveRuntime, i as deviceModelRefusal, l as ownedSimFailure, m as resolveIosWait, n as buildLogFile, o as iosConfigurationSetting, p as resolveDeviceType, r as deviceLabel, s as iosProviderRunOptions, t as appNameFromPath, u as podAction, v as xcodeFailureReport } from "./support-D4aYJniO.mjs";
|
|
31
|
+
import { a as easAuthNote, c as loadProjectProvider, f as resolveEasCliBin, i as checkEasAuth, m as uploadRemote, n as UPLOAD_TIMEOUT_MS, o as exitAfterFlush, p as resolveRemote, r as cacheLevel, s as isEasAuthFailureText, t as RESOLVE_TIMEOUT_MS } from "./remote-cache-C8-V13XN.mjs";
|
|
32
|
+
import { O as readRemoteSimulatorArch, a as ensureMetroReachable, k as gateMetroOrigin, l as remoteIosDeps, n as binOnPath, o as ensureRemoteBootOwned, r as bundleEntryPoint, u as resolveRemoteContext, w as ownedSessionName } from "./device-remote-D5WVWqOq.mjs";
|
|
33
|
+
import { d as withNativeBuildRun, r as cancelledFailure, u as runCancellation } from "./native-run-NaBrRFtQ.mjs";
|
|
34
|
+
import { a as isEasBuildFailure, i as easDeviceBuildRemedy, o as planEasDevelopmentBuild, s as resolveEasDevelopmentBuild } from "./simslim-B8-7AoTI.mjs";
|
|
35
|
+
import { c as startDevServer, h as detectBundleId } from "./start-ClDfwXa4.mjs";
|
|
36
|
+
import { a as refusePlan, c as skippedMissReason, f as swapJsBundle, h as ensureOwnedDevice, i as printPlan, m as ensureBooted, n as planFlagRefusal, o as explainBuildMiss, p as didSetUpDevice, r as planPayload, s as fingerprintErrorMissReason, t as planCachedBuild, v as warmMetro } from "./build-plan-DjeSb2Xl.mjs";
|
|
37
|
+
import { a as sleep, i as launchOutcomeRecord, n as ensureWorkspaceStorageSafely, r as isPhysicalDeviceRequest, t as ensureDevServer } from "./native-runtime-DBzRnEKS.mjs";
|
|
38
|
+
import { t as selectFromPool } from "./device-pool-CUaEWMMm.mjs";
|
|
39
|
+
import { n as nativeRunCommand, r as siblingPlatformSlots, t as launchSlotScope } from "./slot-launch-B8VrHxn1.mjs";
|
|
40
|
+
import { a as printNativeCrashReport, n as IOS_CRASH_REPORT_RETRY, o as simulatorConsolePaths, r as captureNativeCrashes, t as errorDiagnostics } from "./error-diagnostics-B9L4Ae0x.mjs";
|
|
41
|
+
import { a as iosPoolCandidates, c as isWirelessIosDevice, f as resolveIosPhysicalDevice, i as iosDeviceProcess, l as listIosDevices, n as installIosDeviceApp, o as iosPoolNoCandidatesRefusal, p as verifyIosDeviceReleaseLaunch, r as iosDeviceBounds, t as awaitIosDeviceLaunch, u as localNetworkPending } from "./ios-device-FfCN_C8p.mjs";
|
|
42
|
+
import { n as writeHostedIos, t as readHostedIos } from "./ios-state-DS5UpYTH.mjs";
|
|
43
|
+
import { n as prepareHostedIos, t as placeHostedIos } from "./hosted-ios-CIWelIqf.mjs";
|
|
44
|
+
import { X509Certificate } from "node:crypto";
|
|
32
45
|
import { existsSync, mkdirSync, mkdtempSync, openSync, rmSync, writeFileSync } from "node:fs";
|
|
33
|
-
import { InvalidArgumentError } from "commander";
|
|
34
|
-
import { buildDiagnostics } from "@stim-cli/core/state";
|
|
35
46
|
import { basename, join } from "node:path";
|
|
36
|
-
import {
|
|
47
|
+
import { hostSimulatorArch } from "@stim-cli/core";
|
|
48
|
+
import { InvalidArgumentError } from "commander";
|
|
49
|
+
import { buildDiagnostics, hostedIosStatus, isJsonObject, machineCapacity, parseMachine } from "@stim-cli/core/state";
|
|
37
50
|
import { networkInterfaces, tmpdir } from "node:os";
|
|
38
51
|
import chalk from "chalk";
|
|
39
52
|
import { createWarnOnce, loadCacheProvider, resolveTieredBuild, storeTieredBuild } from "@stim-cli/cache";
|
|
@@ -111,149 +124,9 @@ function copyAppAside(appPath, { exec = null, mkdtemp = () => makeTemporaryDirec
|
|
|
111
124
|
appPath: copy
|
|
112
125
|
};
|
|
113
126
|
}
|
|
114
|
-
function buildLogFile(root) {
|
|
115
|
-
return join(workspaceLogsDir(root), `build-ios.ndjson`);
|
|
116
|
-
}
|
|
117
|
-
const MAX_PRINTED_DIAGNOSTICS = 6;
|
|
118
|
-
function deviceLabel(device, udid) {
|
|
119
|
-
const name = device?.deviceName || device?.name || null;
|
|
120
|
-
return name ? `${name} (${shortUdid(udid)})` : shortUdid(udid);
|
|
121
|
-
}
|
|
122
|
-
function deviceShortName(device, udid) {
|
|
123
|
-
return device?.deviceName || device?.name || shortUdid(udid);
|
|
124
|
-
}
|
|
125
|
-
function appNameFromPath(appPath) {
|
|
126
|
-
if (typeof appPath !== "string" || appPath.trim() === "") return null;
|
|
127
|
-
const name = basename(appPath).replace(/\.app$/i, "");
|
|
128
|
-
return name === "" ? null : name;
|
|
129
|
-
}
|
|
130
|
-
function iosConfigurationSetting(settings) {
|
|
131
|
-
const ios = settings?.["ios"];
|
|
132
|
-
if (!ios || typeof ios !== "object" || Array.isArray(ios)) return null;
|
|
133
|
-
const raw = ios["configuration"];
|
|
134
|
-
return typeof raw === "string" && raw.trim() !== "" ? raw.trim() : null;
|
|
135
|
-
}
|
|
136
|
-
function resolveConfiguration(flag, settings) {
|
|
137
|
-
return (typeof flag === "string" && flag.trim() !== "" ? flag.trim() : null) || iosConfigurationSetting(settings);
|
|
138
|
-
}
|
|
139
|
-
function iosStringSetting(settings, key) {
|
|
140
|
-
const ios = settings?.["ios"];
|
|
141
|
-
if (!ios || typeof ios !== "object" || Array.isArray(ios)) return null;
|
|
142
|
-
const raw = ios[key];
|
|
143
|
-
return typeof raw === "string" && raw.trim() !== "" ? raw.trim() : null;
|
|
144
|
-
}
|
|
145
|
-
function resolveDeviceType(flag, settings) {
|
|
146
|
-
return (typeof flag === "string" && flag.trim() !== "" ? flag.trim() : null) || iosStringSetting(settings, "deviceType");
|
|
147
|
-
}
|
|
148
|
-
function resolveRuntime(flag, settings) {
|
|
149
|
-
return (typeof flag === "string" && flag.trim() !== "" ? flag.trim() : null) || iosStringSetting(settings, "runtime");
|
|
150
|
-
}
|
|
151
|
-
function resolveSimulatorAppFlag(flag, physical, remoteBackend) {
|
|
152
|
-
if (flag === void 0) return {};
|
|
153
|
-
let simulatorApp;
|
|
154
|
-
try {
|
|
155
|
-
simulatorApp = parseIosSimulatorApp(flag, "--simulator-app");
|
|
156
|
-
} catch (error) {
|
|
157
|
-
return { refusal: {
|
|
158
|
-
code: "STIM_BAD_ARG",
|
|
159
|
-
message: error.message
|
|
160
|
-
} };
|
|
161
|
-
}
|
|
162
|
-
if (physical || remoteBackend) return { refusal: {
|
|
163
|
-
code: "STIM_BAD_ARG",
|
|
164
|
-
message: "--simulator-app only applies to a local owned iOS simulator.",
|
|
165
|
-
remedy: "Drop --simulator-app when using a physical or remote device."
|
|
166
|
-
} };
|
|
167
|
-
return { simulatorApp };
|
|
168
|
-
}
|
|
169
|
-
function deviceModelRefusal({ slot, deviceTypeFlag, runtimeFlag, deviceType, runtime, physical, remoteBackend, listRuntimes }) {
|
|
170
|
-
if (slot && slot !== "default" && remoteBackend) return {
|
|
171
|
-
code: "STIM_BAD_ARG",
|
|
172
|
-
message: "Named slots currently support local simulators and physical devices.",
|
|
173
|
-
remedy: "Use the default slot for a remote session."
|
|
174
|
-
};
|
|
175
|
-
if (typeof deviceTypeFlag === "string" && deviceTypeFlag.trim() === "") return {
|
|
176
|
-
code: "STIM_BAD_ARG",
|
|
177
|
-
message: "--device-type was given an empty name.",
|
|
178
|
-
remedy: "Pass `--device-type <name>` with a model `xcrun simctl list devicetypes` names, e.g. \"iPad Pro 13-inch (M5)\"."
|
|
179
|
-
};
|
|
180
|
-
if (typeof runtimeFlag === "string" && runtimeFlag.trim() === "") return {
|
|
181
|
-
code: "STIM_BAD_ARG",
|
|
182
|
-
message: "--runtime was given an empty version.",
|
|
183
|
-
remedy: "Pass `--runtime <version>` with a runtime `xcrun simctl list runtimes` reports, e.g. \"18.5\"."
|
|
184
|
-
};
|
|
185
|
-
if (remoteBackend) {
|
|
186
|
-
const given = [deviceTypeFlag && "--device-type", runtimeFlag && "--runtime"].filter(Boolean).join(" and ");
|
|
187
|
-
if (given) return {
|
|
188
|
-
code: "STIM_BAD_ARG",
|
|
189
|
-
message: `${given} ${given.includes(" and ") ? "apply" : "applies"} only to a local owned iOS simulator; the ${remoteBackend} remote backend chooses its own device.`,
|
|
190
|
-
remedy: `Drop ${given} for a remote run. For a local owned simulator, drop --remote and unset ios.remote with \`stim settings unset ios.remote --scope <workspace|repo|committed>\`.`
|
|
191
|
-
};
|
|
192
|
-
return null;
|
|
193
|
-
}
|
|
194
|
-
if (physical) return null;
|
|
195
|
-
if (!deviceType && !runtime) return null;
|
|
196
|
-
let runtimes;
|
|
197
|
-
try {
|
|
198
|
-
runtimes = listRuntimes();
|
|
199
|
-
} catch (error) {
|
|
200
|
-
return {
|
|
201
|
-
code: "STIM_NO_DEVICE",
|
|
202
|
-
message: `Could not read the installed simulator runtimes: ${error?.message || error}`,
|
|
203
|
-
remedy: "Run `stim doctor` to check the simulator toolchain, then try again."
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
const refusal = unknownIosRuntimeRefusal(runtime, runtimes) ?? unknownIosDeviceTypeRefusal(deviceType, runtimes, runtime);
|
|
207
|
-
return refusal ? {
|
|
208
|
-
code: "STIM_BAD_ARG",
|
|
209
|
-
message: refusal.message,
|
|
210
|
-
remedy: refusal.remedy
|
|
211
|
-
} : null;
|
|
212
|
-
}
|
|
213
|
-
function ownedSimFailure(error) {
|
|
214
|
-
return {
|
|
215
|
-
code: "STIM_NO_DEVICE",
|
|
216
|
-
message: `Could not ensure an owned iOS simulator: ${error?.message || error}`,
|
|
217
|
-
remedy: error instanceof IosDeviceMismatchError ? error.remedy : "Run `stim doctor` to check the simulator toolchain, then try again."
|
|
218
|
-
};
|
|
219
|
-
}
|
|
220
|
-
function isReleaseConfiguration(configuration) {
|
|
221
|
-
return typeof configuration === "string" && configuration.trim() !== "" && configuration.trim().toLowerCase() !== "debug";
|
|
222
|
-
}
|
|
223
|
-
function podAction(podState, verdict) {
|
|
224
|
-
if (verdict?.stale) return {
|
|
225
|
-
install: true,
|
|
226
|
-
reason: verdict.reason
|
|
227
|
-
};
|
|
228
|
-
if (verdict?.noPods && podState?.hasPodfile) return {
|
|
229
|
-
install: true,
|
|
230
|
-
reason: "ios/Podfile exists but no pods are installed"
|
|
231
|
-
};
|
|
232
|
-
return { install: false };
|
|
233
|
-
}
|
|
234
|
-
function xcodeFailureReport(result, logPath) {
|
|
235
|
-
const diagnostics = result.diagnostics;
|
|
236
|
-
const code = result.exitCode;
|
|
237
|
-
const how = code === null ? "" : ` (exit code ${code})`;
|
|
238
|
-
return {
|
|
239
|
-
message: diagnostics.length ? `\`xcodebuild\` failed${how} with ${diagnostics.length} diagnostic${diagnostics.length === 1 ? "" : "s"}.` : `\`xcodebuild\` failed${how} with no recognizable diagnostic.`,
|
|
240
|
-
remedy: diagnostics.find((d) => d?.remedy)?.remedy || `See ${logPath} for the transcript.`
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
|
-
function printDiagnostics(note, result) {
|
|
244
|
-
const diagnostics = result.diagnostics;
|
|
245
|
-
const shown = diagnostics.slice(0, MAX_PRINTED_DIAGNOSTICS);
|
|
246
|
-
for (const diagnostic of shown) note(chalk.red(phaseLine("error", describeDiagnostic(diagnostic))));
|
|
247
|
-
const hidden = diagnostics.length - shown.length + result.truncated;
|
|
248
|
-
if (hidden > 0) note(chalk.dim(phaseLine("error", `... and ${hidden} more diagnostic${hidden === 1 ? "" : "s"} in the log`)));
|
|
249
|
-
if (diagnostics.length === 0) {
|
|
250
|
-
note(chalk.red(phaseLine("error", "xcodebuild failed with no recognizable diagnostic; last lines:")));
|
|
251
|
-
for (const line of result.tail.slice(-5)) note(chalk.dim(phaseLine("", line)));
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
127
|
//#endregion
|
|
255
128
|
//#region src/commands/ios/result.ts
|
|
256
|
-
function lastBuildRecord({ fingerprint = null, cacheKey = null, cacheHit = false, cacheSkipped = false, durationMs = 0, appPath = null, bundleId = null, startedAt, status, errorCode = null, missReason = null, diagnostics = null, configuration = null }) {
|
|
129
|
+
function lastBuildRecord({ fingerprint = null, cacheKey = null, cacheHit = false, cacheSkipped = false, durationMs = 0, appPath = null, bundleId = null, startedAt, status, errorCode = null, missReason = null, diagnostics = null, configuration = null, buildMachine = "auto", builtOn, offloadedTo = null, offloadFallback = null }) {
|
|
257
130
|
const record = {
|
|
258
131
|
platform: "ios",
|
|
259
132
|
fingerprint,
|
|
@@ -268,12 +141,16 @@ function lastBuildRecord({ fingerprint = null, cacheKey = null, cacheHit = false
|
|
|
268
141
|
configuration
|
|
269
142
|
};
|
|
270
143
|
if (errorCode) record.errorCode = errorCode;
|
|
144
|
+
record.buildMachine = buildMachine;
|
|
145
|
+
if (builtOn) record.builtOn = builtOn;
|
|
146
|
+
if (offloadedTo) record.offloadedTo = offloadedTo;
|
|
147
|
+
if (offloadFallback) record.offloadFallback = offloadFallback;
|
|
271
148
|
if (missReason && !cacheLevel(cacheHit)) record.missReason = missReason;
|
|
272
149
|
const recorded = status === "failed" ? buildDiagnostics(diagnostics) : [];
|
|
273
150
|
if (recorded.length) record.diagnostics = recorded;
|
|
274
151
|
return record;
|
|
275
152
|
}
|
|
276
|
-
function iosFacts({ slot, udid, deviceName, deviceType = null, runtime = null, fingerprint, configuration = null, scheme = null, cacheKey, cacheHit, cacheSkipped = false, compilationCache = COMPILATION_CACHE_NOT_RUN, waitedForBuild = null, appPath, bundleId, installSkipped = false, metroPort, logsDir, durationMs, launched = true, webPreviewUrl = null, lease, devServer = null }) {
|
|
153
|
+
function iosFacts({ root, slot, udid, deviceName, deviceType = null, runtime = null, fingerprint, configuration = null, scheme = null, cacheKey, cacheHit, cacheSkipped = false, compilationCache = COMPILATION_CACHE_NOT_RUN, waitedForBuild = null, appPath, bundleId, installSkipped = false, metroPort, logsDir, durationMs, launched = true, webPreviewUrl = null, lease, devServer = null, buildMachine = "auto", builtOn, offloadedTo = null, offloadFallback = null }) {
|
|
277
154
|
return {
|
|
278
155
|
platform: "ios",
|
|
279
156
|
...slot && slot !== "default" ? { slot } : {},
|
|
@@ -287,6 +164,10 @@ function iosFacts({ slot, udid, deviceName, deviceType = null, runtime = null, f
|
|
|
287
164
|
cacheKey,
|
|
288
165
|
cacheHit: cacheLevel(cacheHit),
|
|
289
166
|
cacheSkipped: Boolean(cacheSkipped),
|
|
167
|
+
buildMachine,
|
|
168
|
+
...builtOn ? { builtOn } : {},
|
|
169
|
+
...offloadedTo ? { offloadedTo } : {},
|
|
170
|
+
...offloadFallback ? { offloadFallback } : {},
|
|
290
171
|
compilationCache,
|
|
291
172
|
waitedForBuild: waitedForBuild ? {
|
|
292
173
|
pid: waitedForBuild.pid ?? null,
|
|
@@ -298,6 +179,7 @@ function iosFacts({ slot, udid, deviceName, deviceType = null, runtime = null, f
|
|
|
298
179
|
launched: launched === "unverified" || launched === "bundling" ? launched : Boolean(launched),
|
|
299
180
|
metroPort,
|
|
300
181
|
logs: { dir: logsDir },
|
|
182
|
+
...root ? { agentDevice: { stateDir: workspaceAgentDeviceDir(root) } } : {},
|
|
301
183
|
durationMs,
|
|
302
184
|
...webPreviewUrl ? { webPreviewUrl } : {},
|
|
303
185
|
...lease === void 0 ? {} : { lease },
|
|
@@ -327,13 +209,14 @@ async function finishIosUpload(uploadPending, remote, phase, note) {
|
|
|
327
209
|
}
|
|
328
210
|
return false;
|
|
329
211
|
}
|
|
330
|
-
function reportIosResult({ slot, root, json, release, configuration, buildScheme = null, metroCheck, metroPort, logsDir, device, udid, appPath, bundleId, installSkipped, elapsed, startedAt, storeHash, storeKey, cacheHit, missReason, compilationCache, useBuildCache, waitedForBuild, launchState, launchWarning, providerName, closeWriter, webPreviewUrl, lease, recordRun, reclaimed = [], devServer = null }) {
|
|
212
|
+
function reportIosResult({ host, slot, root, json, release, configuration, buildScheme = null, metroCheck, metroPort, logsDir, device, udid, appPath, bundleId, installSkipped, elapsed, startedAt, storeHash, storeKey, cacheHit, missReason, compilationCache, useBuildCache, waitedForBuild, launchState, launchWarning, providerName, buildMachine = "auto", builtOn, offloadedTo = null, offloadFallback = null, closeWriter, webPreviewUrl, lease, recordRun, reclaimed = [], devServer = null, links }) {
|
|
331
213
|
const durationMs = elapsed();
|
|
332
214
|
recordRun({
|
|
333
215
|
failed: false,
|
|
334
216
|
cacheHit,
|
|
335
217
|
waited: waitedForBuild,
|
|
336
|
-
durationMs
|
|
218
|
+
durationMs,
|
|
219
|
+
offloadedTo
|
|
337
220
|
});
|
|
338
221
|
writeLastBuild(root, lastBuildRecord({
|
|
339
222
|
fingerprint: storeHash,
|
|
@@ -346,10 +229,15 @@ function reportIosResult({ slot, root, json, release, configuration, buildScheme
|
|
|
346
229
|
bundleId,
|
|
347
230
|
startedAt,
|
|
348
231
|
status: "ok",
|
|
349
|
-
configuration: configuration ?? "Debug"
|
|
232
|
+
configuration: configuration ?? "Debug",
|
|
233
|
+
buildMachine,
|
|
234
|
+
builtOn,
|
|
235
|
+
offloadedTo,
|
|
236
|
+
offloadFallback
|
|
350
237
|
}));
|
|
351
238
|
closeWriter();
|
|
352
239
|
const facts = iosFacts({
|
|
240
|
+
root,
|
|
353
241
|
slot,
|
|
354
242
|
udid,
|
|
355
243
|
deviceName: device?.deviceName ?? null,
|
|
@@ -372,35 +260,43 @@ function reportIosResult({ slot, root, json, release, configuration, buildScheme
|
|
|
372
260
|
launched: launchState,
|
|
373
261
|
webPreviewUrl,
|
|
374
262
|
lease,
|
|
375
|
-
devServer
|
|
263
|
+
devServer,
|
|
264
|
+
buildMachine,
|
|
265
|
+
builtOn,
|
|
266
|
+
offloadedTo,
|
|
267
|
+
offloadFallback
|
|
376
268
|
});
|
|
377
|
-
if (
|
|
269
|
+
if (host) facts.host = host;
|
|
270
|
+
if (json) console.log(JSON.stringify({
|
|
378
271
|
...facts,
|
|
379
|
-
|
|
380
|
-
|
|
272
|
+
...links ? { links } : {},
|
|
273
|
+
...reclaimed.length ? { reclaimed } : {}
|
|
274
|
+
}));
|
|
381
275
|
else {
|
|
382
|
-
const summary = `${launchWarning ? "WARNING" : "OK"}: ${bundleId} on ${deviceLabel(device, udid)}, ` + (release ? `${configuration} (embedded JS, no Metro)` : `Metro port ${metroPort}`) + ` (${cacheDescription(cacheHit, providerName)}, ${formatDuration(durationMs)})`;
|
|
276
|
+
const summary = `${launchWarning ? "WARNING" : "OK"}: ${bundleId} on ${host ? `${device.deviceName} (iOS ${device.runtime?.replace(/^iOS /, "")}) on ${host.machine}` : deviceLabel(device, udid)}, ` + (release ? `${configuration} (embedded JS, no Metro)` : `Metro port ${metroPort}`) + ` (${cacheDescription(cacheHit, providerName, offloadedTo)}, ${formatDuration(durationMs)})`;
|
|
383
277
|
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);
|
|
384
278
|
const deviceName = device?.deviceName ?? device?.name ?? udid;
|
|
385
|
-
const cacheResult = useBuildCache ? cacheDescription(cacheHit, providerName) :
|
|
279
|
+
const cacheResult = useBuildCache ? cacheDescription(cacheHit, providerName, offloadedTo) : `bypassed; ${cacheDescription(false, null, offloadedTo)}`;
|
|
386
280
|
const metroResult = release ? `embedded (${configuration})` : !metroCheck ? `check skipped on port ${metroPort}` : launchState === "unverified" ? `state unverified on port ${metroPort}` : `running on port ${metroPort}${devServer ? ` (started: ${devServer.reason})` : ""}`;
|
|
387
281
|
console.log([
|
|
388
282
|
outcome,
|
|
389
|
-
phaseLine("device", `${deviceName} (${udid})`),
|
|
283
|
+
phaseLine("device", host ? `${deviceName} (iOS ${device.runtime?.replace(/^iOS /, "")}) on ${host.machine}` : `${deviceName} (${udid})`),
|
|
390
284
|
phaseLine("app", bundleId),
|
|
391
285
|
phaseLine("metro", metroResult),
|
|
392
286
|
phaseLine("cache", cacheResult),
|
|
393
287
|
...compilationCache.status === "not-run" ? [phaseLine("compilation cache", compilationCacheActivityLine(compilationCache))] : [],
|
|
394
|
-
phaseLine("logs", logsDir)
|
|
288
|
+
phaseLine("logs", logsDir),
|
|
289
|
+
phaseLine("agent-device", `AGENT_DEVICE_STATE_DIR=${workspaceAgentDeviceDir(root)}`)
|
|
395
290
|
].join("\n"));
|
|
396
291
|
if (facts.webPreviewUrl) console.error(chalk.dim(`Watch this device: ${facts.webPreviewUrl}`));
|
|
292
|
+
if (links) console.error(chalk.dim(workspaceLinkLine(links)));
|
|
397
293
|
}
|
|
398
294
|
return facts;
|
|
399
295
|
}
|
|
400
|
-
function cacheDescription(cacheHit, providerName = null) {
|
|
296
|
+
function cacheDescription(cacheHit, providerName = null, offloadedTo = null) {
|
|
401
297
|
if (cacheHit === "remote") return `from ${providerName || "the remote cache"}`;
|
|
402
298
|
if (cacheHit === "local") return "from cache";
|
|
403
|
-
return "built";
|
|
299
|
+
return offloadedTo ? `built on ${offloadedTo}` : "built";
|
|
404
300
|
}
|
|
405
301
|
//#endregion
|
|
406
302
|
//#region src/commands/ios/artifact.ts
|
|
@@ -414,15 +310,17 @@ var ArtifactRefusal = class extends Error {
|
|
|
414
310
|
this.failure = failure;
|
|
415
311
|
}
|
|
416
312
|
};
|
|
417
|
-
async function acquireIosArtifact({ root, logFile, udid, remoteDestination, device, configuration, buildScheme, buildProfile, isExpo, optimizations, cache, easBuild, easProfile, maxBuilds, progress }, d) {
|
|
418
|
-
const { phase, note, logWriter, estimates, stats, step } = progress;
|
|
313
|
+
async function acquireIosArtifact({ root, logFile, udid, remoteDestination, hostedDestination, simulatorArch, device, configuration, buildScheme, buildProfile, buildMachine = "auto", isExpo, optimizations, cache, easBuild, easProfile, maxBuilds, progress }, d) {
|
|
314
|
+
const { phase, note, logWriter, estimates, stats, step, miss, hit: lateHit, place, waitingOn } = progress;
|
|
419
315
|
const physical = device !== null;
|
|
420
316
|
const keyOptions = {
|
|
421
317
|
scheme: buildScheme,
|
|
422
318
|
...configuration ? { configuration } : {},
|
|
423
319
|
isSimulator: !physical,
|
|
320
|
+
...simulatorArch ? { arch: simulatorArch } : {},
|
|
424
321
|
...buildProfile ? { buildProfile } : {}
|
|
425
322
|
};
|
|
323
|
+
const providerRunOptions = iosProviderRunOptions(configuration, simulatorArch);
|
|
426
324
|
const lanAddress = device?.lanAddress ?? null;
|
|
427
325
|
const metroPort = device?.metroPort ?? null;
|
|
428
326
|
const release = isReleaseConfiguration(configuration);
|
|
@@ -476,6 +374,23 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
|
|
|
476
374
|
let appPath = null;
|
|
477
375
|
let bundleId = null;
|
|
478
376
|
let cacheHit = false;
|
|
377
|
+
let offloadedTo = null;
|
|
378
|
+
let handoff = null;
|
|
379
|
+
let offloadFallback = null;
|
|
380
|
+
let fallbackMachine = null;
|
|
381
|
+
let hereReason = "no build machine is paired";
|
|
382
|
+
let slotWaitMs;
|
|
383
|
+
let builtOn;
|
|
384
|
+
const fallBack = (reason, line = reason) => {
|
|
385
|
+
if (namedBuildMachine(buildMachine)) throw new OffloadRefusal(buildMachine, reason);
|
|
386
|
+
offloadFallback = reason;
|
|
387
|
+
buildFailure = {
|
|
388
|
+
...buildFailure,
|
|
389
|
+
offloadFallback: reason
|
|
390
|
+
};
|
|
391
|
+
phase("build", `${line} -> building here`);
|
|
392
|
+
};
|
|
393
|
+
const openOffload = { choice: null };
|
|
479
394
|
let remote = null;
|
|
480
395
|
let abandonedRemote = false;
|
|
481
396
|
let uploadPending = null;
|
|
@@ -599,7 +514,7 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
|
|
|
599
514
|
platform: "ios",
|
|
600
515
|
projectRoot: root,
|
|
601
516
|
fingerprintHash: fingerprint,
|
|
602
|
-
runOptions:
|
|
517
|
+
runOptions: providerRunOptions
|
|
603
518
|
});
|
|
604
519
|
if (hit?.appPath) {
|
|
605
520
|
let stored = null;
|
|
@@ -640,6 +555,7 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
|
|
|
640
555
|
step("wait");
|
|
641
556
|
phase("build", text);
|
|
642
557
|
},
|
|
558
|
+
waitingOn,
|
|
643
559
|
warn: (text) => note(chalk.yellow(phaseLine("build", text))),
|
|
644
560
|
out: note
|
|
645
561
|
});
|
|
@@ -752,32 +668,48 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
|
|
|
752
668
|
}
|
|
753
669
|
}
|
|
754
670
|
}
|
|
755
|
-
function
|
|
756
|
-
if (swapFellBack)
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
671
|
+
function reasonForMiss(rekeyedBy) {
|
|
672
|
+
if (swapFellBack) return {
|
|
673
|
+
reason: skippedMissReason("the cached app could not be reused, so this run built it fresh"),
|
|
674
|
+
diff: null
|
|
675
|
+
};
|
|
676
|
+
if (!useBuildCache) return {
|
|
677
|
+
reason: skippedMissReason(cache.disabledByFlag ? "cache reuse turned off by --no-build-cache" : "cache reuse off in config"),
|
|
678
|
+
diff: null
|
|
679
|
+
};
|
|
680
|
+
const current = {
|
|
681
|
+
hash: storeHash ?? fingerprint,
|
|
682
|
+
sources: storeSources
|
|
683
|
+
};
|
|
684
|
+
const explained = explainBuildMiss({
|
|
685
|
+
root,
|
|
686
|
+
platform: "ios",
|
|
687
|
+
current,
|
|
688
|
+
rekeyedBy,
|
|
689
|
+
baselineDeps: { readState: d.readWorkspaceState }
|
|
690
|
+
});
|
|
691
|
+
return {
|
|
692
|
+
reason: explained.reason,
|
|
693
|
+
diff: explained.previousHash && explained.changedNames.length ? fingerprintDiffRecord({
|
|
772
694
|
changed: explained.changedNames,
|
|
773
695
|
previousHash: explained.previousHash,
|
|
774
696
|
hash: current.hash
|
|
775
|
-
})
|
|
776
|
-
}
|
|
697
|
+
}) : null
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
function reportPendingRecheck(prebuild) {
|
|
701
|
+
const pods = d.readPodState(root);
|
|
702
|
+
if ((prebuild === "generate" || prebuild === "regenerate" || podAction(pods, d.podsAreStale(pods.lockText, pods.manifestText)).install) && useBuildCache) miss(reasonForMiss([]).reason, true);
|
|
703
|
+
}
|
|
704
|
+
function explainMiss(rekeyedBy) {
|
|
705
|
+
const explained = reasonForMiss(rekeyedBy);
|
|
706
|
+
missReason = explained.reason;
|
|
707
|
+
if (explained.diff) logWriter().write(explained.diff);
|
|
777
708
|
buildFailure = {
|
|
778
709
|
...buildFailure,
|
|
779
710
|
missReason
|
|
780
711
|
};
|
|
712
|
+
miss(missReason);
|
|
781
713
|
phase("cache", `miss: ${missReason.summary}`);
|
|
782
714
|
if (missReason.kind === "no-baseline") {
|
|
783
715
|
const untracked = untrackedMissLine(d.untrackedNativeFiles({ projectRoot: root }));
|
|
@@ -821,31 +753,199 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
|
|
|
821
753
|
note(chalk.dim(phaseLine("fingerprint", `${shortHash(beforeBuildHash)} -> ${shortHash(storeHash)} (after xcodebuild)`)));
|
|
822
754
|
}
|
|
823
755
|
}
|
|
756
|
+
async function takeBuildSlot() {
|
|
757
|
+
if (!maxBuilds) return;
|
|
758
|
+
try {
|
|
759
|
+
buildSlot = await d.acquireBuildSlot({
|
|
760
|
+
max: maxBuilds,
|
|
761
|
+
root,
|
|
762
|
+
logFile,
|
|
763
|
+
out: note
|
|
764
|
+
});
|
|
765
|
+
slotWaitMs = buildSlot.slotWaitMs;
|
|
766
|
+
} catch (e) {
|
|
767
|
+
const refusal = (0, ownership_claim_exports.claimFailure)(e, "stim ios");
|
|
768
|
+
if (refusal) fail({
|
|
769
|
+
code: refusal.code,
|
|
770
|
+
message: refusal.message,
|
|
771
|
+
remedy: refusal.remedy,
|
|
772
|
+
build: buildFailure
|
|
773
|
+
});
|
|
774
|
+
note(chalk.yellow(phaseLine("build", `could not take a build slot: ${e?.message || e}; building anyway`)));
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
/** Whether this build should leave this Mac, before any machine is asked; null builds here. */
|
|
778
|
+
function placeBuild() {
|
|
779
|
+
const { mode, machines } = buildPlacementCandidates(buildMachine);
|
|
780
|
+
if (machines.length === 0 && !namedBuildMachine(buildMachine) && buildMachine !== "local") return null;
|
|
781
|
+
const runtime = physical || release ? null : hostedDestination?.runtime ?? (remoteDestination ? null : simulatorRuntime(udid));
|
|
782
|
+
const unsupported = physical ? "device builds build here" : remoteDestination && !hostedDestination ? "--remote builds build here" : release ? `${configuration} builds build here` : !cachePolicy.write ? "the build cache is off" : !runtime ? `the runtime of simulator ${udid} is unknown` : null;
|
|
783
|
+
const here = machineCapacity();
|
|
784
|
+
const placement = offloadPlacement({
|
|
785
|
+
mode,
|
|
786
|
+
machines: machines.length,
|
|
787
|
+
here,
|
|
788
|
+
unsupported,
|
|
789
|
+
selected: buildMachine
|
|
790
|
+
});
|
|
791
|
+
if (!placement.offload) {
|
|
792
|
+
hereReason = placement.reason;
|
|
793
|
+
if (mode !== "off") phase("build", `placement: here (${placement.reason})`);
|
|
794
|
+
return null;
|
|
795
|
+
}
|
|
796
|
+
return {
|
|
797
|
+
mode,
|
|
798
|
+
here,
|
|
799
|
+
reason: placement.reason,
|
|
800
|
+
runtime,
|
|
801
|
+
machines
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
/** Asks the paired machines once the post-mutation key is known; null builds here. */
|
|
805
|
+
async function chooseMachine(candidate) {
|
|
806
|
+
const choice = await chooseBuildMachine({
|
|
807
|
+
projectRoot: root,
|
|
808
|
+
target: {
|
|
809
|
+
platform: "ios",
|
|
810
|
+
local: {
|
|
811
|
+
...iosToolchain(),
|
|
812
|
+
...hostedDestination ? { arch: hostedDestination.architecture === "x86_64" ? "x64" : "arm64" } : {}
|
|
813
|
+
},
|
|
814
|
+
runtime: candidate.runtime,
|
|
815
|
+
cocoapodsPinned: bundlerPin(root) !== null
|
|
816
|
+
},
|
|
817
|
+
mode: candidate.mode,
|
|
818
|
+
here: candidate.here,
|
|
819
|
+
note: (line) => note(chalk.dim(phaseLine("build", `offload: ${line}`))),
|
|
820
|
+
machines: candidate.machines,
|
|
821
|
+
selected: buildMachine
|
|
822
|
+
});
|
|
823
|
+
if (typeof choice === "string") {
|
|
824
|
+
const only = candidate.machines.length === 1 ? candidate.machines[0].machine : null;
|
|
825
|
+
if (only && choice.startsWith(`${only}: `)) fallbackMachine = only;
|
|
826
|
+
fallBack(choice);
|
|
827
|
+
return null;
|
|
828
|
+
}
|
|
829
|
+
openOffload.choice = choice;
|
|
830
|
+
phase("build", `placement: ${choice.machine} (${candidate.reason}${placementLoad(choice)})`);
|
|
831
|
+
return choice;
|
|
832
|
+
}
|
|
833
|
+
/** Builds on the chosen machine and stores the app under the post-mutation key; false builds here instead unless a machine was named. */
|
|
834
|
+
async function compileElsewhere({ choice, candidate }) {
|
|
835
|
+
const runtime = candidate.runtime;
|
|
836
|
+
if (!storeKey || !storeHash) {
|
|
837
|
+
if (namedBuildMachine(buildMachine)) fallBack("the build fingerprint or cache key is unavailable");
|
|
838
|
+
return false;
|
|
839
|
+
}
|
|
840
|
+
const stagingDir = join(workspaceDir(root), "offload", "ios");
|
|
841
|
+
const outcome = await offloadBuild({
|
|
842
|
+
choice,
|
|
843
|
+
expectedFingerprint: storeHash,
|
|
844
|
+
request: {
|
|
845
|
+
platform: "ios",
|
|
846
|
+
runtime,
|
|
847
|
+
configuration,
|
|
848
|
+
scheme: buildScheme ?? null,
|
|
849
|
+
isExpo,
|
|
850
|
+
optimizations
|
|
851
|
+
},
|
|
852
|
+
stagingDir,
|
|
853
|
+
onPhase: (name, msg) => phase(name, remotePhaseText(name, msg, choice.machine)),
|
|
854
|
+
onEnter: (name) => {
|
|
855
|
+
if (name === "compile" || name === "build" || name === "prebuild" || name === "pods") {
|
|
856
|
+
builtOn = choice.machine;
|
|
857
|
+
buildFailure = {
|
|
858
|
+
...buildFailure,
|
|
859
|
+
builtOn
|
|
860
|
+
};
|
|
861
|
+
}
|
|
862
|
+
place({
|
|
863
|
+
host: choice.machine,
|
|
864
|
+
phase: name
|
|
865
|
+
});
|
|
866
|
+
step(name === "prebuild" || name === "pods" ? name : "compile");
|
|
867
|
+
},
|
|
868
|
+
onRecord: (record) => logWriter().write({
|
|
869
|
+
...record,
|
|
870
|
+
offloadedTo: choice.machine
|
|
871
|
+
}),
|
|
872
|
+
note: (line) => phase("build", line)
|
|
873
|
+
});
|
|
874
|
+
place(null);
|
|
875
|
+
let stored = null;
|
|
876
|
+
let reason = outcome.ok ? null : outcome.reason;
|
|
877
|
+
if (outcome.ok) {
|
|
878
|
+
const settled = await refingerprintAfterMutation({
|
|
879
|
+
projectRoot: root,
|
|
880
|
+
platform: "ios",
|
|
881
|
+
previousHash: storeHash,
|
|
882
|
+
fingerprint: d.fingerprintProject
|
|
883
|
+
});
|
|
884
|
+
if (!settled || settled.moved) reason = "the checkout here changed while it built";
|
|
885
|
+
else try {
|
|
886
|
+
stored = d.storeBuild("ios", storeKey, outcome.artifactPath, {
|
|
887
|
+
sources: storeSources,
|
|
888
|
+
overwrite: !useBuildCache
|
|
889
|
+
});
|
|
890
|
+
} catch (e) {
|
|
891
|
+
reason = `could not store the app: ${e?.message || e}`;
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
try {
|
|
895
|
+
rmSync(stagingDir, {
|
|
896
|
+
recursive: true,
|
|
897
|
+
force: true
|
|
898
|
+
});
|
|
899
|
+
} catch {}
|
|
900
|
+
step("compile");
|
|
901
|
+
const prepared = stored ? await installableCachedApp(stored) : null;
|
|
902
|
+
if (!outcome.ok || !prepared) {
|
|
903
|
+
const why = reason ?? "the stored app is not installable";
|
|
904
|
+
fallbackMachine = choice.machine;
|
|
905
|
+
fallBack(`${choice.machine}: ${why}`, `offload failed: ${why}`);
|
|
906
|
+
logWriter().write({
|
|
907
|
+
src: "build",
|
|
908
|
+
level: "warn",
|
|
909
|
+
event: "offload_failed",
|
|
910
|
+
msg: reason,
|
|
911
|
+
machine: choice.machine
|
|
912
|
+
});
|
|
913
|
+
return false;
|
|
914
|
+
}
|
|
915
|
+
const { timings } = outcome;
|
|
916
|
+
appPath = prepared;
|
|
917
|
+
builtOn = outcome.machine;
|
|
918
|
+
offloadedTo = outcome.machine;
|
|
919
|
+
handoff = outcome.handoff ?? null;
|
|
920
|
+
stats.setPlacement({
|
|
921
|
+
decision: "offloaded",
|
|
922
|
+
machine: outcome.machine,
|
|
923
|
+
reason: `${candidate.reason}${placementLoad(choice)}`,
|
|
924
|
+
buildMs: timings.totalMs
|
|
925
|
+
});
|
|
926
|
+
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)}`);
|
|
927
|
+
compilationCache = outcome.compilationCache;
|
|
928
|
+
phase("cache", `compilation cache on ${outcome.machine} ${compilationCacheActivityLine(compilationCache)}`);
|
|
929
|
+
logWriter().write({
|
|
930
|
+
src: "build",
|
|
931
|
+
level: "info",
|
|
932
|
+
event: "offload_done",
|
|
933
|
+
msg: `built on ${outcome.machine}`,
|
|
934
|
+
timings
|
|
935
|
+
});
|
|
936
|
+
return true;
|
|
937
|
+
}
|
|
824
938
|
async function buildArtifact() {
|
|
825
939
|
buildFailure = {
|
|
826
940
|
fingerprint,
|
|
827
941
|
cacheKey,
|
|
828
942
|
cacheHit,
|
|
829
|
-
cacheSkipped: !useBuildCache
|
|
943
|
+
cacheSkipped: !useBuildCache,
|
|
944
|
+
buildMachine
|
|
830
945
|
};
|
|
831
946
|
if (!appPath) {
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
max: maxBuilds,
|
|
835
|
-
root,
|
|
836
|
-
logFile,
|
|
837
|
-
out: note
|
|
838
|
-
});
|
|
839
|
-
} catch (e) {
|
|
840
|
-
const refusal = (0, ownership_claim_exports.claimFailure)(e, "stim ios");
|
|
841
|
-
if (refusal) fail({
|
|
842
|
-
code: refusal.code,
|
|
843
|
-
message: refusal.message,
|
|
844
|
-
remedy: refusal.remedy,
|
|
845
|
-
build: buildFailure
|
|
846
|
-
});
|
|
847
|
-
note(chalk.yellow(phaseLine("build", `could not take a build slot: ${e?.message || e}; building anyway`)));
|
|
848
|
-
}
|
|
947
|
+
const offload = placeBuild();
|
|
948
|
+
if (!offload) await takeBuildSlot();
|
|
849
949
|
const mutatingSteps = [];
|
|
850
950
|
const rekeyedBy = [];
|
|
851
951
|
const prebuild = d.planPrebuild(root, "ios", {
|
|
@@ -857,6 +957,7 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
|
|
|
857
957
|
...staleNativeDirRefusal("ios"),
|
|
858
958
|
build: buildFailure
|
|
859
959
|
});
|
|
960
|
+
reportPendingRecheck(prebuild);
|
|
860
961
|
if (prebuild === "generate" || prebuild === "regenerate") {
|
|
861
962
|
step("prebuild");
|
|
862
963
|
recordPrebuild(root, "ios", null);
|
|
@@ -887,7 +988,8 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
|
|
|
887
988
|
});
|
|
888
989
|
}
|
|
889
990
|
const podState = d.readPodState(root);
|
|
890
|
-
const
|
|
991
|
+
const verdict = d.podsAreStale(podState.lockText, podState.manifestText);
|
|
992
|
+
const action = podAction(podState, verdict);
|
|
891
993
|
if (action.install) {
|
|
892
994
|
step("pods");
|
|
893
995
|
const result = await d.runPodInstall(root, logWriter(), { estimateMs: estimates().podsMs });
|
|
@@ -943,6 +1045,7 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
|
|
|
943
1045
|
if (prepared) {
|
|
944
1046
|
appPath = prepared;
|
|
945
1047
|
cacheHit = "local";
|
|
1048
|
+
lateHit();
|
|
946
1049
|
phase("cache", `hit ${shortHash(storeHash)} (post-${mutatingSteps.join("/")} key)`);
|
|
947
1050
|
if (releasedWait) {
|
|
948
1051
|
waitedForBuild = releasedWait.facts;
|
|
@@ -952,15 +1055,42 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
|
|
|
952
1055
|
}
|
|
953
1056
|
}
|
|
954
1057
|
}
|
|
955
|
-
if (!appPath) {
|
|
1058
|
+
if (offload && !appPath) {
|
|
956
1059
|
explainMiss(rekeyedBy);
|
|
957
1060
|
step("compile");
|
|
1061
|
+
if (!storeKey) fallBack("no cache key to store the app under", "offload failed: no cache key to store the app under");
|
|
1062
|
+
const choice = storeKey ? await chooseMachine(offload) : null;
|
|
1063
|
+
if (!choice || !await compileElsewhere({
|
|
1064
|
+
choice,
|
|
1065
|
+
candidate: offload
|
|
1066
|
+
})) await takeBuildSlot();
|
|
1067
|
+
}
|
|
1068
|
+
if (!appPath) {
|
|
1069
|
+
if (!offload) explainMiss(rekeyedBy);
|
|
1070
|
+
step("compile");
|
|
1071
|
+
if (offload) stats.setPlacement({
|
|
1072
|
+
decision: "fell-back",
|
|
1073
|
+
slotWaitMs,
|
|
1074
|
+
reason: offloadFallback ?? "offload failed",
|
|
1075
|
+
...fallbackMachine ? { machine: fallbackMachine } : {}
|
|
1076
|
+
});
|
|
1077
|
+
else stats.setPlacement({
|
|
1078
|
+
decision: "here",
|
|
1079
|
+
reason: hereReason,
|
|
1080
|
+
slotWaitMs
|
|
1081
|
+
});
|
|
1082
|
+
builtOn = "here";
|
|
1083
|
+
buildFailure = {
|
|
1084
|
+
...buildFailure,
|
|
1085
|
+
builtOn
|
|
1086
|
+
};
|
|
958
1087
|
phase("build", `compiling ${configuration || "Debug"} with xcodebuild`);
|
|
959
1088
|
const result = await d.buildIos({
|
|
960
1089
|
root,
|
|
961
1090
|
scheme: buildScheme,
|
|
962
1091
|
udid,
|
|
963
1092
|
destination: remoteDestination ? GENERIC_SIM_DESTINATION : null,
|
|
1093
|
+
arch: remoteDestination ? simulatorArch : null,
|
|
964
1094
|
...physical ? { sdk: IPHONEOS_SDK } : {},
|
|
965
1095
|
logWriter: logWriter(),
|
|
966
1096
|
...configuration ? { configuration } : {},
|
|
@@ -1023,7 +1153,7 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
|
|
|
1023
1153
|
projectRoot: root,
|
|
1024
1154
|
fingerprintHash: storeHash,
|
|
1025
1155
|
buildPath: appPath,
|
|
1026
|
-
runOptions:
|
|
1156
|
+
runOptions: providerRunOptions
|
|
1027
1157
|
});
|
|
1028
1158
|
}
|
|
1029
1159
|
}
|
|
@@ -1033,11 +1163,15 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
|
|
|
1033
1163
|
await resolveInitialFingerprint();
|
|
1034
1164
|
if (!easBuild) {
|
|
1035
1165
|
remote = await resolveRemoteArtifact();
|
|
1166
|
+
if (!appPath) miss(reasonForMiss([]).reason);
|
|
1036
1167
|
await awaitSharedBuild();
|
|
1037
1168
|
await prepareCachedArtifact();
|
|
1169
|
+
if (appPath) lateHit();
|
|
1170
|
+
else if (swapFellBack) miss(reasonForMiss([]).reason);
|
|
1038
1171
|
await buildArtifact();
|
|
1039
1172
|
}
|
|
1040
1173
|
const artifact = {
|
|
1174
|
+
handoff,
|
|
1041
1175
|
path: appPath,
|
|
1042
1176
|
bundleId,
|
|
1043
1177
|
cache: {
|
|
@@ -1047,6 +1181,10 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
|
|
|
1047
1181
|
} : null,
|
|
1048
1182
|
hit: cacheHit,
|
|
1049
1183
|
providerName: remote?.name ?? providerName,
|
|
1184
|
+
buildMachine,
|
|
1185
|
+
...builtOn ? { builtOn } : {},
|
|
1186
|
+
offloadedTo,
|
|
1187
|
+
offloadFallback: offloadedTo ? null : offloadFallback,
|
|
1050
1188
|
readEnabled: useBuildCache,
|
|
1051
1189
|
missReason: cacheHit ? null : missReason,
|
|
1052
1190
|
waitedForBuild,
|
|
@@ -1054,6 +1192,8 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
|
|
|
1054
1192
|
},
|
|
1055
1193
|
failureFields: {
|
|
1056
1194
|
...buildFailure,
|
|
1195
|
+
buildMachine,
|
|
1196
|
+
...builtOn ? { builtOn } : {},
|
|
1057
1197
|
fingerprint: storeHash,
|
|
1058
1198
|
cacheKey: storeKey,
|
|
1059
1199
|
cacheHit,
|
|
@@ -1076,6 +1216,23 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
|
|
|
1076
1216
|
artifact
|
|
1077
1217
|
};
|
|
1078
1218
|
} catch (error) {
|
|
1219
|
+
if (error instanceof OffloadRefusal) {
|
|
1220
|
+
const refusal = error;
|
|
1221
|
+
return {
|
|
1222
|
+
ok: false,
|
|
1223
|
+
failure: {
|
|
1224
|
+
code: refusal.code,
|
|
1225
|
+
message: refusal.message,
|
|
1226
|
+
remedy: refusal.remedy,
|
|
1227
|
+
build: {
|
|
1228
|
+
...buildFailure,
|
|
1229
|
+
buildMachine,
|
|
1230
|
+
...builtOn ? { builtOn } : {}
|
|
1231
|
+
}
|
|
1232
|
+
},
|
|
1233
|
+
compilationCache
|
|
1234
|
+
};
|
|
1235
|
+
}
|
|
1079
1236
|
if (error instanceof ArtifactRefusal) return {
|
|
1080
1237
|
ok: false,
|
|
1081
1238
|
failure: error.failure,
|
|
@@ -1083,12 +1240,248 @@ async function acquireIosArtifact({ root, logFile, udid, remoteDestination, devi
|
|
|
1083
1240
|
};
|
|
1084
1241
|
throw error;
|
|
1085
1242
|
} finally {
|
|
1243
|
+
if (openOffload.choice) closeOffload(openOffload.choice);
|
|
1086
1244
|
releaseLock();
|
|
1087
1245
|
releaseSlot();
|
|
1088
1246
|
if (!transferred) releaseArtifact();
|
|
1089
1247
|
}
|
|
1090
1248
|
}
|
|
1091
1249
|
//#endregion
|
|
1250
|
+
//#region src/commands/ios/remote.ts
|
|
1251
|
+
function resolveIosRemote({ opts, settings, physical, recorded }) {
|
|
1252
|
+
const target = opts.remote !== void 0 ? parseIosRemote(opts.remote) : remoteIosSetting(settings);
|
|
1253
|
+
if (target?.kind === "machine" && target.machine === "auto") return { failure: {
|
|
1254
|
+
code: "STIM_BAD_ARG",
|
|
1255
|
+
message: "stim ios --remote auto is not available yet: automatic placement has not shipped. Name a hosting Mac from hosting.machines."
|
|
1256
|
+
} };
|
|
1257
|
+
if (physical && target?.kind === "machine") return { failure: {
|
|
1258
|
+
code: "STIM_BAD_ARG",
|
|
1259
|
+
message: "--device builds for a phone connected to this machine, and --remote installs on a remote one.",
|
|
1260
|
+
remedy: "Pass only one of --device and --remote; unset ios.remote for a local phone."
|
|
1261
|
+
} };
|
|
1262
|
+
const machine = target?.kind === "machine" ? target.machine : null;
|
|
1263
|
+
if (recorded && recorded.machine !== machine) return { failure: {
|
|
1264
|
+
code: "STIM_BAD_ARG",
|
|
1265
|
+
message: `This workspace's iOS simulator runs on ${recorded.machine}; run stim stop first.`
|
|
1266
|
+
} };
|
|
1267
|
+
return {
|
|
1268
|
+
machine,
|
|
1269
|
+
backend: !physical && target?.kind === "backend" ? target.backend : null,
|
|
1270
|
+
recorded
|
|
1271
|
+
};
|
|
1272
|
+
}
|
|
1273
|
+
function selectIosTarget({ root, slot, opts, settings, physical, release, metroCheck, d }) {
|
|
1274
|
+
let recorded;
|
|
1275
|
+
try {
|
|
1276
|
+
recorded = d.readHostedIos(root, slot)[slot];
|
|
1277
|
+
} catch (error) {
|
|
1278
|
+
return { failure: {
|
|
1279
|
+
code: "STIM_HOSTING_REFUSED",
|
|
1280
|
+
message: error.message,
|
|
1281
|
+
...error instanceof Error && "remedy" in error && typeof error.remedy === "string" ? { remedy: error.remedy } : {}
|
|
1282
|
+
} };
|
|
1283
|
+
}
|
|
1284
|
+
const selection = resolveIosRemote({
|
|
1285
|
+
opts,
|
|
1286
|
+
settings,
|
|
1287
|
+
physical,
|
|
1288
|
+
recorded
|
|
1289
|
+
});
|
|
1290
|
+
if ("failure" in selection) return selection;
|
|
1291
|
+
const localIos = selection.machine ? deviceSlotPlatforms(d.getProject(root), slot)?.ios : void 0;
|
|
1292
|
+
if (selection.machine && localIos?.owned && localIos.deviceUdid) {
|
|
1293
|
+
let localRunning = true;
|
|
1294
|
+
try {
|
|
1295
|
+
localRunning = d.listAllIosSims({ timeoutMs: 5e3 }).some((sim) => sim.udid === localIos.deviceUdid && sim.state !== "Shutdown");
|
|
1296
|
+
} catch {}
|
|
1297
|
+
if (localRunning) return { failure: {
|
|
1298
|
+
code: "STIM_BAD_ARG",
|
|
1299
|
+
message: `This workspace's iOS simulator for slot ${slot} runs on this Mac; run stim stop first.`
|
|
1300
|
+
} };
|
|
1301
|
+
}
|
|
1302
|
+
if (selection.machine && !release && !metroCheck) return { failure: {
|
|
1303
|
+
code: "STIM_BAD_ARG",
|
|
1304
|
+
message: "Hosted Debug runs require the local Metro supervisor; --no-metro-check cannot be used.",
|
|
1305
|
+
remedy: "Run stim stop; stim start, then retry without --no-metro-check."
|
|
1306
|
+
} };
|
|
1307
|
+
return selection;
|
|
1308
|
+
}
|
|
1309
|
+
function hostedIosSelectors(deviceType, runtime) {
|
|
1310
|
+
return {
|
|
1311
|
+
...deviceType ? { deviceType } : {},
|
|
1312
|
+
...runtime ? { runtime } : {}
|
|
1313
|
+
};
|
|
1314
|
+
}
|
|
1315
|
+
async function connectIosTarget(selection, selectors, d) {
|
|
1316
|
+
if (!selection.machine) return { target: null };
|
|
1317
|
+
try {
|
|
1318
|
+
return { target: await d.prepareHostedIos(selection.machine, selectors, selection.recorded) };
|
|
1319
|
+
} catch (error) {
|
|
1320
|
+
return { failure: {
|
|
1321
|
+
code: error instanceof Error && "code" in error && typeof error.code === "string" ? error.code : "STIM_HOSTING_REFUSED",
|
|
1322
|
+
message: error instanceof Error ? error.message : String(error),
|
|
1323
|
+
...error instanceof Error && "remedy" in error && typeof error.remedy === "string" ? { remedy: error.remedy } : {}
|
|
1324
|
+
} };
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
function hostedIosBuildTarget(target) {
|
|
1328
|
+
return target ? { hostedDestination: {
|
|
1329
|
+
runtime: target.choice.runtimeId,
|
|
1330
|
+
architecture: target.choice.architecture
|
|
1331
|
+
} } : {};
|
|
1332
|
+
}
|
|
1333
|
+
async function iosPlacementBudget(d, root, note, hosted) {
|
|
1334
|
+
return hosted ? {
|
|
1335
|
+
reclaimed: [],
|
|
1336
|
+
refusal: null
|
|
1337
|
+
} : d.budgetGate({
|
|
1338
|
+
root,
|
|
1339
|
+
note
|
|
1340
|
+
});
|
|
1341
|
+
}
|
|
1342
|
+
function hostedIosMetroNote(hosted, settings, note) {
|
|
1343
|
+
if (hosted && settings.iosSimulatorApp !== void 0) note(chalk.dim(phaseLine("device", "iosSimulatorApp is ignored on a hosting Mac; the simulator boots headless.")));
|
|
1344
|
+
if (hosted && (publicUrlSetting(settings) || tunnelModeSetting(settings))) note(chalk.dim(phaseLine("metro", "metro.publicUrl and metro.tunnel are ignored on a hosting Mac; Metro uses the private tailnet bridge.")));
|
|
1345
|
+
}
|
|
1346
|
+
async function connectIosBackend(root, backend, deviceType, d) {
|
|
1347
|
+
if (!backend) return {
|
|
1348
|
+
remote: null,
|
|
1349
|
+
arch: null
|
|
1350
|
+
};
|
|
1351
|
+
const resolved = await d.resolveRemoteContext({
|
|
1352
|
+
root,
|
|
1353
|
+
backend,
|
|
1354
|
+
easBin: d.resolveEasCliBin(root)?.file ?? null,
|
|
1355
|
+
deviceType: deviceType?.trim() || null
|
|
1356
|
+
});
|
|
1357
|
+
if ("failed" in resolved) return { failure: {
|
|
1358
|
+
code: resolved.code ?? "STIM_NO_REMOTE_SESSION",
|
|
1359
|
+
message: resolved.failed,
|
|
1360
|
+
remedy: resolved.remedy
|
|
1361
|
+
} };
|
|
1362
|
+
return {
|
|
1363
|
+
remote: d.remoteIosDeps(resolved.ctx),
|
|
1364
|
+
arch: await d.readRemoteSimulatorArch(resolved.ctx.existingDaemon)
|
|
1365
|
+
};
|
|
1366
|
+
}
|
|
1367
|
+
function iosMetroSettings(settings, hosted) {
|
|
1368
|
+
return hosted ? {
|
|
1369
|
+
...settings,
|
|
1370
|
+
ios: {
|
|
1371
|
+
...isJsonObject(settings.ios) ? settings.ios : {},
|
|
1372
|
+
remote: void 0
|
|
1373
|
+
},
|
|
1374
|
+
android: {
|
|
1375
|
+
...isJsonObject(settings.android) ? settings.android : {},
|
|
1376
|
+
remote: void 0
|
|
1377
|
+
},
|
|
1378
|
+
metro: {
|
|
1379
|
+
...isJsonObject(settings.metro) ? settings.metro : {},
|
|
1380
|
+
tunnel: void 0,
|
|
1381
|
+
publicUrl: void 0,
|
|
1382
|
+
ngrokUrl: void 0
|
|
1383
|
+
}
|
|
1384
|
+
} : settings;
|
|
1385
|
+
}
|
|
1386
|
+
//#endregion
|
|
1387
|
+
//#region src/commands/ios/hosted.ts
|
|
1388
|
+
async function finishHostedIosRun({ target, d, artifact, root, slot, release, isExpo, metroCheck, metroPort, logsDir, fail, note, selectors, ...report }) {
|
|
1389
|
+
const bundleId = artifact.bundleId ?? (d.readBundleId(artifact.path) || d.detectBundleId(root));
|
|
1390
|
+
if (!bundleId) return fail({
|
|
1391
|
+
code: "STIM_INSTALL_FAILED",
|
|
1392
|
+
message: "The built app has no bundle identifier."
|
|
1393
|
+
});
|
|
1394
|
+
const launchedAt = d.now();
|
|
1395
|
+
let run;
|
|
1396
|
+
try {
|
|
1397
|
+
run = await d.placeHostedIos(target, {
|
|
1398
|
+
root,
|
|
1399
|
+
slot,
|
|
1400
|
+
bundle: artifact.path,
|
|
1401
|
+
handoff: artifact.handoff,
|
|
1402
|
+
bundleId,
|
|
1403
|
+
selectors,
|
|
1404
|
+
release,
|
|
1405
|
+
...!release && isExpo ? { devClientScheme: d.devClientScheme(root, artifact.path) ?? void 0 } : {},
|
|
1406
|
+
reserved: (placement) => d.writeHostedIos(root, slot, placement),
|
|
1407
|
+
note
|
|
1408
|
+
});
|
|
1409
|
+
} catch (error) {
|
|
1410
|
+
return fail({
|
|
1411
|
+
code: error.code ?? "STIM_HOSTING_REFUSED",
|
|
1412
|
+
message: error.message,
|
|
1413
|
+
remedy: error instanceof Error && "remedy" in error && typeof error.remedy === "string" ? error.remedy : `Retry stim ios --remote ${target.host.machine}, or run stim stop to reconcile any recorded placement.`
|
|
1414
|
+
});
|
|
1415
|
+
}
|
|
1416
|
+
d.writeHostedIos(root, slot, run.placement);
|
|
1417
|
+
d.writeWorkspaceLaunch(root, "ios", {
|
|
1418
|
+
appId: bundleId,
|
|
1419
|
+
deviceId: run.placement.session,
|
|
1420
|
+
metroPort,
|
|
1421
|
+
release,
|
|
1422
|
+
launchedAt: new Date(launchedAt).toISOString()
|
|
1423
|
+
}, slot);
|
|
1424
|
+
let launched = run.launched;
|
|
1425
|
+
if (!release && metroCheck) {
|
|
1426
|
+
const evidence = await d.verifyLaunch({
|
|
1427
|
+
requireBundleResponse: true,
|
|
1428
|
+
platform: "ios",
|
|
1429
|
+
logsDir,
|
|
1430
|
+
since: launchedAt,
|
|
1431
|
+
metroPort,
|
|
1432
|
+
mode: isExpo ? MODE_EXPO : MODE_BARE,
|
|
1433
|
+
slot: launchSlotScope(root, slot),
|
|
1434
|
+
platformShared: siblingPlatformSlots(root, "ios", slot).length > 0
|
|
1435
|
+
});
|
|
1436
|
+
if (evidence.fatal) return fail({
|
|
1437
|
+
code: "STIM_LAUNCH_FAILED",
|
|
1438
|
+
message: "Metro could not deliver the hosted app bundle.",
|
|
1439
|
+
remedy: "Run stim logs --errors and fix the bundle error."
|
|
1440
|
+
});
|
|
1441
|
+
launched = evidence.verified ? true : evidence.requested ? "bundling" : "unverified";
|
|
1442
|
+
}
|
|
1443
|
+
if (launched === "unverified") note(`Launch on ${target.host.machine} is unverified; check the hosted app, then retry stim ios --remote ${target.host.machine}.`);
|
|
1444
|
+
const cache = artifact.cache;
|
|
1445
|
+
const device = run.placement.device;
|
|
1446
|
+
const uploadsAbandoned = await artifact.completeUploads();
|
|
1447
|
+
return {
|
|
1448
|
+
facts: reportIosResult({
|
|
1449
|
+
...report,
|
|
1450
|
+
root,
|
|
1451
|
+
slot,
|
|
1452
|
+
release,
|
|
1453
|
+
metroCheck,
|
|
1454
|
+
metroPort,
|
|
1455
|
+
logsDir,
|
|
1456
|
+
device: {
|
|
1457
|
+
deviceName: device.deviceType,
|
|
1458
|
+
deviceType: device.deviceType,
|
|
1459
|
+
runtime: device.runtime
|
|
1460
|
+
},
|
|
1461
|
+
udid: "",
|
|
1462
|
+
host: hostedIosStatus(run.placement),
|
|
1463
|
+
appPath: artifact.path,
|
|
1464
|
+
bundleId,
|
|
1465
|
+
installSkipped: false,
|
|
1466
|
+
storeHash: cache.identity?.fingerprint ?? null,
|
|
1467
|
+
storeKey: cache.identity?.key ?? null,
|
|
1468
|
+
cacheHit: cache.hit,
|
|
1469
|
+
missReason: cache.missReason,
|
|
1470
|
+
compilationCache: cache.compilation,
|
|
1471
|
+
useBuildCache: cache.readEnabled,
|
|
1472
|
+
waitedForBuild: cache.waitedForBuild,
|
|
1473
|
+
launchState: launched,
|
|
1474
|
+
providerName: cache.providerName,
|
|
1475
|
+
buildMachine: cache.buildMachine,
|
|
1476
|
+
builtOn: cache.builtOn,
|
|
1477
|
+
offloadedTo: cache.offloadedTo,
|
|
1478
|
+
offloadFallback: cache.offloadFallback,
|
|
1479
|
+
webPreviewUrl: null
|
|
1480
|
+
}),
|
|
1481
|
+
uploadsAbandoned
|
|
1482
|
+
};
|
|
1483
|
+
}
|
|
1484
|
+
//#endregion
|
|
1092
1485
|
//#region src/engine/ios-profile.ts
|
|
1093
1486
|
const TAG = /<(\/?)\s*([A-Za-z0-9_]+)(?:\s[^>]*?)?(\/?)>/g;
|
|
1094
1487
|
const ENTITIES = {
|
|
@@ -1587,7 +1980,7 @@ function hostLanCandidates(interfaces = networkInterfaces) {
|
|
|
1587
1980
|
//#endregion
|
|
1588
1981
|
//#region src/commands/ios/collector.ts
|
|
1589
1982
|
function collectorLogFile(root, slot) {
|
|
1590
|
-
return join(workspaceLogsDir(root), `collector-${deviceSlotFileKey("ios", slot)}.log`);
|
|
1983
|
+
return join(workspaceLogsDir$1(root), `collector-${deviceSlotFileKey("ios", slot)}.log`);
|
|
1591
1984
|
}
|
|
1592
1985
|
function collectorEntry() {
|
|
1593
1986
|
return spawnEntry("collector-run");
|
|
@@ -1648,7 +2041,7 @@ async function replaceCollector({ root, slot = "default", udid, bundleId, appNam
|
|
|
1648
2041
|
if (payloadUrl) args.push("--payload-url", payloadUrl);
|
|
1649
2042
|
let stdio = "ignore";
|
|
1650
2043
|
try {
|
|
1651
|
-
mkdirSync(workspaceLogsDir(root), { recursive: true });
|
|
2044
|
+
mkdirSync(workspaceLogsDir$1(root), { recursive: true });
|
|
1652
2045
|
const fd = openSync(collectorLogFile(root, slot), "a");
|
|
1653
2046
|
stdio = [
|
|
1654
2047
|
"ignore",
|
|
@@ -1680,23 +2073,34 @@ async function replaceCollector({ root, slot = "default", udid, bundleId, appNam
|
|
|
1680
2073
|
//#endregion
|
|
1681
2074
|
//#region src/commands/ios/dependencies.ts
|
|
1682
2075
|
const DEFAULT_DEPS = {
|
|
2076
|
+
prepareHostedIos,
|
|
2077
|
+
placeHostedIos,
|
|
2078
|
+
readHostedIos,
|
|
2079
|
+
writeHostedIos,
|
|
1683
2080
|
resolveEasDevelopmentBuild,
|
|
1684
|
-
findProjectRoot,
|
|
2081
|
+
findProjectRoot: findProjectRoot$1,
|
|
1685
2082
|
resolveSettings,
|
|
2083
|
+
settingsLayers,
|
|
2084
|
+
settingOriginScope,
|
|
1686
2085
|
gitCommonDir,
|
|
1687
2086
|
repoRoot,
|
|
1688
2087
|
detectBundleId,
|
|
1689
2088
|
detectIsExpo,
|
|
1690
2089
|
devClientScheme,
|
|
2090
|
+
devClientTakesDevMenuParams,
|
|
1691
2091
|
getProject,
|
|
1692
2092
|
upsertProject,
|
|
2093
|
+
recordIosSchemeApprovals,
|
|
1693
2094
|
checkDeviceCapacity,
|
|
1694
2095
|
budgetGate,
|
|
1695
2096
|
ensureOwnedDevice,
|
|
1696
2097
|
listIosRuntimes,
|
|
2098
|
+
listAllIosSims,
|
|
1697
2099
|
ensureBooted,
|
|
1698
2100
|
resolveRemoteContext,
|
|
1699
2101
|
remoteIosDeps,
|
|
2102
|
+
readRemoteSimulatorArch,
|
|
2103
|
+
hostSimulatorArch,
|
|
1700
2104
|
ensureMetroReachable,
|
|
1701
2105
|
ensureRemoteBootOwned,
|
|
1702
2106
|
detectProviders,
|
|
@@ -1748,6 +2152,7 @@ const DEFAULT_DEPS = {
|
|
|
1748
2152
|
swapJsBundle,
|
|
1749
2153
|
installIosApp,
|
|
1750
2154
|
clearOtherUserApps,
|
|
2155
|
+
clearIosAppData,
|
|
1751
2156
|
clearIosAdoptionPending,
|
|
1752
2157
|
launchIosApp,
|
|
1753
2158
|
verifyLaunch,
|
|
@@ -1922,7 +2327,7 @@ function reportUnverified({ verification, siblings, slot, phase, note, metroPort
|
|
|
1922
2327
|
waitedMs: verification.waitedMs,
|
|
1923
2328
|
bundleId,
|
|
1924
2329
|
udid,
|
|
1925
|
-
devClientUrl: scheme ? devClientUrl(scheme, metroPort ?? 8081, lanAddress ?? void 0) : null,
|
|
2330
|
+
devClientUrl: scheme ? launched?.url ?? devClientUrl(scheme, metroPort ?? 8081, lanAddress ?? void 0) : null,
|
|
1926
2331
|
mode: isExpo ? MODE_EXPO : MODE_BARE,
|
|
1927
2332
|
remote: remoteDevice,
|
|
1928
2333
|
physical,
|
|
@@ -1949,20 +2354,30 @@ function installsOverWifi(d, udid, selectedWireless, note) {
|
|
|
1949
2354
|
if (wireless) note(phaseLine("device", `${current?.name ?? udid} is paired over Wi-Fi, so the install and launch go over the network and take longer than over a cable`));
|
|
1950
2355
|
return wireless;
|
|
1951
2356
|
}
|
|
1952
|
-
function cleanAdoptedIosApps({ d,
|
|
2357
|
+
function cleanAdoptedIosApps({ d, udid, bundleId, devClient, phase, note }) {
|
|
1953
2358
|
const swept = d.clearOtherUserApps({
|
|
1954
2359
|
udid,
|
|
1955
2360
|
keep: bundleId
|
|
1956
2361
|
});
|
|
1957
2362
|
if (swept.removed.length) phase("install", `removed ${swept.removed.join(", ")}, left by the previous workspace`);
|
|
1958
2363
|
if (swept.listed && swept.failed.length === 0) {
|
|
1959
|
-
|
|
2364
|
+
if (swept.kept && bundleId) {
|
|
2365
|
+
try {
|
|
2366
|
+
d.clearIosAppData(udid, bundleId, { defaultsPlist: devClient ? IOS_DEV_MENU_OFF_DEFAULTS_PLIST : null });
|
|
2367
|
+
} catch (error) {
|
|
2368
|
+
return `Could not clear the data ${bundleId} kept from the previous workspace: ${String(error?.message || error)}`;
|
|
2369
|
+
}
|
|
2370
|
+
phase("install", `cleared ${bundleId} data left by the previous workspace`);
|
|
2371
|
+
}
|
|
1960
2372
|
return null;
|
|
1961
2373
|
}
|
|
1962
2374
|
if (!swept.listed) return "Could not list apps left by the previous workspace.";
|
|
1963
2375
|
for (const failure of swept.failed) note(chalk.yellow(phaseLine("install", `could not remove ${failure}, left by the previous workspace`)));
|
|
1964
2376
|
return `Could not remove ${swept.failed.join(", ")}, left by the previous workspace.`;
|
|
1965
2377
|
}
|
|
2378
|
+
function installMayBeProven(adopting, parkedCacheKey, storeKey) {
|
|
2379
|
+
return !adopting || !parkedCacheKey || parkedCacheKey === storeKey;
|
|
2380
|
+
}
|
|
1966
2381
|
function resolveRunBundleId(d, root, appPath, bundleId) {
|
|
1967
2382
|
if (!appPath || bundleId) return bundleId;
|
|
1968
2383
|
return d.readBundleId(appPath) || d.detectBundleId(root);
|
|
@@ -1995,7 +2410,7 @@ function launchFailureRemedy(remoteDevice, udid, bundleId, logFile) {
|
|
|
1995
2410
|
}
|
|
1996
2411
|
async function finishIosRun({ artifact, d, root, slot, json, release, configuration, buildScheme, isExpo, metroCheck, metroPort, logsDir, logFile, device, udid, physical, wireless, lanAddress, lanOriginUrl, remoteDevice, bootPromise, bootDuration, fail, phase, note, logWriter, elapsed, startedAt, closeWriter, lease, releaseLease, recordRun, reclaimed, devServer, enterPhase }) {
|
|
1997
2412
|
const { path: appPath, bundleId: initialBundleId, failureFields: buildFailure, cache } = artifact;
|
|
1998
|
-
const { hit: cacheHit, providerName, readEnabled: useBuildCache, missReason, waitedForBuild, compilation: compilationCache } = cache;
|
|
2413
|
+
const { hit: cacheHit, providerName, buildMachine, builtOn, offloadedTo, offloadFallback, readEnabled: useBuildCache, missReason, waitedForBuild, compilation: compilationCache } = cache;
|
|
1999
2414
|
const storeHash = cache.identity?.fingerprint ?? null;
|
|
2000
2415
|
const storeKey = cache.identity?.key ?? null;
|
|
2001
2416
|
const runCommand = nativeRunCommand("ios", slot, {
|
|
@@ -2033,7 +2448,7 @@ async function finishIosRun({ artifact, d, root, slot, json, release, configurat
|
|
|
2033
2448
|
}
|
|
2034
2449
|
});
|
|
2035
2450
|
if (bundleId) d.upsertProject(root, { bundleId });
|
|
2036
|
-
enterPhase("
|
|
2451
|
+
enterPhase("device");
|
|
2037
2452
|
const booted = await bootPromise;
|
|
2038
2453
|
if (!booted?.ok) return fail({
|
|
2039
2454
|
code: booted?.code || "STIM_NO_DEVICE",
|
|
@@ -2043,6 +2458,7 @@ async function finishIosRun({ artifact, d, root, slot, json, release, configurat
|
|
|
2043
2458
|
const deviceOutcome = physical ? "connected" : `${device?.adopted ? "adopted" : "booted"} ${bootDuration()}`;
|
|
2044
2459
|
phase("device", `${deviceLabel(device, udid)} ${deviceOutcome}`);
|
|
2045
2460
|
const scheme = release ? void 0 : d.devClientScheme(root, appPath);
|
|
2461
|
+
const devMenuParams = d.devClientTakesDevMenuParams(root);
|
|
2046
2462
|
const appName = appNameFromPath(appPath);
|
|
2047
2463
|
const appExecutable = readRunExecutable(d, appPath, note);
|
|
2048
2464
|
const dropSwapDir = artifact.release;
|
|
@@ -2058,6 +2474,7 @@ async function finishIosRun({ artifact, d, root, slot, json, release, configurat
|
|
|
2058
2474
|
root,
|
|
2059
2475
|
note
|
|
2060
2476
|
});
|
|
2477
|
+
enterPhase("install");
|
|
2061
2478
|
const installTimer = stepTimer(d.now);
|
|
2062
2479
|
const installed = d.installIosDeviceApp({
|
|
2063
2480
|
udid,
|
|
@@ -2160,9 +2577,9 @@ async function finishIosRun({ artifact, d, root, slot, json, release, configurat
|
|
|
2160
2577
|
if (adopting) {
|
|
2161
2578
|
const cleanupFailure = cleanAdoptedIosApps({
|
|
2162
2579
|
d,
|
|
2163
|
-
root,
|
|
2164
2580
|
udid,
|
|
2165
2581
|
bundleId,
|
|
2582
|
+
devClient: Boolean(scheme),
|
|
2166
2583
|
phase,
|
|
2167
2584
|
note
|
|
2168
2585
|
});
|
|
@@ -2177,13 +2594,15 @@ async function finishIosRun({ artifact, d, root, slot, json, release, configurat
|
|
|
2177
2594
|
}
|
|
2178
2595
|
});
|
|
2179
2596
|
}
|
|
2597
|
+
enterPhase("install");
|
|
2180
2598
|
const installTimer = stepTimer(d.now);
|
|
2181
2599
|
const installed = d.installIosApp({
|
|
2182
2600
|
udid,
|
|
2183
2601
|
appPath,
|
|
2184
2602
|
bundleId,
|
|
2185
2603
|
devClientScheme: scheme,
|
|
2186
|
-
|
|
2604
|
+
schemeApprovals: device.schemeApprovals,
|
|
2605
|
+
proveInstalled: installMayBeProven(adopting, device?.parkedCacheKey, storeKey)
|
|
2187
2606
|
}, { now: d.now });
|
|
2188
2607
|
if (installed?.failed) return fail({
|
|
2189
2608
|
code: installed.code || "STIM_INSTALL_FAILED",
|
|
@@ -2195,10 +2614,12 @@ async function finishIosRun({ artifact, d, root, slot, json, release, configurat
|
|
|
2195
2614
|
bundleId
|
|
2196
2615
|
}
|
|
2197
2616
|
});
|
|
2617
|
+
if (adopting) d.clearIosAdoptionPending(root, slot);
|
|
2198
2618
|
installSkipped = Boolean(installed?.skipped);
|
|
2199
2619
|
const artifactDuration = installed?.artifactDurationMs === void 0 ? installTimer() : `(${formatDuration(installed.artifactDurationMs)})`;
|
|
2200
2620
|
phase("install", installSkipped ? `unchanged (${deviceShortName(device, udid)} already has this build) ${artifactDuration}` : `${basename(appPath)} -> ${deviceLabel(device, udid)} ${artifactDuration}`);
|
|
2201
2621
|
if (!remoteDevice && installed?.devClientPreparationDurationMs !== void 0) phase("install", `dev client prepared (${formatDuration(installed.devClientPreparationDurationMs)})`);
|
|
2622
|
+
if (installed.schemeApprovals) d.recordIosSchemeApprovals(root, udid, installed.schemeApprovals, slot);
|
|
2202
2623
|
dropSwapDir();
|
|
2203
2624
|
if (!remoteDevice) await d.replaceCollector({
|
|
2204
2625
|
root,
|
|
@@ -2230,6 +2651,7 @@ async function finishIosRun({ artifact, d, root, slot, json, release, configurat
|
|
|
2230
2651
|
bundleId,
|
|
2231
2652
|
metroPort,
|
|
2232
2653
|
devClientScheme: scheme,
|
|
2654
|
+
devMenuParams,
|
|
2233
2655
|
consolePaths: simulatorConsolePaths({
|
|
2234
2656
|
deviceId: udid,
|
|
2235
2657
|
appId: bundleId,
|
|
@@ -2360,12 +2782,20 @@ async function finishIosRun({ artifact, d, root, slot, json, release, configurat
|
|
|
2360
2782
|
launchState,
|
|
2361
2783
|
launchWarning,
|
|
2362
2784
|
providerName,
|
|
2785
|
+
buildMachine,
|
|
2786
|
+
builtOn,
|
|
2787
|
+
offloadedTo,
|
|
2788
|
+
offloadFallback,
|
|
2363
2789
|
closeWriter,
|
|
2364
2790
|
webPreviewUrl: remoteDevice?.webPreviewUrl() ?? null,
|
|
2365
2791
|
lease: physical ? leaseFacts : void 0,
|
|
2366
2792
|
recordRun,
|
|
2367
2793
|
reclaimed,
|
|
2368
|
-
devServer
|
|
2794
|
+
devServer,
|
|
2795
|
+
links: workspaceLinks(root, {
|
|
2796
|
+
platform: "ios",
|
|
2797
|
+
slot
|
|
2798
|
+
})
|
|
2369
2799
|
}),
|
|
2370
2800
|
uploadsAbandoned
|
|
2371
2801
|
};
|
|
@@ -2424,21 +2854,26 @@ async function planIos(opts, d, schemeRefusal) {
|
|
|
2424
2854
|
platform: "ios",
|
|
2425
2855
|
slot,
|
|
2426
2856
|
root,
|
|
2427
|
-
projectKey: statsProjectKey({
|
|
2857
|
+
projectKey: statsProjectKey$1({
|
|
2428
2858
|
root,
|
|
2429
2859
|
commonDir: settingsContext.gitCommonDir,
|
|
2430
2860
|
repoRoot: settingsContext.repoRoot
|
|
2431
2861
|
})
|
|
2432
2862
|
};
|
|
2433
2863
|
const isExpo = d.detectIsExpo(root);
|
|
2864
|
+
const settingsLayersForOrigin = d.settingsLayers(settingsContext);
|
|
2865
|
+
const iosRemote = remoteIosSetting(settings);
|
|
2434
2866
|
const modelRefusal = deviceModelRefusal({
|
|
2435
2867
|
slot,
|
|
2436
2868
|
deviceTypeFlag: opts.deviceType,
|
|
2437
2869
|
runtimeFlag: opts.runtime,
|
|
2438
2870
|
deviceType: resolveDeviceType(opts.deviceType, settings),
|
|
2439
2871
|
runtime: resolveRuntime(opts.runtime, settings),
|
|
2872
|
+
deviceTypeOrigin: d.settingOriginScope(settingsLayersForOrigin, "ios.deviceType"),
|
|
2873
|
+
runtimeOrigin: d.settingOriginScope(settingsLayersForOrigin, "ios.runtime"),
|
|
2440
2874
|
physical: false,
|
|
2441
|
-
remoteBackend:
|
|
2875
|
+
remoteBackend: iosRemote?.kind === "backend" ? iosRemote.backend : null,
|
|
2876
|
+
hosted: iosRemote?.kind === "machine",
|
|
2442
2877
|
listRuntimes: d.listIosRuntimes
|
|
2443
2878
|
});
|
|
2444
2879
|
if (modelRefusal) return refuse(modelRefusal);
|
|
@@ -2463,6 +2898,12 @@ async function planIos(opts, d, schemeRefusal) {
|
|
|
2463
2898
|
refusal: eas.missing
|
|
2464
2899
|
}), json);
|
|
2465
2900
|
}
|
|
2901
|
+
const remoteBackend = remoteIosSetting(settings);
|
|
2902
|
+
if (remoteBackend) return refuse({
|
|
2903
|
+
code: "STIM_BAD_ARG",
|
|
2904
|
+
message: `ios.remote routes this workspace's runs to a ${remoteBackend.kind === "backend" ? remoteBackend.backend : remoteBackend.machine} device, whose architecture --plan cannot read without a session.`,
|
|
2905
|
+
remedy: "Run `stim ios` to build for the remote device, or unset ios.remote to plan the owned simulator."
|
|
2906
|
+
});
|
|
2466
2907
|
const refusal = schemeRefusal(root, opts.scheme, isExpo);
|
|
2467
2908
|
if (refusal) return refuse({
|
|
2468
2909
|
code: refusal.code,
|
|
@@ -2483,10 +2924,17 @@ async function planIos(opts, d, schemeRefusal) {
|
|
|
2483
2924
|
message: `Could not fingerprint ${root}: @expo/fingerprint produced no hash for it.`,
|
|
2484
2925
|
remedy: "Check the project native inputs and the @expo/fingerprint error above, then retry."
|
|
2485
2926
|
});
|
|
2927
|
+
const arch = simulatorBuildArch({
|
|
2928
|
+
physical: false,
|
|
2929
|
+
remoteArch: null,
|
|
2930
|
+
hostArch: d.hostSimulatorArch(),
|
|
2931
|
+
configuration
|
|
2932
|
+
});
|
|
2486
2933
|
const cacheKey = buildCacheKey("ios", fingerprint.hash, {
|
|
2487
2934
|
scheme: opts.scheme,
|
|
2488
2935
|
...configuration ? { configuration } : {},
|
|
2489
2936
|
isSimulator: true,
|
|
2937
|
+
...arch ? { arch } : {},
|
|
2490
2938
|
...buildProfile ? { buildProfile } : {}
|
|
2491
2939
|
});
|
|
2492
2940
|
const plan = await planCachedBuild({
|
|
@@ -2497,7 +2945,7 @@ async function planIos(opts, d, schemeRefusal) {
|
|
|
2497
2945
|
cacheKey,
|
|
2498
2946
|
cachePolicy,
|
|
2499
2947
|
providerConfig: d.resolveCacheProviderConfig(settingsContext),
|
|
2500
|
-
expoRemote: cachePolicy.remote && !buildProfile && !opts.scheme ? { runOptions: configuration
|
|
2948
|
+
expoRemote: cachePolicy.remote && !buildProfile && !opts.scheme ? { runOptions: iosProviderRunOptions(configuration, arch) } : null
|
|
2501
2949
|
}, {
|
|
2502
2950
|
loadCacheProvider: d.loadCacheProvider,
|
|
2503
2951
|
loadProjectProvider: d.loadProjectProvider,
|
|
@@ -2520,9 +2968,9 @@ function iosCommand(program) {
|
|
|
2520
2968
|
registerIos(program);
|
|
2521
2969
|
}
|
|
2522
2970
|
function registerIos(program, deps = {}) {
|
|
2523
|
-
program.command("ios").description("Build (or restore from the fingerprint cache), install and launch this workspace's app on its owned simulator, 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("--scheme <name>", "Shared Xcode app scheme to build; overrides automatic scheme selection, not the app URL scheme").option("--no-metro-check", "Skip the \"is this workspace's dev server running?\" check: do not start it, and build anyway").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("--configuration <name>", "Xcode configuration to build (e.g. Release). A non-Debug configuration embeds the JS bundle and skips Metro entirely. Overrides the ios.configuration setting. Default: Debug").option("--device-type <name>", "Simulator model to create this workspace's owned sim as, exactly as `xcrun simctl list devicetypes` names it (e.g. \"iPad Pro 13-inch (M5)\"). Overrides the ios.deviceType setting for this invocation. A model no installed runtime can create refuses with STIM_BAD_ARG and prints the models they do offer.").option("--runtime <version>", "Simulator runtime to create this workspace's owned sim on, as a version (\"18.5\") or a runtime's full name (\"iOS 18.5\"); nothing else matches. Overrides the ios.runtime setting for this invocation. An unknown version refuses with STIM_BAD_ARG and prints the installed runtimes.").option("--simulator-app <app>", "Open the owned simulator in xcode, siniulator, or stim-desktop for this run, overriding the machine iosSimulatorApp setting. Also opens an already running simulator; local simulators only.").option("--device [udid]", "Build the iphoneos slice for a connected iPhone, install it, and launch it, instead of using this workspace's owned simulator. The phone can be cabled or paired over Wi-Fi. With no UDID, the first cabled device this workspace can lease is used, waiting for one that is busy; a Wi-Fi device is used only when no cabled one is connected and ready. In Debug the app is wired to this workspace's Metro over the LAN. Stim never creates, boots, or deletes a physical device.").option("--remote <
|
|
2524
|
-
if (
|
|
2525
|
-
throw new InvalidArgumentError(
|
|
2971
|
+
program.command("ios").description("Build (or restore from the fingerprint cache), install and launch this workspace's app on its owned simulator, 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("--scheme <name>", "Shared Xcode app scheme to build; overrides automatic scheme selection, not the app URL scheme").option("--no-metro-check", "Skip the \"is this workspace's dev server running?\" check: do not start it, and build anyway").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("--configuration <name>", "Xcode configuration to build (e.g. Release). A non-Debug configuration embeds the JS bundle and skips Metro entirely. Overrides the ios.configuration setting. Default: Debug").option("--device-type <name>", "Simulator model to create this workspace's owned sim as, exactly as `xcrun simctl list devicetypes` names it (e.g. \"iPad Pro 13-inch (M5)\"). Overrides the ios.deviceType setting for this invocation. A model no installed runtime can create refuses with STIM_BAD_ARG and prints the models they do offer.").option("--runtime <version>", "Simulator runtime to create this workspace's owned sim on, as a version (\"18.5\") or a runtime's full name (\"iOS 18.5\"); nothing else matches. Overrides the ios.runtime setting for this invocation. An unknown version refuses with STIM_BAD_ARG and prints the installed runtimes.").option("--simulator-app <app>", "Open the owned simulator in xcode, siniulator, or stim-desktop for this run, overriding the machine iosSimulatorApp setting. Also opens an already running simulator; local simulators only.").option("--device [udid]", "Build the iphoneos slice for a connected iPhone, install it, and launch it, instead of using this workspace's owned simulator. The phone can be cabled or paired over Wi-Fi. With no UDID, the first cabled device this workspace can lease is used, waiting for one that is busy; a Wi-Fi device is used only when no cabled one is connected and ready. In Debug the app is wired to this workspace's Metro over the LAN. Stim never creates, boots, or deletes a physical device.").option("--remote <target>", "Run on eas, proxy, or an approved Mac in hosting.machines; named Macs never fall back locally. auto is not available yet.", (value) => {
|
|
2972
|
+
if (parseMachine(value)) return value.trim();
|
|
2973
|
+
throw new InvalidArgumentError("expected eas, proxy, auto, or a hosting Mac name");
|
|
2526
2974
|
}).option("--wait <seconds>", "How long to wait for another workspace to release the phone it leases, before refusing with STIM_DEVICE_BUSY (default 60, 0 refuses at once). Only with --device.").option("--no-wait", "Install on a phone 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) => {
|
|
2527
2975
|
if (opts.plan) {
|
|
2528
2976
|
const d = {
|
|
@@ -2583,8 +3031,34 @@ function explicitSchemeRefusal(root, scheme, isExpo, d) {
|
|
|
2583
3031
|
if (project.error) return project.error;
|
|
2584
3032
|
return d.resolveScheme(project, { scheme }).error ?? null;
|
|
2585
3033
|
}
|
|
3034
|
+
function resolveIosBuildSetup(flag, settings) {
|
|
3035
|
+
const placement = resolveBuildPlacement(flag);
|
|
3036
|
+
if (placement.failure) return {
|
|
3037
|
+
ok: false,
|
|
3038
|
+
failure: {
|
|
3039
|
+
...placement.failure,
|
|
3040
|
+
setup: true
|
|
3041
|
+
}
|
|
3042
|
+
};
|
|
3043
|
+
try {
|
|
3044
|
+
return {
|
|
3045
|
+
ok: true,
|
|
3046
|
+
buildMachine: placement.selected,
|
|
3047
|
+
optimizations: resolveOptimizations(settings)
|
|
3048
|
+
};
|
|
3049
|
+
} catch (error) {
|
|
3050
|
+
return {
|
|
3051
|
+
ok: false,
|
|
3052
|
+
failure: {
|
|
3053
|
+
code: "STIM_BAD_ARG",
|
|
3054
|
+
message: error.message,
|
|
3055
|
+
remedy: SETTING_SHAPE_REMEDY
|
|
3056
|
+
}
|
|
3057
|
+
};
|
|
3058
|
+
}
|
|
3059
|
+
}
|
|
2586
3060
|
function iosSlotLogFile(root, slot) {
|
|
2587
|
-
return slot === "default" ? buildLogFile(root) : join(workspaceLogsDir(root), `build-${deviceSlotFileKey("ios", slot)}.ndjson`);
|
|
3061
|
+
return slot === "default" ? buildLogFile(root) : join(workspaceLogsDir$1(root), `build-${deviceSlotFileKey("ios", slot)}.ndjson`);
|
|
2588
3062
|
}
|
|
2589
3063
|
function iosSlotDeps(d, slot) {
|
|
2590
3064
|
if (slot !== "default") {
|
|
@@ -2675,10 +3149,16 @@ async function runIos(opts = {}, overrides = {}, progress = NO_BUILD_PROGRESS) {
|
|
|
2675
3149
|
process.exitCode = 1;
|
|
2676
3150
|
return null;
|
|
2677
3151
|
}
|
|
2678
|
-
const logsDir = workspaceLogsDir(root);
|
|
3152
|
+
const logsDir = workspaceLogsDir$1(root);
|
|
2679
3153
|
const logFile = iosSlotLogFile(root, slot);
|
|
2680
3154
|
let writer = null;
|
|
2681
|
-
const logWriter = () => writer ||= d.createWriter(logFile, {
|
|
3155
|
+
const logWriter = () => writer ||= tapBuildLog(d.createWriter(logFile, {
|
|
3156
|
+
truncate: true,
|
|
3157
|
+
fields: {
|
|
3158
|
+
platform: "ios",
|
|
3159
|
+
slot
|
|
3160
|
+
}
|
|
3161
|
+
}), progress);
|
|
2682
3162
|
let leaseHandle = null;
|
|
2683
3163
|
let stopLeaseSignals = null;
|
|
2684
3164
|
const releaseLease = () => {
|
|
@@ -2698,13 +3178,15 @@ async function runIos(opts = {}, overrides = {}, progress = NO_BUILD_PROGRESS) {
|
|
|
2698
3178
|
write: (statsRun, at) => d.recordStats(statsRun, at),
|
|
2699
3179
|
now: () => d.now(),
|
|
2700
3180
|
note: (line) => note(chalk.dim(line)),
|
|
2701
|
-
phases: () => progress.durations()
|
|
3181
|
+
phases: () => progress.durations(),
|
|
3182
|
+
deviceSetup: () => progress.deviceSetupKnown()
|
|
2702
3183
|
});
|
|
2703
3184
|
const recordRun = stats.record;
|
|
2704
3185
|
let compilationCache = COMPILATION_CACHE_NOT_RUN;
|
|
2705
3186
|
let reclaimed = [];
|
|
3187
|
+
let buildMachine = "auto";
|
|
2706
3188
|
let builtConfiguration = null;
|
|
2707
|
-
const fail = ({ code, message, remedy = null, lines = [], logPath = null, build = null, lease }) => {
|
|
3189
|
+
const fail = ({ code, message, remedy = null, lines = [], logPath = null, build = null, lease, setup = false }) => {
|
|
2708
3190
|
({code, message, remedy, lines} = cancelledFailure("ios", {
|
|
2709
3191
|
code,
|
|
2710
3192
|
message
|
|
@@ -2720,6 +3202,7 @@ async function runIos(opts = {}, overrides = {}, progress = NO_BUILD_PROGRESS) {
|
|
|
2720
3202
|
if (remedy) note(chalk.dim(phaseLine("remedy", remedy)));
|
|
2721
3203
|
if (logPath) note(chalk.dim(phaseLine("log", logPath)));
|
|
2722
3204
|
if (build) writeLastBuild(root, lastBuildRecord({
|
|
3205
|
+
buildMachine,
|
|
2723
3206
|
...build,
|
|
2724
3207
|
configuration: builtConfiguration,
|
|
2725
3208
|
startedAt,
|
|
@@ -2728,7 +3211,7 @@ async function runIos(opts = {}, overrides = {}, progress = NO_BUILD_PROGRESS) {
|
|
|
2728
3211
|
durationMs: elapsed()
|
|
2729
3212
|
}));
|
|
2730
3213
|
note(chalk.red(phaseLine("failed", code)));
|
|
2731
|
-
recordRun({
|
|
3214
|
+
if (!setup) recordRun({
|
|
2732
3215
|
failed: true,
|
|
2733
3216
|
durationMs: elapsed()
|
|
2734
3217
|
});
|
|
@@ -2750,12 +3233,13 @@ async function runIos(opts = {}, overrides = {}, progress = NO_BUILD_PROGRESS) {
|
|
|
2750
3233
|
gitCommonDir: d.gitCommonDir(root),
|
|
2751
3234
|
repoRoot: settingsRepoRoot
|
|
2752
3235
|
};
|
|
2753
|
-
const projectKey = statsProjectKey({
|
|
3236
|
+
const projectKey = statsProjectKey$1({
|
|
2754
3237
|
root,
|
|
2755
3238
|
commonDir: settingsContext.gitCommonDir,
|
|
2756
3239
|
repoRoot: settingsRepoRoot
|
|
2757
3240
|
});
|
|
2758
3241
|
stats.setProject(projectKey);
|
|
3242
|
+
progress.estimate(projectKey);
|
|
2759
3243
|
let estimatesRead = null;
|
|
2760
3244
|
const estimates = () => estimatesRead ??= d.readEstimates({
|
|
2761
3245
|
projectKey,
|
|
@@ -2769,16 +3253,10 @@ async function runIos(opts = {}, overrides = {}, progress = NO_BUILD_PROGRESS) {
|
|
|
2769
3253
|
lines: moreShapeErrors,
|
|
2770
3254
|
remedy: SETTING_SHAPE_REMEDY
|
|
2771
3255
|
});
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
}
|
|
2776
|
-
return fail({
|
|
2777
|
-
code: "STIM_BAD_ARG",
|
|
2778
|
-
message: error.message,
|
|
2779
|
-
remedy: SETTING_SHAPE_REMEDY
|
|
2780
|
-
});
|
|
2781
|
-
}
|
|
3256
|
+
const setup = resolveIosBuildSetup(opts.buildMachine, settings);
|
|
3257
|
+
if (!setup.ok) return fail(setup.failure);
|
|
3258
|
+
buildMachine = setup.buildMachine;
|
|
3259
|
+
const { optimizations } = setup;
|
|
2782
3260
|
const buildProfile = optimizationBuildProfile("ios", optimizations);
|
|
2783
3261
|
const cacheProviderConfig = d.resolveCacheProviderConfig(settingsContext);
|
|
2784
3262
|
for (const key of unknownSettingKeys(settings)) note(phaseLine("setting", chalk.yellow(`Warning: setting "${key}" is not read by Stim and will be ignored.`)));
|
|
@@ -2819,63 +3297,55 @@ async function runIos(opts = {}, overrides = {}, progress = NO_BUILD_PROGRESS) {
|
|
|
2819
3297
|
message: "--device builds for a phone connected to this machine, and --remote installs on a remote one.",
|
|
2820
3298
|
remedy: "Pass only one of --device and --remote."
|
|
2821
3299
|
});
|
|
2822
|
-
const
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
code: "STIM_BAD_ARG",
|
|
2826
|
-
message: "--wait and --no-wait ask for opposite things.",
|
|
2827
|
-
remedy: "Pass `--wait <seconds>` to wait for the lease, or `--no-wait` to install without one."
|
|
2828
|
-
});
|
|
2829
|
-
if (waitFlagged && !physical) return fail({
|
|
2830
|
-
code: "STIM_BAD_ARG",
|
|
2831
|
-
message: "--wait and --no-wait only apply to a `--device` run.",
|
|
2832
|
-
remedy: "This workspace owns its simulator, so nothing contends for it. Drop the flag, or pass `--device`."
|
|
2833
|
-
});
|
|
2834
|
-
const waitParsed = parseDeviceWait(noWait ? void 0 : opts.wait);
|
|
2835
|
-
if ("error" in waitParsed) return fail({
|
|
2836
|
-
code: "STIM_BAD_ARG",
|
|
2837
|
-
message: waitParsed.error,
|
|
2838
|
-
remedy: "Pass a whole number of seconds, e.g. --wait 90. `--wait 0` refuses a leased device at once."
|
|
2839
|
-
});
|
|
2840
|
-
const waitSeconds = waitParsed.seconds;
|
|
3300
|
+
const wait = resolveIosWait(opts, physical);
|
|
3301
|
+
if ("failure" in wait) return fail(wait.failure);
|
|
3302
|
+
const { waitSeconds, noWait } = wait;
|
|
2841
3303
|
const isExpo = d.detectIsExpo(root);
|
|
2842
3304
|
const schemeRefusal = explicitSchemeRefusal(root, buildScheme, isExpo, d);
|
|
2843
3305
|
if (schemeRefusal) return fail(schemeRefusal);
|
|
2844
|
-
const
|
|
2845
|
-
|
|
3306
|
+
const remoteSelection = selectIosTarget({
|
|
3307
|
+
root,
|
|
3308
|
+
slot,
|
|
3309
|
+
opts,
|
|
3310
|
+
settings,
|
|
3311
|
+
physical,
|
|
3312
|
+
release,
|
|
3313
|
+
metroCheck,
|
|
3314
|
+
d
|
|
3315
|
+
});
|
|
3316
|
+
if ("failure" in remoteSelection) return fail(remoteSelection.failure);
|
|
3317
|
+
const { machine: hostedMachine, backend: remoteBackend } = remoteSelection;
|
|
3318
|
+
const viewer = resolveSimulatorAppFlag(opts.simulatorApp, physical, hostedMachine ?? remoteBackend);
|
|
2846
3319
|
if ("refusal" in viewer) return fail(viewer.refusal);
|
|
2847
3320
|
const { simulatorApp } = viewer;
|
|
3321
|
+
const settingsLayersForOrigin = d.settingsLayers(settingsContext);
|
|
2848
3322
|
const modelRefusal = deviceModelRefusal({
|
|
2849
3323
|
slot,
|
|
2850
3324
|
deviceTypeFlag: opts.deviceType,
|
|
2851
3325
|
runtimeFlag: opts.runtime,
|
|
2852
3326
|
deviceType,
|
|
2853
3327
|
runtime,
|
|
3328
|
+
deviceTypeOrigin: d.settingOriginScope(settingsLayersForOrigin, "ios.deviceType"),
|
|
3329
|
+
runtimeOrigin: d.settingOriginScope(settingsLayersForOrigin, "ios.runtime"),
|
|
2854
3330
|
physical,
|
|
2855
3331
|
remoteBackend,
|
|
3332
|
+
hosted: Boolean(hostedMachine),
|
|
2856
3333
|
listRuntimes: d.listIosRuntimes
|
|
2857
3334
|
});
|
|
2858
3335
|
if (modelRefusal) return fail(modelRefusal);
|
|
2859
|
-
const
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
const
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
if ("failed" in resolved) return fail({
|
|
2873
|
-
code: resolved.code ?? "STIM_NO_REMOTE_SESSION",
|
|
2874
|
-
message: resolved.failed,
|
|
2875
|
-
remedy: resolved.remedy
|
|
2876
|
-
});
|
|
2877
|
-
remoteDevice = d.remoteIosDeps(resolved.ctx);
|
|
2878
|
-
d = {
|
|
3336
|
+
const selectors = hostedIosSelectors(deviceType, runtime);
|
|
3337
|
+
const connected = await connectIosTarget(remoteSelection, selectors, d);
|
|
3338
|
+
if ("failure" in connected) return fail(connected.failure);
|
|
3339
|
+
const hostedTarget = connected.target;
|
|
3340
|
+
try {
|
|
3341
|
+
const budget = await iosPlacementBudget(d, root, note, Boolean(hostedTarget));
|
|
3342
|
+
reclaimed = budget.reclaimed;
|
|
3343
|
+
if (budget.refusal) return fail(budget.refusal);
|
|
3344
|
+
const backendConnection = await connectIosBackend(root, remoteBackend, opts.deviceType, d);
|
|
3345
|
+
if ("failure" in backendConnection) return fail(backendConnection.failure);
|
|
3346
|
+
const remoteDevice = backendConnection.remote;
|
|
3347
|
+
const remoteArch = hostedTarget ? hostedTarget.choice.architecture : backendConnection.arch;
|
|
3348
|
+
if (remoteDevice) d = {
|
|
2879
3349
|
...d,
|
|
2880
3350
|
checkDeviceCapacity: remoteDevice.checkDeviceCapacity,
|
|
2881
3351
|
ensureOwnedDevice: remoteDevice.ensureOwnedDevice,
|
|
@@ -2883,376 +3353,432 @@ async function runIos(opts = {}, overrides = {}, progress = NO_BUILD_PROGRESS) {
|
|
|
2883
3353
|
installIosApp: remoteDevice.installIosApp,
|
|
2884
3354
|
launchIosApp: remoteDevice.launchIosApp
|
|
2885
3355
|
};
|
|
2886
|
-
|
|
2887
|
-
const easBuild = await d.resolveEasDevelopmentBuild({
|
|
2888
|
-
root,
|
|
2889
|
-
platform: "ios",
|
|
2890
|
-
profile: opts.easProfile,
|
|
2891
|
-
note,
|
|
2892
|
-
isExpo,
|
|
2893
|
-
physical,
|
|
2894
|
-
selectors: [opts.scheme, opts.configuration],
|
|
2895
|
-
buildCache: opts.buildCache
|
|
2896
|
-
});
|
|
2897
|
-
if (isEasBuildFailure(easBuild)) return fail(easBuild);
|
|
2898
|
-
const registerProject = () => d.upsertProject(root, {
|
|
2899
|
-
bundleId: d.detectBundleId(root) ?? void 0,
|
|
2900
|
-
isExpo
|
|
2901
|
-
});
|
|
2902
|
-
if (remoteBackend !== "eas") registerProject();
|
|
2903
|
-
const proj = d.getProject(root);
|
|
2904
|
-
const limits = d.getConcurrencyLimits();
|
|
2905
|
-
let physicalDevice = null;
|
|
2906
|
-
let wireless = false;
|
|
2907
|
-
if (physical && typeof deviceFlag !== "string") {
|
|
2908
|
-
const pooled = await d.selectFromPool({
|
|
3356
|
+
const easBuild = await d.resolveEasDevelopmentBuild({
|
|
2909
3357
|
root,
|
|
2910
3358
|
platform: "ios",
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
noCandidates: () => {
|
|
2918
|
-
const resolved = iosPoolNoCandidatesRefusal(d.listIosDevices());
|
|
2919
|
-
return {
|
|
2920
|
-
message: resolved.error,
|
|
2921
|
-
remedy: resolved.remedy
|
|
2922
|
-
};
|
|
2923
|
-
},
|
|
2924
|
-
waitSeconds,
|
|
2925
|
-
noWait,
|
|
2926
|
-
now: d.now,
|
|
2927
|
-
warn: (line) => note(chalk.yellow(phaseLine("lease", line)))
|
|
2928
|
-
});
|
|
2929
|
-
if (pooled.status === "refused") return fail({
|
|
2930
|
-
code: pooled.refusal.code,
|
|
2931
|
-
message: pooled.refusal.message,
|
|
2932
|
-
remedy: pooled.refusal.remedy,
|
|
2933
|
-
...pooled.refusal.lease === null ? {} : { lease: pooled.refusal.lease }
|
|
2934
|
-
});
|
|
2935
|
-
physicalDevice = {
|
|
2936
|
-
udid: pooled.candidate.id,
|
|
2937
|
-
name: pooled.candidate.name ?? pooled.candidate.id
|
|
2938
|
-
};
|
|
2939
|
-
wireless = pooled.candidate.fallback === true;
|
|
2940
|
-
} else if (physical) {
|
|
2941
|
-
const resolved = resolveIosPhysicalDevice(typeof deviceFlag === "string" ? deviceFlag : null, d.listIosDevices());
|
|
2942
|
-
if (!resolved.udid) return fail({
|
|
2943
|
-
code: "STIM_NO_DEVICE",
|
|
2944
|
-
message: resolved.error,
|
|
2945
|
-
remedy: resolved.remedy
|
|
3359
|
+
profile: opts.easProfile,
|
|
3360
|
+
note,
|
|
3361
|
+
isExpo,
|
|
3362
|
+
physical,
|
|
3363
|
+
selectors: [opts.scheme, opts.configuration],
|
|
3364
|
+
buildCache: opts.buildCache
|
|
2946
3365
|
});
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
wireless = resolved.wireless === true;
|
|
2952
|
-
}
|
|
2953
|
-
if (!physical) {
|
|
2954
|
-
const capacity = d.checkDeviceCapacity({
|
|
2955
|
-
platform: "ios",
|
|
2956
|
-
project: proj,
|
|
2957
|
-
max: limits.maxDevices
|
|
3366
|
+
if (isEasBuildFailure(easBuild)) return fail(easBuild);
|
|
3367
|
+
const registerProject = () => d.upsertProject(root, {
|
|
3368
|
+
bundleId: d.detectBundleId(root) ?? void 0,
|
|
3369
|
+
isExpo
|
|
2958
3370
|
});
|
|
2959
|
-
if (
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
if (physicalDevice) device = {
|
|
2968
|
-
deviceUdid: physicalDevice.udid,
|
|
2969
|
-
deviceName: physicalDevice.name,
|
|
2970
|
-
owned: false
|
|
2971
|
-
};
|
|
2972
|
-
else {
|
|
2973
|
-
const prepare = stepClock(d.now);
|
|
2974
|
-
try {
|
|
2975
|
-
device = await d.ensureOwnedDevice({
|
|
3371
|
+
if (remoteBackend !== "eas") registerProject();
|
|
3372
|
+
const proj = d.getProject(root);
|
|
3373
|
+
const limits = d.getConcurrencyLimits();
|
|
3374
|
+
let physicalDevice = null;
|
|
3375
|
+
let wireless = false;
|
|
3376
|
+
if (physical && typeof deviceFlag !== "string") {
|
|
3377
|
+
const pooled = await d.selectFromPool({
|
|
3378
|
+
root,
|
|
2976
3379
|
platform: "ios",
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
3380
|
+
idLabel: "udid",
|
|
3381
|
+
list: () => iosPoolCandidates(d.listIosDevices()).map((entry) => ({
|
|
3382
|
+
id: entry.udid,
|
|
3383
|
+
name: entry.name,
|
|
3384
|
+
fallback: isWirelessIosDevice(entry)
|
|
3385
|
+
})),
|
|
3386
|
+
noCandidates: () => {
|
|
3387
|
+
const resolved = iosPoolNoCandidatesRefusal(d.listIosDevices());
|
|
3388
|
+
return {
|
|
3389
|
+
message: resolved.error,
|
|
3390
|
+
remedy: resolved.remedy
|
|
3391
|
+
};
|
|
2986
3392
|
},
|
|
2987
|
-
|
|
2988
|
-
|
|
3393
|
+
waitSeconds,
|
|
3394
|
+
noWait,
|
|
3395
|
+
now: d.now,
|
|
3396
|
+
warn: (line) => note(chalk.yellow(phaseLine("lease", line)))
|
|
2989
3397
|
});
|
|
2990
|
-
|
|
2991
|
-
|
|
3398
|
+
if (pooled.status === "refused") return fail({
|
|
3399
|
+
code: pooled.refusal.code,
|
|
3400
|
+
message: pooled.refusal.message,
|
|
3401
|
+
remedy: pooled.refusal.remedy,
|
|
3402
|
+
...pooled.refusal.lease === null ? {} : { lease: pooled.refusal.lease }
|
|
3403
|
+
});
|
|
3404
|
+
physicalDevice = {
|
|
3405
|
+
udid: pooled.candidate.id,
|
|
3406
|
+
name: pooled.candidate.name ?? pooled.candidate.id
|
|
3407
|
+
};
|
|
3408
|
+
wireless = pooled.candidate.fallback === true;
|
|
3409
|
+
} else if (physical) {
|
|
3410
|
+
const resolved = resolveIosPhysicalDevice(typeof deviceFlag === "string" ? deviceFlag : null, d.listIosDevices());
|
|
3411
|
+
if (!resolved.udid) return fail({
|
|
3412
|
+
code: "STIM_NO_DEVICE",
|
|
3413
|
+
message: resolved.error,
|
|
3414
|
+
remedy: resolved.remedy
|
|
3415
|
+
});
|
|
3416
|
+
physicalDevice = {
|
|
3417
|
+
udid: resolved.udid,
|
|
3418
|
+
name: resolved.name ?? resolved.udid
|
|
3419
|
+
};
|
|
3420
|
+
wireless = resolved.wireless === true;
|
|
2992
3421
|
}
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3422
|
+
if (!physical && !hostedTarget) {
|
|
3423
|
+
const capacity = d.checkDeviceCapacity({
|
|
3424
|
+
platform: "ios",
|
|
3425
|
+
project: proj,
|
|
3426
|
+
max: limits.maxDevices
|
|
3427
|
+
});
|
|
3428
|
+
if (capacity) return fail(capacity);
|
|
3429
|
+
}
|
|
3430
|
+
let metroPort = proj?.metroPort ?? null;
|
|
3431
|
+
let lanAddress = null;
|
|
3432
|
+
let lanOriginUrl = null;
|
|
3433
|
+
let devServer = null;
|
|
3434
|
+
if (!await resolveMetroPort()) return null;
|
|
3435
|
+
let device;
|
|
3436
|
+
if (physicalDevice) device = {
|
|
3437
|
+
deviceUdid: physicalDevice.udid,
|
|
3438
|
+
deviceName: physicalDevice.name,
|
|
3439
|
+
owned: false
|
|
3440
|
+
};
|
|
3441
|
+
else if (hostedTarget) device = {
|
|
3442
|
+
owned: false,
|
|
3443
|
+
deviceName: hostedTarget.choice.deviceType,
|
|
3444
|
+
runtime: hostedTarget.choice.runtime
|
|
3445
|
+
};
|
|
3446
|
+
else {
|
|
3447
|
+
const prepare = stepClock(d.now);
|
|
3448
|
+
try {
|
|
3449
|
+
device = await d.ensureOwnedDevice({
|
|
3450
|
+
platform: "ios",
|
|
3451
|
+
project: proj,
|
|
3452
|
+
projectPath: root,
|
|
3453
|
+
settingsRoot: root,
|
|
3454
|
+
settings,
|
|
3455
|
+
flags: {
|
|
3456
|
+
deviceType,
|
|
3457
|
+
runtime,
|
|
3458
|
+
runtimeFlag: resolveRuntime(opts.runtime, null),
|
|
3459
|
+
simulatorApp
|
|
3460
|
+
},
|
|
3461
|
+
note,
|
|
3462
|
+
out: note
|
|
3463
|
+
});
|
|
3464
|
+
} catch (e) {
|
|
3465
|
+
return fail(ownedSimFailure(e));
|
|
3466
|
+
}
|
|
3467
|
+
progress.deviceSetup(didSetUpDevice(device, Boolean(remoteDevice)));
|
|
3468
|
+
const prepareMs = prepare();
|
|
3469
|
+
if (device.created || prepareMs >= 2e3) phase("device", `${deviceLabel(device, device.deviceUdid)} ${device.created ? "created" : "prepared"} (${formatDuration(prepareMs)})`);
|
|
3470
|
+
}
|
|
3471
|
+
let bootDuration = "";
|
|
3472
|
+
let bootPromise;
|
|
3473
|
+
let udid = "";
|
|
3474
|
+
async function resolveMetroPort() {
|
|
3475
|
+
if (release) {
|
|
3476
|
+
metroPort = null;
|
|
3477
|
+
phase("metro", `skipped (${configuration}: the JS bundle is embedded, no dev server is used)`);
|
|
3478
|
+
} else if (metroCheck) {
|
|
3479
|
+
const gate = await ensureDevServer({
|
|
3480
|
+
root,
|
|
3481
|
+
port: metroPort,
|
|
3482
|
+
settings: iosMetroSettings(settings, Boolean(hostedTarget)),
|
|
3483
|
+
remote: Boolean(remoteDevice),
|
|
3484
|
+
note,
|
|
3485
|
+
resolve: d.resolveProjectMetro,
|
|
3486
|
+
start: d.startDevServer,
|
|
3487
|
+
readState: d.readWorkspaceState
|
|
3488
|
+
});
|
|
3489
|
+
reclaimed = [...reclaimed, ...gate.reclaimed];
|
|
3490
|
+
if (!gate.ok) {
|
|
3491
|
+
fail({
|
|
3492
|
+
code: gate.code,
|
|
3493
|
+
message: gate.message,
|
|
3494
|
+
remedy: gate.remedy,
|
|
3495
|
+
lines: gate.lines
|
|
3496
|
+
});
|
|
3497
|
+
return false;
|
|
3498
|
+
}
|
|
3499
|
+
metroPort = gate.port;
|
|
3500
|
+
devServer = gate.devServer;
|
|
3501
|
+
} else {
|
|
3502
|
+
const pin = metroPortSetting(root);
|
|
3503
|
+
if (pin.error) {
|
|
3504
|
+
fail({
|
|
3505
|
+
code: "STIM_BAD_ARG",
|
|
3506
|
+
message: pin.error,
|
|
3507
|
+
remedy: SETTING_SHAPE_REMEDY
|
|
3508
|
+
});
|
|
3509
|
+
return false;
|
|
3510
|
+
}
|
|
3511
|
+
if (pin.port === null && !metroPort) note(chalk.yellow(`No Metro port is reserved for this workspace; wiring the app to ${DEFAULT_METRO_PORT}.`));
|
|
3512
|
+
metroPort = pin.port ?? metroPort ?? 8081;
|
|
3513
|
+
}
|
|
3514
|
+
hostedIosMetroNote(Boolean(hostedTarget), settings, note);
|
|
3515
|
+
if (physical && metroPort !== null && !await resolveLanOrigin()) return false;
|
|
3516
|
+
if (remoteDevice && metroPort !== null) {
|
|
3517
|
+
const reachable = await d.ensureMetroReachable({
|
|
3518
|
+
ctx: remoteDevice.ctx,
|
|
3519
|
+
metroPort,
|
|
3520
|
+
isExpo,
|
|
3521
|
+
tunnelMode: tunnelModeSetting(settings) ?? void 0,
|
|
3522
|
+
publicUrl: publicUrlSetting(settings),
|
|
3523
|
+
available: d.detectProviders(binOnPath, tunnelModeSetting(settings) ?? "auto")
|
|
3524
|
+
});
|
|
3525
|
+
if ("failed" in reachable) {
|
|
3526
|
+
fail({
|
|
3527
|
+
code: reachable.code ?? "STIM_NO_REMOTE_SESSION",
|
|
3528
|
+
message: reachable.failed,
|
|
3529
|
+
remedy: reachable.remedy
|
|
3530
|
+
});
|
|
3531
|
+
return false;
|
|
3532
|
+
}
|
|
3533
|
+
}
|
|
3534
|
+
if (!release && metroCheck && optimizations.metroWarmup) d.warmMetro({
|
|
3006
3535
|
port: metroPort,
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3536
|
+
platform: "ios",
|
|
3537
|
+
isExpo,
|
|
3538
|
+
appId: proj?.bundleId,
|
|
3539
|
+
bundleUrl: metroWarmupUrlSetting(settings, "ios")
|
|
3540
|
+
});
|
|
3541
|
+
return true;
|
|
3542
|
+
}
|
|
3543
|
+
async function resolveLanOrigin() {
|
|
3544
|
+
const port = metroPort;
|
|
3545
|
+
const chosen = chooseLanAddress({
|
|
3546
|
+
pinned: iosLanHostSetting(settings),
|
|
3547
|
+
candidates: d.hostLanCandidates()
|
|
3013
3548
|
});
|
|
3014
|
-
|
|
3015
|
-
if (!gate.ok) {
|
|
3549
|
+
if (!chosen) {
|
|
3016
3550
|
fail({
|
|
3017
|
-
code:
|
|
3018
|
-
message:
|
|
3019
|
-
remedy:
|
|
3020
|
-
lines: gate.lines
|
|
3551
|
+
code: "STIM_NO_LAN_ADDRESS",
|
|
3552
|
+
message: "A Debug run on a phone needs an address the phone can reach, and this Mac has no non-internal IPv4 interface.",
|
|
3553
|
+
remedy: "The phone reaches Metro over the network you share, because USB carries no reverse forward. Join a Wi-Fi or Ethernet network, or connect this Mac by cable, then run the command again."
|
|
3021
3554
|
});
|
|
3022
3555
|
return false;
|
|
3023
3556
|
}
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
note(chalk.
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
metroPort,
|
|
3557
|
+
lanAddress = chosen.address;
|
|
3558
|
+
lanOriginUrl = lanOriginUrlFor(chosen.address, port);
|
|
3559
|
+
const source = chosen.pinned ? "ios.lanHost" : `${chosen.interfaceName ?? "interface"}${chosen.candidates > 1 ? ` of ${chosen.candidates} candidates` : ""}`;
|
|
3560
|
+
phase("lan", `${lanOriginUrl} (${source})`);
|
|
3561
|
+
if (publicUrlSetting(settings) || tunnelModeSetting(settings)) note(chalk.dim(phaseLine("lan", "metro.publicUrl and metro.tunnel are ignored on --device: neither channel to a phone carries a URL, only a host and a port. They still apply to --remote.")));
|
|
3562
|
+
if (!metroCheck) return true;
|
|
3563
|
+
const reachable = await d.ensureLanReachable({
|
|
3564
|
+
origin: lanOriginUrl,
|
|
3565
|
+
metroPort: port,
|
|
3566
|
+
root,
|
|
3035
3567
|
isExpo,
|
|
3036
|
-
|
|
3037
|
-
publicUrl: publicUrlSetting(settings),
|
|
3038
|
-
available: d.detectProviders(binOnPath)
|
|
3568
|
+
logsDir
|
|
3039
3569
|
});
|
|
3040
3570
|
if ("failed" in reachable) {
|
|
3041
3571
|
fail({
|
|
3042
|
-
code:
|
|
3572
|
+
code: "STIM_LAN_METRO_UNREACHABLE",
|
|
3043
3573
|
message: reachable.failed,
|
|
3044
3574
|
remedy: reachable.remedy
|
|
3045
3575
|
});
|
|
3046
3576
|
return false;
|
|
3047
3577
|
}
|
|
3578
|
+
phase("lan", `gated: ${lanOriginUrl} answered as this workspace's Metro`);
|
|
3579
|
+
return true;
|
|
3048
3580
|
}
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
});
|
|
3056
|
-
return true;
|
|
3057
|
-
}
|
|
3058
|
-
async function resolveLanOrigin() {
|
|
3059
|
-
const port = metroPort;
|
|
3060
|
-
const chosen = chooseLanAddress({
|
|
3061
|
-
pinned: iosLanHostSetting(settings),
|
|
3062
|
-
candidates: d.hostLanCandidates()
|
|
3063
|
-
});
|
|
3064
|
-
if (!chosen) {
|
|
3065
|
-
fail({
|
|
3066
|
-
code: "STIM_NO_LAN_ADDRESS",
|
|
3067
|
-
message: "A Debug run on a phone needs an address the phone can reach, and this Mac has no non-internal IPv4 interface.",
|
|
3068
|
-
remedy: "The phone reaches Metro over the network you share, because USB carries no reverse forward. Join a Wi-Fi or Ethernet network, or connect this Mac by cable, then run the command again."
|
|
3069
|
-
});
|
|
3070
|
-
return false;
|
|
3071
|
-
}
|
|
3072
|
-
lanAddress = chosen.address;
|
|
3073
|
-
lanOriginUrl = lanOriginUrlFor(chosen.address, port);
|
|
3074
|
-
const source = chosen.pinned ? "ios.lanHost" : `${chosen.interfaceName ?? "interface"}${chosen.candidates > 1 ? ` of ${chosen.candidates} candidates` : ""}`;
|
|
3075
|
-
phase("lan", `${lanOriginUrl} (${source})`);
|
|
3076
|
-
if (publicUrlSetting(settings) || tunnelModeSetting(settings)) note(chalk.dim(phaseLine("lan", "metro.publicUrl and metro.tunnel are ignored on --device: neither channel to a phone carries a URL, only a host and a port. They still apply to --remote.")));
|
|
3077
|
-
if (!metroCheck) return true;
|
|
3078
|
-
const reachable = await d.ensureLanReachable({
|
|
3079
|
-
origin: lanOriginUrl,
|
|
3080
|
-
metroPort: port,
|
|
3081
|
-
root,
|
|
3082
|
-
isExpo,
|
|
3083
|
-
logsDir
|
|
3084
|
-
});
|
|
3085
|
-
if ("failed" in reachable) {
|
|
3086
|
-
fail({
|
|
3087
|
-
code: "STIM_LAN_METRO_UNREACHABLE",
|
|
3088
|
-
message: reachable.failed,
|
|
3089
|
-
remedy: reachable.remedy
|
|
3090
|
-
});
|
|
3091
|
-
return false;
|
|
3092
|
-
}
|
|
3093
|
-
phase("lan", `gated: ${lanOriginUrl} answered as this workspace's Metro`);
|
|
3094
|
-
return true;
|
|
3095
|
-
}
|
|
3096
|
-
let artifact = null;
|
|
3097
|
-
try {
|
|
3098
|
-
const boot = () => physicalDevice ? Promise.resolve({
|
|
3099
|
-
ok: true,
|
|
3100
|
-
udid: physicalDevice.udid
|
|
3101
|
-
}) : Promise.resolve(d.ensureBooted({
|
|
3102
|
-
platform: "ios",
|
|
3103
|
-
device,
|
|
3104
|
-
simulatorApp,
|
|
3105
|
-
out: note
|
|
3106
|
-
})).catch((e) => ({
|
|
3107
|
-
ok: false,
|
|
3108
|
-
reason: String(e?.message || e)
|
|
3109
|
-
}));
|
|
3110
|
-
const startBoot = () => {
|
|
3111
|
-
const bootTimer = stepTimer(d.now);
|
|
3112
|
-
bootPromise = (remoteDevice?.ctx.backend === "eas" ? d.ensureRemoteBootOwned({
|
|
3113
|
-
root,
|
|
3114
|
-
platform: "ios",
|
|
3115
|
-
sessionName: ownedSessionName(remoteDevice.ctx.label),
|
|
3116
|
-
startedAt: new Date(d.now()).toISOString(),
|
|
3117
|
-
boot,
|
|
3118
|
-
createdSessionId: remoteDevice.createdSessionId,
|
|
3119
|
-
abandonCreatedSession: remoteDevice.abandonCreatedSession,
|
|
3120
|
-
webPreviewUrl: remoteDevice.webPreviewUrl,
|
|
3121
|
-
writeState: d.writeWorkspaceState,
|
|
3122
|
-
register: registerProject,
|
|
3123
|
-
notice: (line) => note(chalk.dim(phaseLine("lock", line)))
|
|
3124
|
-
}) : boot()).then((result) => {
|
|
3125
|
-
bootDuration = bootTimer();
|
|
3126
|
-
return result;
|
|
3127
|
-
});
|
|
3128
|
-
return bootPromise.then((result) => result?.udid ?? "");
|
|
3129
|
-
};
|
|
3130
|
-
const localBoot = remoteDevice ? null : startBoot();
|
|
3131
|
-
udid = device.deviceUdid ?? await localBoot ?? "";
|
|
3132
|
-
const acquiredArtifact = unlessCancelled(await acquireIosArtifact({
|
|
3133
|
-
root,
|
|
3134
|
-
logFile,
|
|
3135
|
-
udid,
|
|
3136
|
-
configuration,
|
|
3137
|
-
buildScheme,
|
|
3138
|
-
buildProfile,
|
|
3139
|
-
isExpo,
|
|
3140
|
-
remoteDestination: Boolean(remoteDevice),
|
|
3141
|
-
device: physical ? {
|
|
3142
|
-
lanAddress,
|
|
3143
|
-
metroPort,
|
|
3144
|
-
signingName: iosSigningIdentitySetting(settings),
|
|
3145
|
-
signingSha1: iosSigningIdentitySha1Setting(settings)
|
|
3146
|
-
} : null,
|
|
3147
|
-
optimizations: optimizations.ios,
|
|
3148
|
-
cache: {
|
|
3149
|
-
policy: cachePolicy,
|
|
3150
|
-
providerConfig: cacheProviderConfig,
|
|
3151
|
-
disabledByFlag: opts.buildCache === false
|
|
3152
|
-
},
|
|
3153
|
-
easBuild,
|
|
3154
|
-
easProfile: opts.easProfile,
|
|
3155
|
-
maxBuilds: limits.maxBuilds,
|
|
3156
|
-
progress: {
|
|
3157
|
-
phase,
|
|
3158
|
-
note,
|
|
3159
|
-
logWriter,
|
|
3160
|
-
estimates,
|
|
3161
|
-
stats,
|
|
3162
|
-
step: progress.step
|
|
3163
|
-
}
|
|
3164
|
-
}, d));
|
|
3165
|
-
if (!acquiredArtifact.ok) {
|
|
3166
|
-
compilationCache = acquiredArtifact.compilationCache;
|
|
3167
|
-
return fail(acquiredArtifact.failure);
|
|
3168
|
-
}
|
|
3169
|
-
artifact = acquiredArtifact.artifact;
|
|
3170
|
-
compilationCache = artifact.cache.compilation;
|
|
3171
|
-
if (!localBoot) {
|
|
3172
|
-
progress.step("install");
|
|
3173
|
-
udid = await startBoot();
|
|
3174
|
-
}
|
|
3175
|
-
if (physicalDevice) {
|
|
3176
|
-
const acquired = await d.acquireRunLease({
|
|
3177
|
-
root,
|
|
3581
|
+
let artifact = null;
|
|
3582
|
+
try {
|
|
3583
|
+
const boot = () => physicalDevice ? Promise.resolve({
|
|
3584
|
+
ok: true,
|
|
3585
|
+
udid: physicalDevice.udid
|
|
3586
|
+
}) : Promise.resolve(d.ensureBooted({
|
|
3178
3587
|
platform: "ios",
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3588
|
+
device,
|
|
3589
|
+
simulatorApp,
|
|
3590
|
+
out: note
|
|
3591
|
+
})).catch((e) => ({
|
|
3592
|
+
ok: false,
|
|
3593
|
+
reason: String(e?.message || e)
|
|
3594
|
+
}));
|
|
3595
|
+
const startBoot = () => {
|
|
3596
|
+
const bootTimer = stepTimer(d.now);
|
|
3597
|
+
bootPromise = (remoteDevice?.ctx.backend === "eas" ? d.ensureRemoteBootOwned({
|
|
3598
|
+
root,
|
|
3599
|
+
platform: "ios",
|
|
3600
|
+
sessionName: ownedSessionName(remoteDevice.ctx.label),
|
|
3601
|
+
startedAt: new Date(d.now()).toISOString(),
|
|
3602
|
+
deviceType: remoteDevice.ctx.deviceType ?? null,
|
|
3603
|
+
boot,
|
|
3604
|
+
createdSessionId: remoteDevice.createdSessionId,
|
|
3605
|
+
abandonCreatedSession: remoteDevice.abandonCreatedSession,
|
|
3606
|
+
webPreviewUrl: remoteDevice.webPreviewUrl,
|
|
3607
|
+
writeState: d.writeWorkspaceState,
|
|
3608
|
+
register: registerProject,
|
|
3609
|
+
notice: (line) => note(chalk.dim(phaseLine("lock", line)))
|
|
3610
|
+
}) : boot()).then((result) => {
|
|
3611
|
+
bootDuration = bootTimer();
|
|
3612
|
+
return result;
|
|
3613
|
+
});
|
|
3614
|
+
return bootPromise.then((result) => result?.udid ?? "");
|
|
3615
|
+
};
|
|
3616
|
+
const localBoot = remoteDevice || hostedTarget ? null : startBoot();
|
|
3617
|
+
udid = device.deviceUdid ?? await localBoot ?? "";
|
|
3618
|
+
const acquiredArtifact = unlessCancelled(await acquireIosArtifact({
|
|
3197
3619
|
root,
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3620
|
+
logFile,
|
|
3621
|
+
udid,
|
|
3622
|
+
configuration,
|
|
3623
|
+
buildScheme,
|
|
3624
|
+
buildProfile,
|
|
3625
|
+
buildMachine,
|
|
3626
|
+
isExpo,
|
|
3627
|
+
remoteDestination: Boolean(remoteDevice || hostedTarget),
|
|
3628
|
+
...hostedIosBuildTarget(hostedTarget),
|
|
3629
|
+
simulatorArch: simulatorBuildArch({
|
|
3630
|
+
physical,
|
|
3631
|
+
remoteArch,
|
|
3632
|
+
hostArch: d.hostSimulatorArch(),
|
|
3633
|
+
configuration
|
|
3634
|
+
}),
|
|
3635
|
+
device: physical ? {
|
|
3636
|
+
lanAddress,
|
|
3637
|
+
metroPort,
|
|
3638
|
+
signingName: iosSigningIdentitySetting(settings),
|
|
3639
|
+
signingSha1: iosSigningIdentitySha1Setting(settings)
|
|
3640
|
+
} : null,
|
|
3641
|
+
optimizations: optimizations.ios,
|
|
3642
|
+
cache: {
|
|
3643
|
+
policy: cachePolicy,
|
|
3644
|
+
providerConfig: cacheProviderConfig,
|
|
3645
|
+
disabledByFlag: opts.buildCache === false
|
|
3646
|
+
},
|
|
3647
|
+
easBuild,
|
|
3648
|
+
easProfile: opts.easProfile,
|
|
3649
|
+
maxBuilds: limits.maxBuilds,
|
|
3650
|
+
progress: {
|
|
3651
|
+
phase,
|
|
3652
|
+
note,
|
|
3653
|
+
logWriter,
|
|
3654
|
+
estimates,
|
|
3655
|
+
stats,
|
|
3656
|
+
step: progress.step,
|
|
3657
|
+
miss: progress.miss,
|
|
3658
|
+
hit: progress.hit,
|
|
3659
|
+
place: progress.place,
|
|
3660
|
+
waitingOn: progress.waitingOn
|
|
3661
|
+
}
|
|
3662
|
+
}, d));
|
|
3663
|
+
if (!acquiredArtifact.ok) {
|
|
3664
|
+
compilationCache = acquiredArtifact.compilationCache;
|
|
3665
|
+
return fail(acquiredArtifact.failure);
|
|
3205
3666
|
}
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
return await
|
|
3667
|
+
artifact = acquiredArtifact.artifact;
|
|
3668
|
+
compilationCache = artifact.cache.compilation;
|
|
3669
|
+
if (hostedTarget) return await finishHostedIosRun({
|
|
3670
|
+
target: hostedTarget,
|
|
3671
|
+
root,
|
|
3209
3672
|
slot,
|
|
3210
3673
|
d,
|
|
3211
|
-
|
|
3212
|
-
json,
|
|
3213
|
-
release,
|
|
3674
|
+
artifact,
|
|
3214
3675
|
configuration,
|
|
3215
3676
|
buildScheme,
|
|
3677
|
+
release,
|
|
3216
3678
|
isExpo,
|
|
3217
3679
|
metroCheck,
|
|
3218
3680
|
metroPort,
|
|
3219
3681
|
logsDir,
|
|
3220
|
-
|
|
3221
|
-
device,
|
|
3222
|
-
udid,
|
|
3223
|
-
physical,
|
|
3224
|
-
wireless,
|
|
3225
|
-
lanAddress,
|
|
3226
|
-
lanOriginUrl,
|
|
3227
|
-
remoteDevice,
|
|
3228
|
-
bootPromise,
|
|
3229
|
-
bootDuration: () => bootDuration,
|
|
3230
|
-
artifact,
|
|
3231
|
-
fail,
|
|
3232
|
-
phase,
|
|
3233
|
-
note,
|
|
3234
|
-
logWriter,
|
|
3682
|
+
json,
|
|
3235
3683
|
elapsed,
|
|
3236
3684
|
startedAt,
|
|
3237
|
-
closeWriter: () => writer?.close
|
|
3238
|
-
lease: leaseHandle,
|
|
3239
|
-
releaseLease,
|
|
3685
|
+
closeWriter: () => writer?.close(),
|
|
3240
3686
|
recordRun,
|
|
3241
3687
|
reclaimed,
|
|
3242
3688
|
devServer,
|
|
3243
|
-
|
|
3689
|
+
fail,
|
|
3690
|
+
note,
|
|
3691
|
+
selectors
|
|
3692
|
+
});
|
|
3693
|
+
if (!localBoot) {
|
|
3694
|
+
progress.step("device");
|
|
3695
|
+
udid = await startBoot();
|
|
3696
|
+
}
|
|
3697
|
+
if (physicalDevice) {
|
|
3698
|
+
progress.step("device");
|
|
3699
|
+
const acquired = await d.acquireRunLease({
|
|
3700
|
+
root,
|
|
3701
|
+
platform: "ios",
|
|
3702
|
+
id: physicalDevice.udid,
|
|
3703
|
+
deviceName: physicalDevice.name,
|
|
3704
|
+
idLabel: "udid",
|
|
3705
|
+
waitSeconds,
|
|
3706
|
+
noWait,
|
|
3707
|
+
installBoundMs: iosDeviceBounds(wireless).installMs,
|
|
3708
|
+
appId: artifact.bundleId ?? proj?.bundleId ?? null,
|
|
3709
|
+
holderAppId: (holder) => d.getProject(holder)?.bundleId ?? null,
|
|
3710
|
+
now: d.now,
|
|
3711
|
+
warn: (line) => note(chalk.yellow(phaseLine("lease", line)))
|
|
3712
|
+
});
|
|
3713
|
+
if (acquired.status === "refused") return fail({
|
|
3714
|
+
code: acquired.refusal.code,
|
|
3715
|
+
message: acquired.refusal.message,
|
|
3716
|
+
remedy: acquired.refusal.remedy,
|
|
3717
|
+
lease: acquired.refusal.lease
|
|
3718
|
+
});
|
|
3719
|
+
leaseHandle = d.runLease({
|
|
3720
|
+
root,
|
|
3721
|
+
platform: "ios",
|
|
3722
|
+
kind: acquired.status === "leased" ? acquired.kind : null,
|
|
3723
|
+
expiresAt: acquired.status === "leased" ? acquired.expiresAt : null
|
|
3724
|
+
});
|
|
3725
|
+
if (acquired.status === "leased") {
|
|
3726
|
+
stopLeaseSignals = d.releaseLeaseOnSignal(releaseLease);
|
|
3727
|
+
phase("lease", `${acquired.kind} lease on ${physicalDevice.udid} until ${leaseExpiryText(acquired.expiresAt, d.now())}`);
|
|
3728
|
+
}
|
|
3729
|
+
}
|
|
3730
|
+
try {
|
|
3731
|
+
return await finishIosRun({
|
|
3732
|
+
slot,
|
|
3733
|
+
d,
|
|
3734
|
+
root,
|
|
3735
|
+
json,
|
|
3736
|
+
release,
|
|
3737
|
+
configuration,
|
|
3738
|
+
buildScheme,
|
|
3739
|
+
isExpo,
|
|
3740
|
+
metroCheck,
|
|
3741
|
+
metroPort,
|
|
3742
|
+
logsDir,
|
|
3743
|
+
logFile,
|
|
3744
|
+
device,
|
|
3745
|
+
udid,
|
|
3746
|
+
physical,
|
|
3747
|
+
wireless,
|
|
3748
|
+
lanAddress,
|
|
3749
|
+
lanOriginUrl,
|
|
3750
|
+
remoteDevice,
|
|
3751
|
+
bootPromise,
|
|
3752
|
+
bootDuration: () => bootDuration,
|
|
3753
|
+
artifact,
|
|
3754
|
+
fail,
|
|
3755
|
+
phase,
|
|
3756
|
+
note,
|
|
3757
|
+
logWriter,
|
|
3758
|
+
elapsed,
|
|
3759
|
+
startedAt,
|
|
3760
|
+
closeWriter: () => writer?.close?.(),
|
|
3761
|
+
lease: leaseHandle,
|
|
3762
|
+
releaseLease,
|
|
3763
|
+
recordRun,
|
|
3764
|
+
reclaimed,
|
|
3765
|
+
devServer,
|
|
3766
|
+
enterPhase: progress.step
|
|
3767
|
+
});
|
|
3768
|
+
} finally {
|
|
3769
|
+
releaseLease();
|
|
3770
|
+
}
|
|
3771
|
+
} catch (error) {
|
|
3772
|
+
recordRun({
|
|
3773
|
+
failed: true,
|
|
3774
|
+
durationMs: elapsed()
|
|
3244
3775
|
});
|
|
3776
|
+
throw error;
|
|
3245
3777
|
} finally {
|
|
3246
|
-
|
|
3778
|
+
artifact?.release();
|
|
3247
3779
|
}
|
|
3248
|
-
} catch (error) {
|
|
3249
|
-
recordRun({
|
|
3250
|
-
failed: true,
|
|
3251
|
-
durationMs: elapsed()
|
|
3252
|
-
});
|
|
3253
|
-
throw error;
|
|
3254
3780
|
} finally {
|
|
3255
|
-
|
|
3781
|
+
hostedTarget?.host.connection.close();
|
|
3256
3782
|
}
|
|
3257
3783
|
}
|
|
3258
3784
|
//#endregion
|