stim 1.8.0 → 1.10.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 +23 -155
- package/dist/{android-DWssqHQ8.mjs → android-787HhP5w.mjs} +168 -39
- package/dist/{android-BRgBYzU0.mjs → android-DBkeHwpm.mjs} +250 -200
- package/dist/{android-DeYdjjBP.mjs → android-DPov-fv2.mjs} +2 -2
- package/dist/{android-cas-SWIjzeuL.mjs → android-cas-CITmIeWY.mjs} +22 -14
- package/dist/android-cas-compiler.mjs +2 -2
- package/dist/app-install-CrJ6t_bv.mjs +819 -0
- package/dist/budget-BP4jZZL4.mjs +620 -0
- package/dist/build-progress-BXx-ZBe4.mjs +512 -0
- package/dist/{cache-manifest-CYJi8-Hx.mjs → cache-manifest-Pubfp8wV.mjs} +4 -4
- package/dist/cache-manifest.mjs +1 -1
- package/dist/cli.mjs +22 -19
- package/dist/collector-run.d.mts +7 -1
- package/dist/collector-run.mjs +10 -9
- package/dist/{command-output-B3ycpsYv.mjs → command-output-D5z2_bcF.mjs} +11 -2
- package/dist/{config-B7iyKWkL.mjs → config-BEq0Qw_a.mjs} +134 -121
- package/dist/{dependency-state-D0M7oKA7.mjs → dependency-state-r9UesA9a.mjs} +2 -2
- package/dist/{deps-oCXVNEqQ.mjs → deps-DP4eWqea.mjs} +8 -5
- package/dist/{detached-entry-Bpo6ZY_I.mjs → detached-entry-DJHoca49.mjs} +1 -1
- package/dist/{device-BUoZyMoR.mjs → dev-client-92U593KT.mjs} +1090 -660
- package/dist/{device-RdujjHLB.mjs → device-DhTZe7L4.mjs} +13 -20
- package/dist/{build-lock-CgtsB6TV.mjs → device-lease-CbEaIk7b.mjs} +481 -27
- package/dist/{device-pool-BxRwdufO.mjs → device-pool-Di8Q-9Ro.mjs} +7 -7
- package/dist/{device-remote-ByN8P6CB.mjs → device-remote-Dy1jGj3U.mjs} +645 -119
- package/dist/{doctor-BMt156qe.mjs → doctor-B-inAJmt.mjs} +137 -113
- package/dist/{error-diagnostics-D1Kaf-eT.mjs → error-diagnostics-xxWKNO3-.mjs} +22 -17
- package/dist/exec-DK8KaVHK.mjs +133 -0
- package/dist/{gc-v2YcoJvm.mjs → gc-ChPFZMRC.mjs} +836 -813
- package/dist/{guide-BTAlD1_y.mjs → guide-DwmaD_Wa.mjs} +953 -209
- package/dist/guide-status-CWvti2Lg.mjs +125 -0
- package/dist/idle-DpRLp5lj.mjs +157 -0
- package/dist/{build-slots-C-oAHRT1.mjs → in-use-CjUluqIX.mjs} +102 -8
- package/dist/{ios-BcCiI4TS.mjs → ios-B2Fpqvlb.mjs} +60 -71
- package/dist/{ios-BP04pY5f.mjs → ios-Dd-hYJBh.mjs} +241 -234
- package/dist/{ios-device-CFXA46ez.mjs → ios-device-BiOsSVzd.mjs} +3 -2
- package/dist/{ios-device-CauFJiYx.mjs → ios-device-CtRdEVjx.mjs} +149 -52
- package/dist/launch-verify-CQS_Kg-C.mjs +365 -0
- package/dist/{logs-CakdBTtm.mjs → logs-MARIy6m9.mjs} +20 -15
- package/dist/metro-BWLOJ_Wr.mjs +409 -0
- package/dist/{named-ports-DzMOlmaX.mjs → named-ports-DiTfSz6p.mjs} +4 -4
- package/dist/{ndjson-BZwI1NBv.mjs → ndjson-CDY9IkVC.mjs} +23 -43
- package/dist/{ndjson-DvHSsNU9.d.mts → ndjson-Du3j0AQ9.d.mts} +1 -0
- package/dist/{ownership-Cp52QAK1.mjs → ownership-D2xbLVoz.mjs} +2 -3
- package/dist/{teardown-BJqEzPo_.mjs → ownership-DZjdx8Gq.mjs} +664 -612
- package/dist/{ports-BIbJaKNW.mjs → ports-CLSSFOuz.mjs} +3 -3
- package/dist/{process-output-Dl5uKE03.mjs → process-output-Fpl8PYi8.mjs} +1 -1
- package/dist/{project-DDZWf_16.mjs → project-BXZzOABt.mjs} +8 -3
- package/dist/{reload-BdypPbjM.mjs → reload-DoETJ3rl.mjs} +11 -13
- package/dist/{server-bare-BQkyxa_b.mjs → server-bare-DVbb7kXH.mjs} +2 -2
- package/dist/{server-expo-BVAA1OqJ.mjs → server-expo-BvOCeakp.mjs} +8 -7
- package/dist/server-expo-DBVTdult.mjs +2 -0
- package/dist/{settings-BXXorVM2.mjs → settings-DOugPlPz.mjs} +59 -159
- package/dist/settings-EWYNwe72.mjs +378 -0
- package/dist/settings.schema.json +1064 -0
- package/dist/simslim-Ca3487H6.mjs +436 -0
- package/dist/{slot-launch-ZUUe0TQP.mjs → slot-launch-C5cg0fmk.mjs} +4 -4
- package/dist/spawn-claims-CUleC2cM.mjs +49 -0
- package/dist/{start-CmxtOKjU.mjs → start-F9gv5dcC.mjs} +159 -114
- package/dist/start-VoFsPSes.mjs +2 -0
- package/dist/{state-DZobzxLW.mjs → state-WGdIbi_d.mjs} +5 -3
- package/dist/{stats-BYcZ6n1h.mjs → stats-BAgJaLA4.mjs} +66 -5
- package/dist/{stats-CLg5RuDS.mjs → stats-DBYJ5bxL.mjs} +4 -4
- package/dist/status-DLCD-o3s.mjs +764 -0
- package/dist/status-Dr4fRkym.mjs +216 -0
- package/dist/{guide-status-Craq9z0t.mjs → stim-installations-CINXy_su.mjs} +3 -122
- package/dist/{stop-C8JW6kKp.mjs → stop-Dj4pfmyv.mjs} +111 -31
- package/dist/{stop-B1kOpYei.mjs → stop-ZvOeTmKb.mjs} +2 -2
- package/dist/supervisor-run.d.mts +11 -2
- package/dist/supervisor-run.mjs +228 -42
- package/dist/{workspace-process-lock-BYfZz_S-.mjs → workspace-process-lock-BXA96myD.mjs} +14 -9
- package/dist/workspaces-BTUyFfkv.mjs +1003 -0
- package/dist/worktree-4aZg2NnI.mjs +2 -0
- package/dist/{worktree-CFLA-6MX.mjs → worktree-BMS7GeqB.mjs} +523 -61
- package/dist/{worktree-FG_ZKKdb.mjs → worktree-BlizKDpZ.mjs} +200 -54
- package/dist/{xcode-B6YaWeg0.mjs → xcode-B2tBkf0m.mjs} +66 -57
- package/package.json +6 -6
- package/skill/SKILL.md +2 -0
- package/dist/app-install-BDbsSgCl.mjs +0 -1175
- package/dist/dev-client-BmT-ioFI.mjs +0 -797
- package/dist/device-lease-Ffz_M5t0.mjs +0 -359
- package/dist/exec-B29XNuhP.mjs +0 -77
- package/dist/logs-query-BGL-Pl12.mjs +0 -290
- package/dist/metro-C7bcJ0uo.mjs +0 -233
- package/dist/ownership-wolE-0Do.mjs +0 -470
- package/dist/public-http-probe-CyMDrQ3c.mjs +0 -61
- package/dist/reclaim-KJ-0uWEF.mjs +0 -361
- package/dist/server-expo-YDDG3eP7.mjs +0 -2
- package/dist/spawn-entry-nCE6m770.mjs +0 -14
- package/dist/status-DdkLki47.mjs +0 -426
- package/dist/workspace-state-BT5E3SA2.mjs +0 -146
- package/dist/{metro-store-CDDZHtPl.mjs → metro-store-DzeWkfqW.mjs} +1 -1
|
@@ -1,37 +1,39 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { r as createNdjsonWriter } from "./ndjson-
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { a as
|
|
7
|
-
import { n as MODE_EXPO, p as writeWorkspaceLaunch, t as MODE_BARE } from "./state-
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import { n as
|
|
11
|
-
import { A as
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import { t as
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import { r as
|
|
22
|
-
import {
|
|
23
|
-
import { i as
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
import { n as
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
1
|
+
import { L as deviceSlotFileKey, R as deviceSlotKey, S as setDevice, T as withConfigLock, V as parseDeviceSlotOption, W as validateDeviceSlot, f as getProject, g as loadConfig, k as ownership_claim_exports, l as getConcurrencyLimits, w as upsertProject, x as saveConfig, z as deviceSlotPlatforms } from "./config-BEq0Qw_a.mjs";
|
|
2
|
+
import { t as getExecutor } from "./exec-DK8KaVHK.mjs";
|
|
3
|
+
import { r as createNdjsonWriter } from "./ndjson-CDY9IkVC.mjs";
|
|
4
|
+
import { O as workspaceDir, b as emulatorLogFile, c as signalProcessTree, h as recordWorkspaceUse, i as pidExists, j as workspaceLogsDir, m as readWorkspaceState, s as resolveProjectMetro, y as writeWorkspaceState } from "./metro-BWLOJ_Wr.mjs";
|
|
5
|
+
import { D as makeTemporaryDirectory, O as removeTemporaryEntry, s as gitCommonDir, y as repoRoot } from "./worktree-BlizKDpZ.mjs";
|
|
6
|
+
import { a as detectIsExpo, i as detectBundleId, l as projectShortcut, n as appProjectProblem, o as findProjectRoot, r as detectAndroidPackage } from "./project-BXZzOABt.mjs";
|
|
7
|
+
import { n as MODE_EXPO, p as writeWorkspaceLaunch, t as MODE_BARE } from "./state-WGdIbi_d.mjs";
|
|
8
|
+
import { t as withWorkspaceProcessLock } from "./workspace-process-lock-BXA96myD.mjs";
|
|
9
|
+
import { r as waitForChild, t as createLineReader } from "./process-output-Fpl8PYi8.mjs";
|
|
10
|
+
import { _ as stepTimer, c as launchErrorReport, f as refuseNoProject, g as stepClock, i as formatDuration, l as phaseLine, m as shortHash, n as appReadinessMessage, v as launchErrorPreview } from "./command-output-D5z2_bcF.mjs";
|
|
11
|
+
import { A as resolveBuild, C as fingerprintDiffRecord, D as providerDownloadPath, E as prepareProviderDownloadDir, F as assetDiffReason, I as captureAssetManifest, L as compareAssetManifests, M as storedAssetManifest, N as untrackedMissLine, O as providerUploadOutcome, P as untrackedNativeFiles, R as readAssetManifest, S as filesystemBuildCapability, T as fingerprintProject, _ as waitForBuild, b as describeFingerprintMiss, g as releaseBuildLock, j as storeBuild, k as refingerprintAfterMutation, m as acquireBuildLock, v as waitForSharedBuild, w as fingerprintDiffSuffix, y as buildCacheKey } from "./device-lease-CbEaIk7b.mjs";
|
|
12
|
+
import { n as NO_BUILD_PROGRESS, s as startBuildProgress } from "./build-progress-BXx-ZBe4.mjs";
|
|
13
|
+
import { B as resolvePhysicalDevice, E as listInstalledSystemImages, F as physicalDeviceModel, L as probeEmulatorSerial, O as memoizeEmulatorProbe, R as resetAdoptedAvd, W as waitForBoot, _ as emulatorDiskSpaceRemedy, a as androidPoolNoCandidatesRefusal, b as findBuildTool, i as androidPoolCandidates, n as androidDeviceAbi, o as androidSystemImageAbi, r as androidHome, s as androidToolCwd, t as androidBuildToolName, v as emulatorFailureRemedy, w as listAdbDevices, y as extractEmulatorFailure, z as resolveOwnedAvdSerial } from "./android-787HhP5w.mjs";
|
|
14
|
+
import { A as resolveCacheProviderConfig, E as publicUrlSetting, H as compilerCacheFallbackMessage, I as tunnelModeSetting, L as unknownSettingKeys, M as settingFile, O as remoteAndroidSetting, P as settingShapeErrors, S as metroWarmupUrlSetting, U as optimizationBuildProfile, V as artifactCachePolicy, W as resolveOptimizations, a as SETTING_SHAPE_REMEDY, d as cacheProviderSettingError, j as resolveSettings, r as REMOTE_DEVICE_BACKENDS, s as androidAvdConfigSettingError, u as androidDataPartitionSizeGbSettingError, z as detectProviders } from "./settings-DOugPlPz.mjs";
|
|
15
|
+
import { y as parkedMaxSetting } from "./ownership-DZjdx8Gq.mjs";
|
|
16
|
+
import { a as acquireBuildSlot, s as releaseBuildSlot } from "./in-use-CjUluqIX.mjs";
|
|
17
|
+
import { n as verifyCollectorOwnership, r as readCollectors } from "./ownership-D2xbLVoz.mjs";
|
|
18
|
+
import { t as cleanLine } from "./server-expo-BvOCeakp.mjs";
|
|
19
|
+
import { A as UPLOAD_TIMEOUT_MS, B as resolveRemote, F as isEasAuthFailureText, I as loadProjectProvider, M as checkEasAuth, N as easAuthNote, P as exitAfterFlush, V as uploadRemote, a as ensureMetroReachable, c as remoteAndroidDeps, j as cacheLevel, k as RESOLVE_TIMEOUT_MS, n as binOnPath, o as ensureRemoteBootOwned, u as resolveRemoteContext, w as ownedSessionName, z as resolveEasCliBin } from "./device-remote-Dy1jGj3U.mjs";
|
|
20
|
+
import { _ as launchAndroidReleaseApp, d as deviceShellArg, f as installAndroidApp, g as launchAndroidApp, o as androidAppProcess, p as installConflictKind, r as DEFAULT_METRO_PORT, s as androidDevClientUrl, t as ADB_INSTALL_TIMEOUT_MS } from "./app-install-CrJ6t_bv.mjs";
|
|
21
|
+
import { a as VERIFY_TIMEOUT_MS, d as verifyLaunch, i as RELEASE_VERIFY_WAIT_MS, l as unverifiedLaunchLines, n as LAUNCH_FATAL, r as LAUNCH_UNVERIFIED, t as LAUNCH_BUNDLING, u as verifyAndroidReleaseLaunch } from "./launch-verify-CQS_Kg-C.mjs";
|
|
22
|
+
import { d as isEasBuildFailure, f as resolveEasDevelopmentBuild, o as unknownAndroidSystemImageRefusal, r as checkDeviceCapacity } from "./simslim-Ca3487H6.mjs";
|
|
23
|
+
import { a as lostLine, c as releaseLeaseOnSignal, i as leaseExpiryText, l as runLease, n as DEBUG_VERIFY_STEP_MS, o as lostRefusal, r as acquireRunLease, s as parseDeviceWait, t as selectFromPool, u as waitFlagConflict } from "./device-pool-Di8Q-9Ro.mjs";
|
|
24
|
+
import { _ as isPhysicalDeviceRequest, b as warmMetro, c as runPrebuild, d as ensureOwnedDevice, f as AvdBootError, g as ensureWorkspaceStorageSafely, h as ensureDevServer, i as detectEntryFile, l as staleNativeDirRefusal, n as pickDevClientScheme, o as planPrebuild, p as AvdRecoveryError, s as recordPrebuild, t as devClientScheme, u as ensureBooted, v as launchOutcomeRecord } from "./dev-client-92U593KT.mjs";
|
|
25
|
+
import { t as budgetGate } from "./budget-BP4jZZL4.mjs";
|
|
26
|
+
import { f as spawnEntry, s as startDevServer } from "./start-F9gv5dcC.mjs";
|
|
27
|
+
import { a as statsProjectKey, i as recordRunStats, n as readRunEstimates, t as createRunRecorder } from "./stats-BAgJaLA4.mjs";
|
|
28
|
+
import { a as buildAndroid, c as CCACHE_NOT_RUN, f as resolveCcache, l as CCACHE_UNAVAILABLE, n as resolveAndroidCas, o as productFlavorRefusal, p as formatDiagnostic, r as resolveAndroidCompilerCache, s as readProductFlavors, u as ccacheActivityLine } from "./android-cas-CITmIeWY.mjs";
|
|
29
|
+
import { d as startBuildHeartbeat, f as tailLines, n as HEARTBEAT_INTERVAL_MS } from "./xcode-B2tBkf0m.mjs";
|
|
30
|
+
import { n as nativeRunCommand, t as launchSlotScope } from "./slot-launch-C5cg0fmk.mjs";
|
|
31
|
+
import { a as printNativeCrashReport, r as captureNativeCrashes, t as errorDiagnostics } from "./error-diagnostics-xxWKNO3-.mjs";
|
|
30
32
|
import { constants, copyFileSync, existsSync, mkdirSync, openSync, readFileSync, readdirSync, renameSync, rmSync } from "node:fs";
|
|
31
33
|
import { InvalidArgumentError } from "commander";
|
|
32
34
|
import { basename, dirname, isAbsolute, join, relative } from "node:path";
|
|
33
|
-
import { createWarnOnce, loadCacheProvider, resolveTieredBuild, storeTieredBuild } from "@stim-cli/cache";
|
|
34
35
|
import chalk from "chalk";
|
|
36
|
+
import { createWarnOnce, loadCacheProvider, resolveTieredBuild, storeTieredBuild } from "@stim-cli/cache";
|
|
35
37
|
//#region src/engine/apk-swap.ts
|
|
36
38
|
const ANDROID_BUNDLE_NAME = "index.android.bundle";
|
|
37
39
|
const LAST_LINES = 5;
|
|
@@ -437,7 +439,6 @@ function systemImageRefusal({ slot, flag, resolved, physical, remoteBackend, lis
|
|
|
437
439
|
function isReleaseVariant(variant) {
|
|
438
440
|
return typeof variant === "string" && /release$/i.test(variant.trim());
|
|
439
441
|
}
|
|
440
|
-
const NO_METRO = "STIM_NO_METRO";
|
|
441
442
|
const NO_FINGERPRINT = "STIM_NO_FINGERPRINT";
|
|
442
443
|
const NO_DEVICE = "STIM_NO_DEVICE";
|
|
443
444
|
const LAUNCH_FAILED = "STIM_LAUNCH_FAILED";
|
|
@@ -643,8 +644,8 @@ function fail$1(code, message, remedy, extra = {}) {
|
|
|
643
644
|
extra
|
|
644
645
|
};
|
|
645
646
|
}
|
|
646
|
-
async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo, device, physical, buildPlan, cacheProviderConfig, requestedBuildCache, easBuild, androidPackage: initialPackage, record, maxBuilds, progress }, { deviceAbi, fingerprint, untracked, resolveCached, storeCached, storedAssets, captureAssets, acquireLock, releaseLock, waitForBuild, loadProvider, easAuth, resolveRemoteBuild, uploadRemoteBuild, loadCacheProviderModule, acquireSlot, releaseSlot,
|
|
647
|
-
const { phase, out, estimates, stats } = progress;
|
|
647
|
+
async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo, device, physical, buildPlan, cacheProviderConfig, requestedBuildCache, easBuild, androidPackage: initialPackage, record, maxBuilds, progress }, { deviceAbi, fingerprint, untracked, resolveCached, storeCached, storedAssets, captureAssets, acquireLock, releaseLock, waitForBuild, loadProvider, easAuth, resolveRemoteBuild, uploadRemoteBuild, loadCacheProviderModule, acquireSlot, releaseSlot, planPrebuildFor, prebuild, build, ccacheFor, swapApk, readState, now }) {
|
|
648
|
+
const { phase, out, estimates, stats, step } = progress;
|
|
648
649
|
const { variant, release, profile: buildProfile, cas, cache: cachePolicy } = buildPlan;
|
|
649
650
|
const useBuildCache = cachePolicy.read;
|
|
650
651
|
let androidPackage = initialPackage;
|
|
@@ -715,6 +716,7 @@ async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo
|
|
|
715
716
|
stats.setCacheKey(cacheKey);
|
|
716
717
|
return true;
|
|
717
718
|
}
|
|
719
|
+
step("cache-lookup");
|
|
718
720
|
const fingerprintTimer = stepTimer(now);
|
|
719
721
|
try {
|
|
720
722
|
const computed = await fingerprint(root, { platform: PLATFORM });
|
|
@@ -841,88 +843,37 @@ async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo
|
|
|
841
843
|
if (!easBuild) await resolveRemoteArtifact();
|
|
842
844
|
let waitedForBuild = null;
|
|
843
845
|
let releasedWait = null;
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
}
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
const who = holder.projectRoot || "another workspace";
|
|
874
|
-
phase("build", `${who} is already building ${shortHash(hash)} (pid ${holder.pid})${holder.logFile ? ` -- tail ${holder.logFile}` : ""} -- stim guide lifecycle concurrency`);
|
|
875
|
-
let waited = null;
|
|
876
|
-
try {
|
|
877
|
-
const ceilingMs = WAIT_CEILING_MS - (now() - waitStarted);
|
|
878
|
-
if (ceilingMs <= 0) throw Object.assign(/* @__PURE__ */ new Error(`Waited ${formatDuration(now() - waitStarted)} for shared builds without an artifact; ${who} (pid ${holder.pid}) holds ${attempt.path}.`), {
|
|
879
|
-
code: "STIM_BUILD_WAIT_TIMEOUT",
|
|
880
|
-
lockPath: attempt.path
|
|
881
|
-
});
|
|
882
|
-
waited = await waitForBuild({
|
|
883
|
-
platform: PLATFORM,
|
|
884
|
-
key: cacheKey,
|
|
885
|
-
out,
|
|
886
|
-
ceilingMs
|
|
887
|
-
});
|
|
888
|
-
} catch (err) {
|
|
889
|
-
const refusal = (0, ownership_claim_exports.claimFailure)(err, "stim android");
|
|
890
|
-
if (refusal) {
|
|
891
|
-
phaseFailure = fail$1(refusal.code, refusal.message, refusal.remedy, { lastBuildStatus: true });
|
|
892
|
-
return false;
|
|
893
|
-
}
|
|
894
|
-
const wtErr = err;
|
|
895
|
-
if (wtErr?.code !== "STIM_BUILD_WAIT_TIMEOUT") throw err;
|
|
896
|
-
phaseFailure = fail$1("STIM_BUILD_WAIT_TIMEOUT", wtErr.message, `Check pid ${holder.pid}; if it is not really building, remove ${wtErr.lockPath} and run \`stim android\` again.`, { lastBuildStatus: true });
|
|
897
|
-
return false;
|
|
898
|
-
}
|
|
899
|
-
if (waited?.hit) {
|
|
900
|
-
apkPath = waited.hit ?? null;
|
|
901
|
-
record.cacheHit = "local";
|
|
902
|
-
waitedForBuild = {
|
|
903
|
-
pid: holder.pid,
|
|
904
|
-
ms: waited.waitedMs
|
|
905
|
-
};
|
|
906
|
-
phase("build", `waited ${formatDuration(waited.waitedMs)} for ${who}'s build -> installed from cache -- stim guide lifecycle concurrency`);
|
|
907
|
-
} else if (waited?.lockReleased) {
|
|
908
|
-
failedHolder = void 0;
|
|
909
|
-
releasedWait = {
|
|
910
|
-
facts: {
|
|
911
|
-
pid: holder.pid,
|
|
912
|
-
ms: waited.waitedMs
|
|
913
|
-
},
|
|
914
|
-
who
|
|
915
|
-
};
|
|
916
|
-
phase("build", `${who}'s build lock was released; rechecking this workspace before building`);
|
|
917
|
-
} else {
|
|
918
|
-
phase("build", chalk.yellow(`${who}'s build ended without an artifact (${waited?.builderFailed}); retrying the build lock`));
|
|
919
|
-
failedHolder = holder;
|
|
920
|
-
}
|
|
921
|
-
} else break;
|
|
846
|
+
if (!easBuild && useBuildCache && !apkPath) {
|
|
847
|
+
const shared = await waitForSharedBuild({
|
|
848
|
+
platform: PLATFORM,
|
|
849
|
+
key: cacheKey,
|
|
850
|
+
fingerprint: hash,
|
|
851
|
+
root,
|
|
852
|
+
logFile: buildLog,
|
|
853
|
+
command: "stim android",
|
|
854
|
+
acquire: acquireLock,
|
|
855
|
+
wait: waitForBuild,
|
|
856
|
+
now,
|
|
857
|
+
phase: (text) => {
|
|
858
|
+
step("wait");
|
|
859
|
+
phase("build", text);
|
|
860
|
+
},
|
|
861
|
+
warn: (text) => phase("build", chalk.yellow(text)),
|
|
862
|
+
out
|
|
863
|
+
});
|
|
864
|
+
if (shared.refusal) {
|
|
865
|
+
const { code, message, remedy } = shared.refusal;
|
|
866
|
+
phaseFailure = fail$1(code, message, remedy, { lastBuildStatus: true });
|
|
867
|
+
return refused();
|
|
868
|
+
}
|
|
869
|
+
buildLock = shared.lock;
|
|
870
|
+
releasedWait = shared.released;
|
|
871
|
+
if (shared.hit) {
|
|
872
|
+
apkPath = shared.hit.path;
|
|
873
|
+
record.cacheHit = "local";
|
|
874
|
+
waitedForBuild = shared.hit.waited;
|
|
922
875
|
}
|
|
923
|
-
return true;
|
|
924
876
|
}
|
|
925
|
-
if (!easBuild && !await waitForSharedBuild()) return refused();
|
|
926
877
|
let swapDir = null;
|
|
927
878
|
let swapFellBack = false;
|
|
928
879
|
const installableCachedApk = async (key, cachedPath) => {
|
|
@@ -975,8 +926,23 @@ async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo
|
|
|
975
926
|
}
|
|
976
927
|
phase("build", chalk.yellow(`could not take a build slot: ${err?.message || err}; building anyway`));
|
|
977
928
|
}
|
|
978
|
-
|
|
979
|
-
|
|
929
|
+
const prebuildPlan = planPrebuildFor(root, PLATFORM, {
|
|
930
|
+
isExpo,
|
|
931
|
+
fingerprint: hash,
|
|
932
|
+
sources: fingerprintSources
|
|
933
|
+
});
|
|
934
|
+
if (prebuildPlan === "refuse") {
|
|
935
|
+
const refusal = staleNativeDirRefusal(PLATFORM);
|
|
936
|
+
phaseFailure = fail$1(refusal.code, refusal.message, refusal.remedy, { lastBuildStatus: true });
|
|
937
|
+
return false;
|
|
938
|
+
}
|
|
939
|
+
if (prebuildPlan === "generate" || prebuildPlan === "regenerate") {
|
|
940
|
+
step("prebuild");
|
|
941
|
+
recordPrebuild(root, PLATFORM, null);
|
|
942
|
+
const pre = await prebuild(root, PLATFORM, writer, {
|
|
943
|
+
isExpo,
|
|
944
|
+
clean: prebuildPlan === "regenerate"
|
|
945
|
+
});
|
|
980
946
|
if (pre.failed) {
|
|
981
947
|
phaseFailure = fail$1(pre.code, pre.reason, pre.remedy, {
|
|
982
948
|
lastBuildStatus: true,
|
|
@@ -985,8 +951,8 @@ async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo
|
|
|
985
951
|
});
|
|
986
952
|
return false;
|
|
987
953
|
}
|
|
988
|
-
phase("prebuild",
|
|
989
|
-
androidPackage =
|
|
954
|
+
phase("prebuild", `${prebuildPlan === "generate" ? "android/ generated" : "android/ not generated from this fingerprint -> regenerated with --clean"} (${formatDuration(pre.durationMs)})`);
|
|
955
|
+
androidPackage = detectAndroidPackage(root) || androidPackage;
|
|
990
956
|
record.bundleId = androidPackage;
|
|
991
957
|
const after = await refingerprintAfterMutation({
|
|
992
958
|
projectRoot: root,
|
|
@@ -994,6 +960,7 @@ async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo
|
|
|
994
960
|
previousHash: hash,
|
|
995
961
|
fingerprint
|
|
996
962
|
});
|
|
963
|
+
if (after) recordPrebuild(root, PLATFORM, after.hash);
|
|
997
964
|
if (after?.moved) {
|
|
998
965
|
storeHash = after.hash;
|
|
999
966
|
storeSources = after.sources;
|
|
@@ -1017,6 +984,7 @@ async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo
|
|
|
1017
984
|
}
|
|
1018
985
|
}
|
|
1019
986
|
if (!apkPath) {
|
|
987
|
+
step("compile");
|
|
1020
988
|
phase("build", `compiling ${variant || "debug"} with Gradle`);
|
|
1021
989
|
const built = await build({
|
|
1022
990
|
root,
|
|
@@ -1147,7 +1115,7 @@ function tail(lines, n = FALLBACK_LINES) {
|
|
|
1147
1115
|
}
|
|
1148
1116
|
//#endregion
|
|
1149
1117
|
//#region src/commands/android/result.ts
|
|
1150
|
-
function androidFacts({ slot, serial, avdName = null, deviceName = null, systemImage = null, fingerprint, cacheKey = null, variant = null, metroPort = null, cacheHit, cacheSkipped = false, waitedForBuild = null, appPath, bundleId, installSkipped = false, launched, ccache = CCACHE_UNAVAILABLE, logs, debugHttpHost = null, debugHttpHostNote = null, devClientUrl = null, durationMs, lease }) {
|
|
1118
|
+
function androidFacts({ slot, serial, avdName = null, deviceName = null, systemImage = null, fingerprint, cacheKey = null, variant = null, metroPort = null, cacheHit, cacheSkipped = false, waitedForBuild = null, appPath, bundleId, installSkipped = false, launched, ccache = CCACHE_UNAVAILABLE, logs, debugHttpHost = null, debugHttpHostNote = null, devClientUrl = null, durationMs, lease, devServer = null }) {
|
|
1151
1119
|
return {
|
|
1152
1120
|
platform: PLATFORM,
|
|
1153
1121
|
...slot && slot !== "default" ? { slot } : {},
|
|
@@ -1175,7 +1143,8 @@ function androidFacts({ slot, serial, avdName = null, deviceName = null, systemI
|
|
|
1175
1143
|
devClientUrl: devClientUrl ?? null,
|
|
1176
1144
|
logs: logs ?? null,
|
|
1177
1145
|
durationMs: typeof durationMs === "number" && Number.isFinite(durationMs) ? durationMs : null,
|
|
1178
|
-
...lease === void 0 ? {} : { lease }
|
|
1146
|
+
...lease === void 0 ? {} : { lease },
|
|
1147
|
+
...devServer ? { devServer } : {}
|
|
1179
1148
|
};
|
|
1180
1149
|
}
|
|
1181
1150
|
function lastBuildRecord({ fingerprint, cacheKey, cacheHit, cacheSkipped = false, durationMs, appPath, bundleId, startedAt, status, errorCode = null, avdName = null, deviceName = null }) {
|
|
@@ -1213,7 +1182,7 @@ async function finishAndroidUpload(uploadPending, remote, phase) {
|
|
|
1213
1182
|
}
|
|
1214
1183
|
return false;
|
|
1215
1184
|
}
|
|
1216
|
-
function reportAndroidResult({ slot, json, useBuildCache, variant, release, metroCheck, metroPort, logsDir, serial, apkPath, androidPackage, installSkipped, record, waitedForBuild, remote, providerName, launchState, launchWarning, launched, ccache, durationMs, writer, emit, lease, recordRun }) {
|
|
1185
|
+
function reportAndroidResult({ slot, json, useBuildCache, variant, release, metroCheck, metroPort, logsDir, serial, apkPath, androidPackage, installSkipped, record, waitedForBuild, remote, providerName, launchState, launchWarning, launched, ccache, durationMs, writer, emit, lease, recordRun, reclaimed = [], devServer = null }) {
|
|
1217
1186
|
recordRun({
|
|
1218
1187
|
failed: false,
|
|
1219
1188
|
cacheHit: cacheLevel(record.cacheHit),
|
|
@@ -1243,16 +1212,20 @@ function reportAndroidResult({ slot, json, useBuildCache, variant, release, metr
|
|
|
1243
1212
|
ccache,
|
|
1244
1213
|
logs: logsDir,
|
|
1245
1214
|
durationMs,
|
|
1246
|
-
lease
|
|
1215
|
+
lease,
|
|
1216
|
+
devServer
|
|
1247
1217
|
});
|
|
1248
1218
|
writer.close();
|
|
1249
|
-
if (json) emit(JSON.stringify(
|
|
1219
|
+
if (json) emit(JSON.stringify(reclaimed.length ? {
|
|
1220
|
+
...facts,
|
|
1221
|
+
reclaimed
|
|
1222
|
+
} : facts));
|
|
1250
1223
|
else {
|
|
1251
1224
|
const summary = `${launchWarning ? "WARNING" : "OK"}: ${androidPackage} launched on ${serial}, ${release ? `${variant} (embedded JS, no Metro)` : `Metro port ${metroPort}`} (${cacheOutcome(record.cacheHit, remote?.name ?? providerName)})`;
|
|
1252
1225
|
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);
|
|
1253
1226
|
const deviceName = record.avdName || record.deviceName || serial;
|
|
1254
1227
|
const cacheResult = useBuildCache ? cacheOutcome(record.cacheHit, remote?.name ?? providerName) : "bypassed; built";
|
|
1255
|
-
const metroResult = release ? `embedded (${variant})` : !metroCheck ? `check skipped on port ${metroPort}` : launchState === "unverified" ? `state unverified on port ${metroPort}` : `running on port ${metroPort}`;
|
|
1228
|
+
const metroResult = release ? `embedded (${variant})` : !metroCheck ? `check skipped on port ${metroPort}` : launchState === "unverified" ? `state unverified on port ${metroPort}` : `running on port ${metroPort}${devServer ? ` (started: ${devServer.reason})` : ""}`;
|
|
1256
1229
|
emit([
|
|
1257
1230
|
outcome,
|
|
1258
1231
|
phaseLine("device", `${deviceName} (${serial})`),
|
|
@@ -1508,7 +1481,7 @@ async function resolveInstallSerial({ root, slot, device, physical, remoteDevice
|
|
|
1508
1481
|
});
|
|
1509
1482
|
return resolved.serial;
|
|
1510
1483
|
}
|
|
1511
|
-
async function finishAndroidRun({ lease, releaseLease, root, slot, json, metroCheck, useBuildCache, variant, release, isExpo, metroPort, logsDir, emuLog, device, physical, remoteDevice, bootPromise, resolveAvdSerial, waitForDeviceBoot, bootDuration, apkPath, androidPackage: initialPackage, swapDir, record, waitedForBuild, ccache, uploadPending, providerUpload, providerName, remote, abandonedRemote: remoteWasAbandoned, started, startedAt, writer, phase, fail, readApkPackage, install, launch, launchRelease, resolveDevClientScheme, verifyLaunched, verifyReleaseLaunched, spawn, kill, pidAlive, verifyCollector, writeLaunch, writeState, now, out, emit, recordRun }) {
|
|
1484
|
+
async function finishAndroidRun({ lease, releaseLease, root, slot, json, metroCheck, useBuildCache, variant, release, isExpo, metroPort, logsDir, emuLog, device, physical, remoteDevice, bootPromise, resolveAvdSerial, waitForDeviceBoot, bootDuration, apkPath, androidPackage: initialPackage, swapDir, record, waitedForBuild, ccache, uploadPending, providerUpload, providerName, remote, abandonedRemote: remoteWasAbandoned, started, startedAt, writer, phase, fail, readApkPackage, install, launch, launchRelease, resolveDevClientScheme, verifyLaunched, verifyReleaseLaunched, spawn, kill, pidAlive, verifyCollector, writeLaunch, writeState, now, out, emit, recordRun, reclaimed, devServer, enterPhase }) {
|
|
1512
1485
|
let androidPackage = initialPackage;
|
|
1513
1486
|
let leaseWarned = false;
|
|
1514
1487
|
const raiseLeaseFor = (boundMs, beforeInstall) => {
|
|
@@ -1524,6 +1497,7 @@ async function finishAndroidRun({ lease, releaseLease, root, slot, json, metroCh
|
|
|
1524
1497
|
}
|
|
1525
1498
|
return null;
|
|
1526
1499
|
};
|
|
1500
|
+
enterPhase("install");
|
|
1527
1501
|
const booted = await bootPromise;
|
|
1528
1502
|
const runCommand = nativeRunCommand("android", slot, {
|
|
1529
1503
|
physical,
|
|
@@ -1575,9 +1549,12 @@ async function finishAndroidRun({ lease, releaseLease, root, slot, json, metroCh
|
|
|
1575
1549
|
allowUninstall: release || adopting
|
|
1576
1550
|
});
|
|
1577
1551
|
if (installed.failed) {
|
|
1578
|
-
const
|
|
1579
|
-
|
|
1580
|
-
|
|
1552
|
+
const installRemedy = remoteDevice?.failureRemedy() ?? localInstallRemedy({
|
|
1553
|
+
reason: installed.reason,
|
|
1554
|
+
packageName: androidPackage,
|
|
1555
|
+
serial,
|
|
1556
|
+
useBuildCache
|
|
1557
|
+
});
|
|
1581
1558
|
return fail(installed.code || "STIM_INSTALL_FAILED", installed.reason, installRemedy, { lastBuildStatus: true });
|
|
1582
1559
|
}
|
|
1583
1560
|
if (adopting) try {
|
|
@@ -1613,6 +1590,7 @@ async function finishAndroidRun({ lease, releaseLease, root, slot, json, metroCh
|
|
|
1613
1590
|
if (!androidPackage) return fail(LAUNCH_FAILED, "Could not determine this app's Android package name, so there is nothing to launch.", "Set `expo.android.package` in app.json / app.config.js, or `namespace` in android/app/build.gradle.", { lastBuildStatus: true });
|
|
1614
1591
|
if (physical) raiseLeaseFor(0, false);
|
|
1615
1592
|
const scheme = release ? void 0 : resolveDevClientScheme(root, apkPath);
|
|
1593
|
+
enterPhase("launch");
|
|
1616
1594
|
const launchTimer = stepTimer(now);
|
|
1617
1595
|
const launchedAt = now();
|
|
1618
1596
|
writer.write({
|
|
@@ -1651,7 +1629,7 @@ async function finishAndroidRun({ lease, releaseLease, root, slot, json, metroCh
|
|
|
1651
1629
|
appId: androidPackage,
|
|
1652
1630
|
since: launchedAt
|
|
1653
1631
|
}, logsDir, (line) => phase("launch", chalk.red(line)), Boolean(remoteDevice));
|
|
1654
|
-
return fail(launched.code || "STIM_LAUNCH_FAILED", launched.reason,
|
|
1632
|
+
return fail(launched.code || "STIM_LAUNCH_FAILED", launched.reason, launchRemedy(remoteDevice, serial, androidPackage), { lastBuildStatus: true });
|
|
1655
1633
|
}
|
|
1656
1634
|
writer.write({
|
|
1657
1635
|
src: "build",
|
|
@@ -1671,17 +1649,15 @@ async function finishAndroidRun({ lease, releaseLease, root, slot, json, metroCh
|
|
|
1671
1649
|
} catch (error) {
|
|
1672
1650
|
out(phaseLine("state", chalk.yellow(`could not record Android launch: ${error?.message || error}`)));
|
|
1673
1651
|
}
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
});
|
|
1684
|
-
}
|
|
1652
|
+
if (!release) reportMetroRoute({
|
|
1653
|
+
launched,
|
|
1654
|
+
remote: Boolean(remoteDevice),
|
|
1655
|
+
metroPort,
|
|
1656
|
+
androidPackage,
|
|
1657
|
+
serial,
|
|
1658
|
+
phase,
|
|
1659
|
+
writer
|
|
1660
|
+
});
|
|
1685
1661
|
if (launched.devClientNote) {
|
|
1686
1662
|
phase("metro", chalk.yellow(launched.devClientNote));
|
|
1687
1663
|
writer.write({
|
|
@@ -1704,7 +1680,21 @@ async function finishAndroidRun({ lease, releaseLease, root, slot, json, metroCh
|
|
|
1704
1680
|
out
|
|
1705
1681
|
});
|
|
1706
1682
|
const remoteRelease = Boolean(remoteDevice && release);
|
|
1707
|
-
if (
|
|
1683
|
+
if (remoteDevice) {
|
|
1684
|
+
killPreviousCollector(root, {
|
|
1685
|
+
slot,
|
|
1686
|
+
kill,
|
|
1687
|
+
isAlive: pidAlive,
|
|
1688
|
+
verify: verifyCollector,
|
|
1689
|
+
note: (line) => out(phaseLine("logs", line))
|
|
1690
|
+
});
|
|
1691
|
+
writer.write({
|
|
1692
|
+
src: "build",
|
|
1693
|
+
level: "info",
|
|
1694
|
+
event: "collector_skipped",
|
|
1695
|
+
msg: `remote session ${serial}: device logs come from agent-device/EAS, so no adb logcat collector is attached`
|
|
1696
|
+
});
|
|
1697
|
+
} else {
|
|
1708
1698
|
if (physical) raiseLeaseFor(0, false);
|
|
1709
1699
|
const collectorPid = await startCollector({
|
|
1710
1700
|
root,
|
|
@@ -1780,7 +1770,9 @@ async function finishAndroidRun({ lease, releaseLease, root, slot, json, metroCh
|
|
|
1780
1770
|
writer,
|
|
1781
1771
|
emit,
|
|
1782
1772
|
lease: physical ? leaseFacts : void 0,
|
|
1783
|
-
recordRun
|
|
1773
|
+
recordRun,
|
|
1774
|
+
reclaimed,
|
|
1775
|
+
devServer
|
|
1784
1776
|
});
|
|
1785
1777
|
if (remoteWasAbandoned || uploadWasAbandoned) exitAfterFlush(0);
|
|
1786
1778
|
return {
|
|
@@ -1788,6 +1780,33 @@ async function finishAndroidRun({ lease, releaseLease, root, slot, json, metroCh
|
|
|
1788
1780
|
facts
|
|
1789
1781
|
};
|
|
1790
1782
|
}
|
|
1783
|
+
function localInstallRemedy({ reason, packageName, serial, useBuildCache }) {
|
|
1784
|
+
const conflict = installConflictKind(reason);
|
|
1785
|
+
if (!conflict) return `Check that ${serial} is still connected (\`adb devices\`) and has room for the APK.`;
|
|
1786
|
+
const rerun = useBuildCache ? " Then run this command again; it installs the APK from cache without building it again." : " Then run this command again.";
|
|
1787
|
+
return `${packageName} is already installed on ${serial} ` + (conflict === "signature" ? "with a different signer" : "at a higher versionCode") + `. Uninstall it first (\`adb -s ${serial} uninstall ${packageName}\`); its data goes with it.` + rerun;
|
|
1788
|
+
}
|
|
1789
|
+
function launchRemedy(remoteDevice, serial, packageName) {
|
|
1790
|
+
return remoteDevice?.failureRemedy() ?? `Check the app installed correctly (\`adb -s ${serial} shell pm list packages ${packageName}\`).`;
|
|
1791
|
+
}
|
|
1792
|
+
function reportMetroRoute({ launched, remote, metroPort, androidPackage, serial, phase, writer }) {
|
|
1793
|
+
if (remote) {
|
|
1794
|
+
phase("metro", `public origin ${launched.jsLocation}`);
|
|
1795
|
+
return;
|
|
1796
|
+
}
|
|
1797
|
+
const reversedSummary = (launched.reversed ?? []).join(", ") || `tcp:${metroPort}->tcp:${metroPort}`;
|
|
1798
|
+
if (launched.debugHttpHost) {
|
|
1799
|
+
phase("metro", `debug_http_host ${launched.debugHttpHost} + adb reverse ${reversedSummary}`);
|
|
1800
|
+
return;
|
|
1801
|
+
}
|
|
1802
|
+
phase("metro", chalk.yellow(`adb reverse ${reversedSummary}; ${launched.debugHttpHostNote || "debug_http_host not written"}`));
|
|
1803
|
+
writer.write({
|
|
1804
|
+
src: "build",
|
|
1805
|
+
level: "warn",
|
|
1806
|
+
event: "debug_http_host_failed",
|
|
1807
|
+
msg: `debug_http_host was not written for ${androidPackage} on ${serial}: ${launched.debugHttpHostNote || "unknown reason"}`
|
|
1808
|
+
});
|
|
1809
|
+
}
|
|
1791
1810
|
function diagnoseBootFailure(booted, logFile, runCommand, physical) {
|
|
1792
1811
|
return noDeviceDiagnostic({
|
|
1793
1812
|
reason: booted.reason ?? "The emulator did not boot.",
|
|
@@ -1858,8 +1877,6 @@ function resolveAndroidRunPlan({ settings, settingsContext, slot, easProfile, va
|
|
|
1858
1877
|
if (dataPartitionSizeError) return fail("STIM_BAD_ARG", dataPartitionSizeError, "Set android.dataPartitionSizeGb to a whole number of GiB from 6 through 16384.");
|
|
1859
1878
|
const avdConfigError = validateAvdConfig(settings, root);
|
|
1860
1879
|
if (avdConfigError) return fail("STIM_BAD_ARG", avdConfigError, "Use only documented android.avdConfig keys, or an android.avdConfigFile fragment contained by the app directory.");
|
|
1861
|
-
const remoteSettingError = remoteDeviceSettingError(settings);
|
|
1862
|
-
if (remoteSettingError) return fail("STIM_BAD_ARG", remoteSettingError, `Set ios.remote and android.remote to one of: ${REMOTE_DEVICE_BACKENDS.join(", ")}.`);
|
|
1863
1880
|
const systemImage = resolveSystemImage(systemImageFlag, settings);
|
|
1864
1881
|
const variant = easProfile !== void 0 ? "debug" : resolveVariant(variantFlag, settings);
|
|
1865
1882
|
const flavorRefusal = productFlavorRefusal({
|
|
@@ -1932,36 +1949,51 @@ function androidCommand(program) {
|
|
|
1932
1949
|
registerAndroid(program);
|
|
1933
1950
|
}
|
|
1934
1951
|
function registerAndroid(program) {
|
|
1935
|
-
program.command("android").description("Build (or install from the shared cache), install and launch this workspace's Android app on its owned emulator, wired to the reserved Metro port.
|
|
1952
|
+
program.command("android").description("Build (or install from the shared cache), install and launch this workspace's Android app on its owned emulator, wired to the reserved Metro port. A Debug run starts the dev server when it is not running.").option("--eas-profile <name>", "Download a matching EAS development build; on a miss, print the build command without running it").option("--slot <name>", "Reusable device slot within this workspace (default: default)", parseDeviceSlotOption).option("--json", "Emit the facts as a single JSON line on stdout; every other line goes to stderr").option("--no-metro-check", "Skip the reserved-port Metro health check and do not start it (the app will load no bundle unless something else serves it)").option("--no-build-cache", "Build fresh, ignoring cached artifacts (local and the project's build-cache provider); the fresh build still replaces the cache entry").option("--variant <name>", "Gradle variant to assemble and install (e.g. productionDebug on a flavored project); overrides the android.variant setting. A variant ending in Release embeds the JS bundle and skips Metro entirely. Default: debug").option("--system-image <id>", "Android system image to create this workspace's owned AVD from, as the sdkmanager package id (e.g. \"system-images;android-36;google_apis;arm64-v8a\"). Overrides the android.systemImage setting for this invocation. An unknown id refuses with STIM_BAD_ARG and prints the installed images.").option("--device [serial]", "Install and launch on a connected physical device instead of this workspace's owned emulator. With no serial, the first connected device this workspace can lease is used. Stim never creates, boots, or deletes a physical device.").option("--remote <backend>", "Install and launch on a remote device with proxy or EAS. Builds are local unless --eas-profile selects an existing EAS build.", (value) => {
|
|
1936
1953
|
if (REMOTE_DEVICE_BACKENDS.includes(value)) return value;
|
|
1937
1954
|
throw new InvalidArgumentError(`expected one of: ${REMOTE_DEVICE_BACKENDS.join(", ")}`);
|
|
1938
1955
|
}).option("--wait <seconds>", "How long to wait for another workspace to release the device it leases, before refusing with STIM_DEVICE_BUSY (default 60, 0 refuses at once). Only with --device.").option("--no-wait", "Install on a device another workspace leases instead of waiting: this run takes no lease and, when both workspaces build the same app id, the install terminates the holder's running app. Only with --device.").action(async (opts) => {
|
|
1939
1956
|
const root = findProjectRoot(process.cwd());
|
|
1940
1957
|
if (!root) {
|
|
1941
|
-
|
|
1942
|
-
process.exit(1);
|
|
1958
|
+
refuseNoProject({ json: Boolean(opts.json) });
|
|
1943
1959
|
return;
|
|
1944
1960
|
}
|
|
1945
|
-
if (!(await withWorkspaceProcessLock(workspaceDir(root), "native-run", () =>
|
|
1946
|
-
root
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1961
|
+
if (!(await withWorkspaceProcessLock(workspaceDir(root), "native-run", async (claim) => {
|
|
1962
|
+
recordWorkspaceUse(root);
|
|
1963
|
+
const progress = startBuildProgress({
|
|
1964
|
+
root,
|
|
1965
|
+
platform: "android",
|
|
1966
|
+
slot: opts.slot ?? "default",
|
|
1967
|
+
claim,
|
|
1968
|
+
note: (line) => console.error(chalk.dim(line))
|
|
1969
|
+
});
|
|
1970
|
+
try {
|
|
1971
|
+
return await runAndroid({
|
|
1972
|
+
root,
|
|
1973
|
+
slot: opts.slot,
|
|
1974
|
+
easProfile: opts.easProfile,
|
|
1975
|
+
json: Boolean(opts.json),
|
|
1976
|
+
metroCheck: opts.metroCheck !== false,
|
|
1977
|
+
useBuildCache: opts.buildCache !== false,
|
|
1978
|
+
variant: opts.variant ?? null,
|
|
1979
|
+
systemImage: opts.systemImage ?? null,
|
|
1980
|
+
remoteDevice: opts.remote ?? null,
|
|
1981
|
+
device: opts.device ?? null,
|
|
1982
|
+
wait: opts.wait,
|
|
1983
|
+
waitConflict: waitFlagConflict(process.argv),
|
|
1984
|
+
progress
|
|
1985
|
+
});
|
|
1986
|
+
} finally {
|
|
1987
|
+
progress.clear();
|
|
1988
|
+
}
|
|
1989
|
+
}, {
|
|
1959
1990
|
external: true,
|
|
1960
|
-
waitMs: 18e5
|
|
1991
|
+
waitMs: 18e5,
|
|
1992
|
+
declareSpawns: true
|
|
1961
1993
|
})).ok) process.exit(1);
|
|
1962
1994
|
});
|
|
1963
1995
|
}
|
|
1964
|
-
function resolveRunAndroidOptions({ root, json = false, remoteDevice: commandRemoteBackend = null, resolveSettingsFor = resolveSettings, resolveRemoteDeviceContext = resolveRemoteContext, remoteDeviceDeps: makeRemoteDeviceDeps = remoteAndroidDeps, resolveEasBin = resolveEasCliBin, ensureMetroReachable: ensureRemoteMetro = ensureMetroReachable, ensureRemoteBootOwned: ensureRemoteOwned = ensureRemoteBootOwned, detectRemoteProviders = detectProviders, metroCheck = true, useBuildCache = true, easProfile, resolveEasDevelopmentBuild: resolveEasBuild = resolveEasDevelopmentBuild, variant: variantFlag = null, systemImage: systemImageFlag = null, device: deviceFlag = null, wait: waitFlag = void 0, waitConflict = false, acquireLease = acquireRunLease, makeRunLease = runLease, selectPool = selectFromPool, onLeaseSignal = releaseLeaseOnSignal, listDevices = listAdbDevices, deviceModel = physicalDeviceModel, deviceAbi = androidDeviceAbi, isEmulatorDevice = probeEmulatorSerial, readApkPackage = (apkPath) => apkPackage(dumpApkManifest(apkPath)), getLimits = getConcurrencyLimits, checkCapacity = checkDeviceCapacity, acquireSlot = acquireBuildSlot, releaseSlot = releaseBuildSlot, ensureDevice = ensureOwnedDevice, listSystemImages = listInstalledSystemImages, ensureDeviceBooted = ensureBooted, resolveAvdSerial = resolveOwnedAvdSerial, waitForDeviceBoot = waitForBoot, resolveMetro = resolveProjectMetro, warmMetro: prewarmMetro = warmMetro,
|
|
1996
|
+
function resolveRunAndroidOptions({ root, json = false, remoteDevice: commandRemoteBackend = null, resolveSettingsFor = resolveSettings, resolveRemoteDeviceContext = resolveRemoteContext, remoteDeviceDeps: makeRemoteDeviceDeps = remoteAndroidDeps, resolveEasBin = resolveEasCliBin, ensureMetroReachable: ensureRemoteMetro = ensureMetroReachable, ensureRemoteBootOwned: ensureRemoteOwned = ensureRemoteBootOwned, detectRemoteProviders = detectProviders, metroCheck = true, useBuildCache = true, easProfile, resolveEasDevelopmentBuild: resolveEasBuild = resolveEasDevelopmentBuild, variant: variantFlag = null, systemImage: systemImageFlag = null, device: deviceFlag = null, wait: waitFlag = void 0, waitConflict = false, acquireLease = acquireRunLease, makeRunLease = runLease, selectPool = selectFromPool, onLeaseSignal = releaseLeaseOnSignal, listDevices = listAdbDevices, deviceModel = physicalDeviceModel, deviceAbi = androidDeviceAbi, isEmulatorDevice = probeEmulatorSerial, readApkPackage = (apkPath) => apkPackage(dumpApkManifest(apkPath)), getLimits = getConcurrencyLimits, checkCapacity = checkDeviceCapacity, acquireSlot = acquireBuildSlot, releaseSlot = releaseBuildSlot, ensureDevice = ensureOwnedDevice, listSystemImages = listInstalledSystemImages, ensureDeviceBooted = ensureBooted, resolveAvdSerial = resolveOwnedAvdSerial, waitForDeviceBoot = waitForBoot, resolveMetro = resolveProjectMetro, warmMetro: prewarmMetro = warmMetro, startServer = startDevServer, readState = readWorkspaceState, pidAlive = pidExists, verifyCollector = verifyCollectorOwnership, verifyLaunched = verifyLaunch, ensureStorage = ensureWorkspaceStorageSafely, fingerprint = fingerprintProject, untracked = untrackedNativeFiles, resolveCached = resolveBuild, storeCached = storeBuild, storedAssets = storedAssetManifest, captureAssets = captureAssetManifest, acquireLock = acquireBuildLock, releaseLock = releaseBuildLock, waitForBuild: waitForBuild$1 = waitForBuild, loadProvider = loadProjectProvider, easAuth = checkEasAuth, resolveRemoteBuild = resolveRemote, uploadRemoteBuild = uploadRemote, resolveCacheProvider = resolveCacheProviderConfig, loadCacheProviderModule = loadCacheProvider, planPrebuildFor = planPrebuild, prebuild = runPrebuild, build = buildAndroid, ccacheFor = resolveCcache, install = installAndroidApp, launch = launchAndroidApp, launchRelease = launchAndroidReleaseApp, verifyReleaseLaunched = verifyAndroidReleaseLaunch, swapApk = swapApkBundle, resolveDevClientScheme = androidDevClientScheme, spawn = (cmd, args, opts) => getExecutor().spawn(cmd, args, opts), kill = (pid, signal) => process.kill(pid, signal), createWriter = createNdjsonWriter, writeLaunch = writeWorkspaceLaunch, writeState = writeWorkspaceState, recordStats = recordRunStats, readEstimates = readRunEstimates, now = Date.now, out = (line) => console.error(line), emit = (line) => console.log(line) } = {}) {
|
|
1965
1997
|
return {
|
|
1966
1998
|
root,
|
|
1967
1999
|
json,
|
|
@@ -2002,7 +2034,7 @@ function resolveRunAndroidOptions({ root, json = false, remoteDevice: commandRem
|
|
|
2002
2034
|
waitForDeviceBoot,
|
|
2003
2035
|
resolveMetro,
|
|
2004
2036
|
prewarmMetro,
|
|
2005
|
-
|
|
2037
|
+
startServer,
|
|
2006
2038
|
readState,
|
|
2007
2039
|
pidAlive,
|
|
2008
2040
|
verifyCollector,
|
|
@@ -2023,7 +2055,7 @@ function resolveRunAndroidOptions({ root, json = false, remoteDevice: commandRem
|
|
|
2023
2055
|
uploadRemoteBuild,
|
|
2024
2056
|
resolveCacheProvider,
|
|
2025
2057
|
loadCacheProviderModule,
|
|
2026
|
-
|
|
2058
|
+
planPrebuildFor,
|
|
2027
2059
|
prebuild,
|
|
2028
2060
|
build,
|
|
2029
2061
|
ccacheFor,
|
|
@@ -2055,6 +2087,10 @@ function androidSlotOptions(options) {
|
|
|
2055
2087
|
...args,
|
|
2056
2088
|
slot
|
|
2057
2089
|
}),
|
|
2090
|
+
ensureDeviceBooted: (args) => base.ensureDeviceBooted({
|
|
2091
|
+
...args,
|
|
2092
|
+
slot
|
|
2093
|
+
}),
|
|
2058
2094
|
checkCapacity: (args) => base.checkCapacity({
|
|
2059
2095
|
...args,
|
|
2060
2096
|
slot
|
|
@@ -2094,7 +2130,8 @@ function avdSetupFailure(error, root, logFile) {
|
|
|
2094
2130
|
};
|
|
2095
2131
|
}
|
|
2096
2132
|
async function runAndroid(options = {}) {
|
|
2097
|
-
let { root, json, commandRemoteBackend, resolveSettingsFor, resolveRemoteDeviceContext, makeRemoteDeviceDeps, resolveEasBin, ensureRemoteMetro, ensureRemoteOwned, detectRemoteProviders, metroCheck, useBuildCache: requestedBuildCache, easProfile, resolveEasBuild, variantFlag, systemImageFlag, deviceFlag, waitFlag, waitConflict, acquireLease, makeRunLease, selectPool, onLeaseSignal, listDevices, deviceModel, deviceAbi, isEmulatorDevice, readApkPackage, getLimits, checkCapacity, acquireSlot, releaseSlot, ensureDevice, listSystemImages, ensureDeviceBooted, resolveAvdSerial, waitForDeviceBoot, resolveMetro, prewarmMetro,
|
|
2133
|
+
let { root, json, commandRemoteBackend, resolveSettingsFor, resolveRemoteDeviceContext, makeRemoteDeviceDeps, resolveEasBin, ensureRemoteMetro, ensureRemoteOwned, detectRemoteProviders, metroCheck, useBuildCache: requestedBuildCache, easProfile, resolveEasBuild, variantFlag, systemImageFlag, deviceFlag, waitFlag, waitConflict, acquireLease, makeRunLease, selectPool, onLeaseSignal, listDevices, deviceModel, deviceAbi, isEmulatorDevice, readApkPackage, getLimits, checkCapacity, acquireSlot, releaseSlot, ensureDevice, listSystemImages, ensureDeviceBooted, resolveAvdSerial, waitForDeviceBoot, resolveMetro, prewarmMetro, startServer, readState, pidAlive, verifyCollector, verifyLaunched, ensureStorage, fingerprint, untracked, resolveCached, storeCached, storedAssets, captureAssets, acquireLock, releaseLock, waitForBuild, loadProvider, easAuth, resolveRemoteBuild, uploadRemoteBuild, resolveCacheProvider, loadCacheProviderModule, planPrebuildFor, prebuild, build, ccacheFor, install, launch, launchRelease, verifyReleaseLaunched, swapApk, resolveDevClientScheme, spawn, kill, createWriter, writeLaunch, writeState, recordStats, readEstimates, now, out, emit } = androidSlotOptions(options);
|
|
2134
|
+
const progress = options.progress ?? NO_BUILD_PROGRESS;
|
|
2098
2135
|
const slot = validateDeviceSlot(options.slot);
|
|
2099
2136
|
const started = now();
|
|
2100
2137
|
const startedAt = new Date(started).toISOString();
|
|
@@ -2159,10 +2196,12 @@ async function runAndroid(options = {}) {
|
|
|
2159
2196
|
platform: PLATFORM,
|
|
2160
2197
|
write: recordStats,
|
|
2161
2198
|
now,
|
|
2162
|
-
note: (line) => out(phaseLine("stats", chalk.dim(line)))
|
|
2199
|
+
note: (line) => out(phaseLine("stats", chalk.dim(line))),
|
|
2200
|
+
phases: () => progress.durations()
|
|
2163
2201
|
});
|
|
2164
2202
|
const recordRun = stats.record;
|
|
2165
2203
|
let ccacheActivity = CCACHE_NOT_RUN;
|
|
2204
|
+
let reclaimed = [];
|
|
2166
2205
|
const fail = (code, message, remedy, { lastBuildStatus = false, diagnostics = [], lines = [], logPath = null, lease } = {}) => {
|
|
2167
2206
|
if (lastBuildStatus) persistLastBuild({
|
|
2168
2207
|
writeState,
|
|
@@ -2188,7 +2227,8 @@ async function runAndroid(options = {}) {
|
|
|
2188
2227
|
message,
|
|
2189
2228
|
remedy: remedy ?? null,
|
|
2190
2229
|
...ccacheActivity.status === "not-run" ? {} : { ccache: ccacheActivity },
|
|
2191
|
-
...lease === void 0 ? {} : { lease }
|
|
2230
|
+
...lease === void 0 ? {} : { lease },
|
|
2231
|
+
...reclaimed.length ? { reclaimed } : {}
|
|
2192
2232
|
}));
|
|
2193
2233
|
writer.close();
|
|
2194
2234
|
return {
|
|
@@ -2243,6 +2283,19 @@ async function runAndroid(options = {}) {
|
|
|
2243
2283
|
const useBuildCache = cachePolicy.read;
|
|
2244
2284
|
const physical = target.kind === "physical";
|
|
2245
2285
|
const remoteBackend = target.kind === "remote" ? target.backend : null;
|
|
2286
|
+
const budget = await (options.checkBudget ?? budgetGate)({
|
|
2287
|
+
root,
|
|
2288
|
+
note: out
|
|
2289
|
+
});
|
|
2290
|
+
reclaimed = budget.reclaimed;
|
|
2291
|
+
if (budget.refusal) return fail(budget.refusal.code, budget.refusal.message, budget.refusal.remedy);
|
|
2292
|
+
const remoteContext = remoteBackend ? await resolveRemoteDeviceContext({
|
|
2293
|
+
root,
|
|
2294
|
+
platform: PLATFORM,
|
|
2295
|
+
backend: remoteBackend,
|
|
2296
|
+
easBin: resolveEasBin(root)?.file ?? null
|
|
2297
|
+
}) : null;
|
|
2298
|
+
if (remoteContext && "failed" in remoteContext) return fail(remoteContext.code ?? "STIM_NO_REMOTE_SESSION", remoteContext.failed, remoteContext.remedy);
|
|
2246
2299
|
const easBuild = await resolveEasBuild({
|
|
2247
2300
|
root,
|
|
2248
2301
|
platform: PLATFORM,
|
|
@@ -2267,46 +2320,37 @@ async function runAndroid(options = {}) {
|
|
|
2267
2320
|
let remoteDevice = null;
|
|
2268
2321
|
const reservedPort = project?.metroPort ?? null;
|
|
2269
2322
|
let metroPort = null;
|
|
2323
|
+
let devServer = null;
|
|
2270
2324
|
let phaseFailure = null;
|
|
2271
2325
|
async function resolveMetroPort() {
|
|
2272
2326
|
if (release) phase("metro", `skipped (${variant}: the JS bundle is embedded, no dev server is used)`);
|
|
2273
2327
|
else if (metroCheck) {
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
}), noMetroRemedy({
|
|
2288
|
-
port: reservedPort,
|
|
2289
|
-
supervisor,
|
|
2290
|
-
supervisorAlive
|
|
2291
|
-
}));
|
|
2328
|
+
const gate = await ensureDevServer({
|
|
2329
|
+
root,
|
|
2330
|
+
port: reservedPort,
|
|
2331
|
+
settings,
|
|
2332
|
+
remote: remoteContext !== null,
|
|
2333
|
+
note: out,
|
|
2334
|
+
resolve: resolveMetro,
|
|
2335
|
+
start: startServer,
|
|
2336
|
+
readState
|
|
2337
|
+
});
|
|
2338
|
+
reclaimed = [...reclaimed, ...gate.reclaimed];
|
|
2339
|
+
if (!gate.ok) {
|
|
2340
|
+
phaseFailure = fail(gate.code, gate.message, gate.remedy, { lines: gate.lines });
|
|
2292
2341
|
return false;
|
|
2293
2342
|
}
|
|
2294
|
-
|
|
2343
|
+
metroPort = gate.port;
|
|
2344
|
+
devServer = gate.devServer;
|
|
2345
|
+
phase("metro", `port ${metroPort} (${devServer ? `started: ${devServer.reason}` : `pid ${gate.pid ?? "unknown, started outside Stim"}`})`);
|
|
2346
|
+
return true;
|
|
2295
2347
|
} else phase("metro", reservedPort ? `port ${reservedPort} (not checked)` : `no reservation; using ${DEFAULT_METRO_PORT} (not checked)`);
|
|
2296
2348
|
metroPort = release ? null : reservedPort ?? 8081;
|
|
2297
2349
|
return true;
|
|
2298
2350
|
}
|
|
2299
2351
|
if (!await resolveMetroPort()) return phaseFailure;
|
|
2300
|
-
if (
|
|
2301
|
-
|
|
2302
|
-
root,
|
|
2303
|
-
label,
|
|
2304
|
-
platform: PLATFORM,
|
|
2305
|
-
backend: remoteBackend,
|
|
2306
|
-
easBin: resolveEasBin(root)?.file ?? null
|
|
2307
|
-
});
|
|
2308
|
-
if ("failed" in resolved) return fail(resolved.code ?? "STIM_NO_REMOTE_SESSION", resolved.failed, resolved.remedy);
|
|
2309
|
-
remoteDevice = makeRemoteDeviceDeps(resolved.ctx);
|
|
2352
|
+
if (remoteContext) {
|
|
2353
|
+
remoteDevice = makeRemoteDeviceDeps(remoteContext.ctx);
|
|
2310
2354
|
if (metroPort !== null) {
|
|
2311
2355
|
const reachable = await ensureRemoteMetro({
|
|
2312
2356
|
ctx: remoteDevice.ctx,
|
|
@@ -2389,6 +2433,7 @@ async function runAndroid(options = {}) {
|
|
|
2389
2433
|
const boot = () => Promise.resolve(ensureDeviceBooted({
|
|
2390
2434
|
platform: PLATFORM,
|
|
2391
2435
|
device,
|
|
2436
|
+
projectPath: root,
|
|
2392
2437
|
out,
|
|
2393
2438
|
logFile: emuLog
|
|
2394
2439
|
})).catch((e) => ({
|
|
@@ -2404,6 +2449,7 @@ async function runAndroid(options = {}) {
|
|
|
2404
2449
|
boot,
|
|
2405
2450
|
createdSessionId: remoteDevice.createdSessionId,
|
|
2406
2451
|
abandonCreatedSession: remoteDevice.abandonCreatedSession,
|
|
2452
|
+
webPreviewUrl: remoteDevice.webPreviewUrl,
|
|
2407
2453
|
writeState,
|
|
2408
2454
|
register: registerProject
|
|
2409
2455
|
}) : boot()).then((result) => {
|
|
@@ -2457,7 +2503,8 @@ async function runAndroid(options = {}) {
|
|
|
2457
2503
|
phase,
|
|
2458
2504
|
out,
|
|
2459
2505
|
estimates,
|
|
2460
|
-
stats
|
|
2506
|
+
stats,
|
|
2507
|
+
step: progress.step
|
|
2461
2508
|
}
|
|
2462
2509
|
}, {
|
|
2463
2510
|
deviceAbi,
|
|
@@ -2477,7 +2524,7 @@ async function runAndroid(options = {}) {
|
|
|
2477
2524
|
loadCacheProviderModule,
|
|
2478
2525
|
acquireSlot,
|
|
2479
2526
|
releaseSlot,
|
|
2480
|
-
|
|
2527
|
+
planPrebuildFor,
|
|
2481
2528
|
prebuild,
|
|
2482
2529
|
build,
|
|
2483
2530
|
ccacheFor,
|
|
@@ -2590,7 +2637,10 @@ async function runAndroid(options = {}) {
|
|
|
2590
2637
|
now,
|
|
2591
2638
|
out,
|
|
2592
2639
|
emit,
|
|
2593
|
-
recordRun
|
|
2640
|
+
recordRun,
|
|
2641
|
+
reclaimed,
|
|
2642
|
+
devServer,
|
|
2643
|
+
enterPhase: progress.step
|
|
2594
2644
|
});
|
|
2595
2645
|
} finally {
|
|
2596
2646
|
releaseLease();
|
|
@@ -2607,4 +2657,4 @@ async function runAndroid(options = {}) {
|
|
|
2607
2657
|
}
|
|
2608
2658
|
}
|
|
2609
2659
|
//#endregion
|
|
2610
|
-
export { NO_DEVICE, NO_FINGERPRINT,
|
|
2660
|
+
export { NO_DEVICE, NO_FINGERPRINT, androidDevClientScheme, androidFacts, androidSystemImageSetting, androidVariantSetting, apkDevClientFacts, apkPackage, collectorLogFile, androidCommand as default, displayPath, dumpApkManifest, findAapt, formatDuration, isReleaseVariant, killPreviousCollector, lastBuildRecord, noDeviceDiagnostic, parseXmltree, phaseLine, registerAndroid, resolveSystemImage, resolveVariant, runAndroid, shortHash, startCollector };
|