stim 1.17.2 → 1.19.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 +50 -1
- package/dist/{activity-IRvJY_ru.mjs → activity-BSw0x84R.mjs} +4 -4
- package/dist/{agent-device-usage-output-BK41vnzN.mjs → agent-device-usage-output-Bjxu_VmX.mjs} +8 -8
- package/dist/{android-B9yaNKwa.mjs → android-BXc6BmEi.mjs} +658 -404
- package/dist/{android-B_YNZy61.mjs → android-BdQC6zs1.mjs} +9 -9
- package/dist/android-BvV1gGdN.mjs +4 -0
- package/dist/{android-DE4v59Mb.mjs → android-DE5g_5gv.mjs} +2 -2
- package/dist/{android-cas-ZFpjJBL_.mjs → android-cas-C87geAg-.mjs} +5 -5
- package/dist/android-cas-compiler.mjs +2 -2
- package/dist/api-DIf_s86s.mjs +150 -0
- package/dist/api-run.d.mts +1 -0
- package/dist/api-run.mjs +145 -0
- package/dist/api.d.mts +218 -0
- package/dist/api.mjs +2 -0
- package/dist/{app-install-VPkI5WEg.mjs → app-install-CFI4ptUL.mjs} +33 -13
- package/dist/{attempt-Bk2ouQVL.mjs → attempt-DW-mQqM7.mjs} +14 -4
- package/dist/{web-Dl0KSRHf.mjs → browser-web-CDhqQxhv.mjs} +138 -176
- package/dist/{budget-DfcgFdQB.mjs → budget-Dz-kyXLv.mjs} +1391 -58
- package/dist/build-facts-B87vo-Im.d.mts +142 -0
- package/dist/{cache-manifest-DnUHBJaU.mjs → cache-manifest-DiwKSyv9.mjs} +3 -3
- package/dist/cache-manifest.mjs +1 -1
- package/dist/{chrome-BrcVWQyD.mjs → chrome-BTUOo8p2.mjs} +1 -1
- package/dist/cli-C9Zvgbdc.mjs +68 -0
- package/dist/cli.mjs +1 -1
- package/dist/{client-SvBdgObD.mjs → client-CuUo8Nvg.mjs} +381 -49
- package/dist/collector-run.d.mts +6 -0
- package/dist/collector-run.mjs +9 -9
- package/dist/{command-output-CDAIFD-8.mjs → command-output-Bt8ygHE7.mjs} +2 -2
- package/dist/{config-D0DbpZtM.mjs → config-DC5jEtd1.mjs} +9 -5
- package/dist/{created-devices-CpvzBL25.mjs → created-devices-D0s26Hx5.mjs} +2 -2
- package/dist/{dependency-state-CLcBHH2M.mjs → dependency-state-DKaS1OM9.mjs} +2 -2
- package/dist/{detached-entry-CiTEgMR1.mjs → detached-entry-pB_IPfV4.mjs} +1 -1
- package/dist/{dev-client-B-XC4uRH.mjs → dev-client-CDY_zAmD.mjs} +2 -2
- package/dist/{device-C8IOoJPv.mjs → device-BHw0WlHl.mjs} +8 -8
- package/dist/{device-capacity-ZC4XSZ4R.mjs → device-capacity-BoN1jQkO.mjs} +25 -14
- package/dist/device-host-worker.mjs +19 -19
- package/dist/{gradle-dJpre3Yn.mjs → device-ios-B2ruohE-.mjs} +661 -107
- package/dist/{device-lease-DpoS36_u.mjs → device-lease-Cu7PtQpZ.mjs} +9 -9
- package/dist/{device-lease-run-B8YltIH_.mjs → device-lease-run-D38SnZtt.mjs} +4 -4
- package/dist/{device-pool-3o7s5lo4.mjs → device-pool-CP4rAsHx.mjs} +3 -3
- package/dist/{device-remote-hFSkpfQu.mjs → device-remote-B5GiUifk.mjs} +15 -16
- package/dist/{doctor-CFv1fCrF.mjs → doctor-PRJH94v6.mjs} +61 -34
- package/dist/doctor-watchman-wZhyTdp4.mjs +259 -0
- package/dist/{error-diagnostics-C-NMnhrk.mjs → error-diagnostics--v5HGOU0.mjs} +11 -11
- package/dist/{exec-CaErVv7t.mjs → exec-i8GDPwq9.mjs} +93 -6
- package/dist/{gc-DEmkg3Xh.mjs → gc-CyHMcKcn.mjs} +25 -471
- package/dist/{guide-CSmhMTf8.mjs → guide-dRaQJJk9.mjs} +681 -507
- package/dist/{hosted-android-E1Heoqnv.mjs → hosted-android-Bs7fytj7.mjs} +3 -3
- package/dist/{hosted-client-Bp4knSwu.mjs → hosted-client-C5G4h_OJ.mjs} +2 -2
- package/dist/{hosted-ios-BnbeYOUD.mjs → hosted-ios-Bik035eL.mjs} +1 -1
- package/dist/{hosted-logs-OcgIBvCT.mjs → hosted-logs-CI9Az9a4.mjs} +9 -9
- package/dist/{hosted-macos-DcxKjtfH.mjs → hosted-macos-DABm3ScO.mjs} +5 -5
- package/dist/{hosted-native-1Ebbig4w.mjs → hosted-native-DbV2aUVw.mjs} +13 -9
- package/dist/{idle-shutdown-IGyNV8Cr.mjs → idle-shutdown-DYiMBn34.mjs} +7 -8
- package/dist/{ios-DSfnPfse.mjs → ios-BWbHFZVz.mjs} +19 -7
- package/dist/{ios-Cr1h5gnO.mjs → ios-DCrkgXEM.mjs} +1 -1
- package/dist/ios-DYUr9LGc.mjs +8 -0
- package/dist/ios-ITYno9rO.mjs +2241 -0
- package/dist/{ios-device-pAN4t54-.mjs → ios-device-CJ_ZOGky.mjs} +3 -3
- package/dist/{ios-device-Ee7sDXfp.mjs → ios-device-CrW8aMb5.mjs} +1 -1
- package/dist/{ios-state-8L3lgum6.mjs → ios-state-By0XuLTQ.mjs} +1 -1
- package/dist/{launch-verify-lTnWZikR.mjs → launch-verify-DCveSE6d.mjs} +5 -5
- package/dist/{logs-BAaS7gS2.mjs → logs-Bao1HOMy.mjs} +13 -13
- package/dist/{macos-BIRQS90f.mjs → macos-ciyBdTY5.mjs} +194 -132
- package/dist/macos-nIOATJmR.mjs +2 -0
- package/dist/macos-run.mjs +1 -1
- package/dist/maintenance-run.mjs +349 -47
- package/dist/manifest-CX0d1IkZ.mjs +148 -0
- package/dist/{metro-g8NqiJp-.mjs → metro-DI2AjPF_.mjs} +196 -43
- package/dist/{metro-gateway-C-ijGI2F.mjs → metro-gateway-BxpJLIwh.mjs} +4 -4
- package/dist/{metro-store-CDDZHtPl.mjs → metro-store-BAHFcJm6.mjs} +16 -2
- package/dist/{build-plan-BDsSi09V.mjs → miss-reason-CjQCOKqq.mjs} +207 -249
- package/dist/{named-ports-C8m8EQs2.mjs → named-ports-B4iWH4E-.mjs} +70 -8
- package/dist/{native-runtime-BEK171xQ.mjs → native-runtime-BGxsbfny.mjs} +6 -6
- package/dist/{ndjson-PclOIwVY.mjs → ndjson-LGZAMsqa.mjs} +8 -4
- package/dist/offload-worker.d.mts +3 -45
- package/dist/offload-worker.mjs +11 -11
- package/dist/{ownership-DyRQru0q.mjs → ownership-EB0ySdGK.mjs} +3 -3
- package/dist/{page-QF_Ke10s.mjs → page-Bv9s2KwY.mjs} +1 -1
- package/dist/placement-log-Dxe34rJm.mjs +88 -0
- package/dist/plan-placement-BVY59qa8.mjs +290 -0
- package/dist/{ports-4nYqxPWC.mjs → ports-ByPc0_97.mjs} +3 -3
- package/dist/{prebuild-D7RUMkwN.mjs → prebuild-BKJ3wJI1.mjs} +5 -5
- package/dist/preview-BbSottmO.mjs +986 -0
- package/dist/{rolldown-runtime-BsOwBuO_.mjs → process-identity-Biv3arCO.mjs} +11 -1
- package/dist/project-B5eQ1USs.mjs +612 -0
- package/dist/{pull-request-pxnDCQzn.mjs → pull-request-IHNdMnG8.mjs} +1 -1
- package/dist/pull-requests.mjs +1 -1
- package/dist/react-native-ios-BMh2i25I.mjs +2 -0
- package/dist/react-native-ios-CSpIxexZ.mjs +2083 -0
- package/dist/{recordings-BNgsD1Tr.mjs → recordings-Bv8McZN4.mjs} +1 -1
- package/dist/{reload-DcRxj3qG.mjs → reload-rAPdqeRR.mjs} +14 -15
- package/dist/{remote-cache-Ci4DQYYj.mjs → remote-cache-N3hAEaAL.mjs} +5 -5
- package/dist/{run-CkE9y0nc.mjs → run-lAkle1-c.mjs} +5 -5
- package/dist/{server-bare-BkrSxSsZ.mjs → server-bare-CkIjnegq.mjs} +5 -6
- package/dist/server-expo-DGsdUGX2.mjs +2 -0
- package/dist/{server-expo-CHTyXD0G.mjs → server-expo-n3tUi3Em.mjs} +5 -6
- package/dist/{settings-SR9OmW9h.mjs → settings-2am7TSxp.mjs} +5 -5
- package/dist/{settings-Cfd-0r2d.mjs → settings-CxgngUZk.mjs} +116 -103
- package/dist/{settings-BBpMcDbD.mjs → settings-Djl-mpjX.mjs} +1 -1
- package/dist/settings.schema.json +198 -9
- package/dist/{simslim-BWNrc3M3.mjs → simslim-D9LkJxIO.mjs} +7 -7
- package/dist/{slot-launch-DmjBmpQV.mjs → slot-launch-HV_KtPIw.mjs} +6 -6
- package/dist/{spawn-claims-D0XfSe8u.mjs → spawn-claims-Kqiifsk2.mjs} +1 -1
- package/dist/{spawn-entry-jw19m8Nl.mjs → spawn-entry-Dcb7sDUz.mjs} +1 -0
- package/dist/{stage-CzaiwFsI.mjs → stage-BgaZJVZY.mjs} +14 -3
- package/dist/{start-AlfCPRVU.mjs → start-D8NbxNBb.mjs} +1 -1
- package/dist/{start-DUz3mYdb.mjs → start-DtxthMfI.mjs} +34 -27
- package/dist/{state-PeQiJuIu.mjs → state-D2KtnALG.mjs} +4 -4
- package/dist/{state-DyaNTgfR.mjs → state-Tp4uImDS.mjs} +5 -3
- package/dist/{state-B4PQFXMW.d.mts → state-lKh8shBp.d.mts} +2 -3
- package/dist/{stats-Bq4fJOdB.mjs → stats-8EJ5ivFT.mjs} +6 -6
- package/dist/{status-DlKvVJBE.mjs → status-BEeL6IKz.mjs} +6 -4
- package/dist/{status-BA2pehwf.mjs → status-BOyp0CUl.mjs} +31 -34
- package/dist/{stim-desktop-Cnz8CPnl.mjs → stim-desktop-C65jk_q2.mjs} +1 -1
- package/dist/{stim-installations-QMEI32v1.mjs → stim-installations-DbmUjsLv.mjs} +1 -1
- package/dist/stop-D0UIpZuh.mjs +72 -0
- package/dist/{stop-zF1y04f2.mjs → stop-DRipAg-e.mjs} +2 -2
- package/dist/{stop-DQIasgKp.mjs → stop-zDlknFoz.mjs} +79 -33
- package/dist/supervisor-run.d.mts +24 -22
- package/dist/supervisor-run.mjs +21 -22
- package/dist/{support-CsJNVfOc.mjs → support-CNqYkV0y.mjs} +7 -7
- package/dist/{support-WvD__tSV.mjs → support-DsNaFg6D.mjs} +5 -5
- package/dist/swiftpm-macos-aJzPAj1t.mjs +119 -0
- package/dist/{toolchain-DNyR3NPq.mjs → toolchain-C3xSoyUl.mjs} +475 -269
- package/dist/{trigger-Bemu8ytw.mjs → trigger-CAhmG-4s.mjs} +6 -6
- package/dist/trigger-CoalsMTV.mjs +2 -0
- package/dist/{warm-progress-DF4JRvYI.mjs → warm-progress-pFLB2e6P.mjs} +135 -38
- package/dist/{status-CzS4bPO9.mjs → watchman-BL1IdOtq.mjs} +75 -7
- package/dist/web-DFpFeZu5.mjs +2 -0
- package/dist/web-DbbrPUn0.mjs +127 -0
- package/dist/web-run.mjs +10 -10
- package/dist/{workspace-state-DqcCbpE7.mjs → workspace-state-CR9X86xJ.mjs} +3 -3
- package/dist/{ownership-BMLqyBgJ.mjs → workspaces-Djb2gfi9.mjs} +661 -23
- package/dist/{worktree-Be_7NLKy.mjs → worktree-BL4M1rhB.mjs} +252 -50
- package/dist/{worktree-j3IcFAI5.mjs → worktree-DV-A0LNL.mjs} +1 -1
- package/package.json +10 -5
- package/shim/bundle-response.cjs +2 -1
- package/shim/bundle-response.d.cts +1 -1
- package/shim/expo-metro-config.cjs +1 -1
- package/dist/build-progress-D0WkjCPd.mjs +0 -1036
- package/dist/build-slots-41v_d2x9.mjs +0 -142
- package/dist/cli-CYXb29oE.mjs +0 -51
- package/dist/deps-iKw0eD4P.mjs +0 -438
- package/dist/device-ios-C2Um-XUR.mjs +0 -412
- package/dist/errors-B6W7FkxQ.mjs +0 -15
- package/dist/guide-status-Du-ZQ3FD.mjs +0 -125
- package/dist/idle-BXG__1BA.mjs +0 -176
- package/dist/in-use-CvGOUn-x.mjs +0 -130
- package/dist/ios-BJ4XUSe1.mjs +0 -3867
- package/dist/native-run-D1Q56rAD.mjs +0 -158
- package/dist/preview-B-g9d7kV.mjs +0 -318
- package/dist/process-identity-BmRm1qV0.mjs +0 -12
- package/dist/project-D47Rh0ly.mjs +0 -276
- package/dist/server-expo-CeakhI8P.mjs +0 -2
- package/dist/state-BKa8wNN5.mjs +0 -148
- package/dist/stop-BIiM49EY.mjs +0 -48
- package/dist/stop-cause-rhmGfwvt.mjs +0 -97
- package/dist/trigger-CXts6qt1.mjs +0 -2
- package/dist/watchman-APYjXSQa.mjs +0 -48
- package/dist/workspace-process-lock-2mNzOlaB.mjs +0 -58
- package/dist/workspaces-Bcib97-6.mjs +0 -306
|
@@ -1,25 +1,193 @@
|
|
|
1
|
-
import { t as
|
|
2
|
-
import {
|
|
3
|
-
import { n as ownership_claim_exports, t as process_identity_exports } from "./process-identity-BmRm1qV0.mjs";
|
|
1
|
+
import { n as ownership_claim_exports, t as process_identity_exports } from "./process-identity-Biv3arCO.mjs";
|
|
2
|
+
import { t as getExecutor } from "./exec-i8GDPwq9.mjs";
|
|
4
3
|
import { t as createLineReader } from "./process-output-Fpl8PYi8.mjs";
|
|
5
|
-
import { l as phaseLine } from "./command-output-
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import { t as
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import { r as
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
4
|
+
import { a as formatElapsed, l as phaseLine } from "./command-output-Bt8ygHE7.mjs";
|
|
5
|
+
import { D as withConfigLock$1, H as projectDeviceSlots, M as isOnMountedVolume, N as listMountedVolumes, P as measuredDirectorySize, _ as loadConfig$1, d as getConfigDir, g as isPathPrefix, j as formatBytes, k as acquireAvdClaim, p as getProject, z as deviceSlotPlatforms } from "./config-DC5jEtd1.mjs";
|
|
6
|
+
import { S as workspaceName, g as workspaceAgentDeviceDir, r as readWorkspaceState$1, s as workspaceLastUsed, v as workspaceDir } from "./workspace-state-CR9X86xJ.mjs";
|
|
7
|
+
import { Y as resolveTailscaleBinary, mt as repoRoot } from "./settings-CxgngUZk.mjs";
|
|
8
|
+
import { d as signalProcessTree, s as pidExists } from "./metro-DI2AjPF_.mjs";
|
|
9
|
+
import { _ as removeSingletonFiles, c as webClaimRoot, f as webProfileDir, g as liveProfileHolder, h as chromeProcessState, i as clearWebRecord, o as readWebRecord, t as CDP_PORT_LABEL, u as webFacts } from "./state-D2KtnALG.mjs";
|
|
10
|
+
import { t as agentDeviceDaemon, u as readAgentDeviceRecords } from "./activity-BSw0x84R.mjs";
|
|
11
|
+
import { h as listBuildLocks } from "./device-lease-Cu7PtQpZ.mjs";
|
|
12
|
+
import { n as declareSpawnsOn, o as stopDeclaringSpawnsOn } from "./spawn-claims-Kqiifsk2.mjs";
|
|
13
|
+
import { n as readCreatedDevices$1, r as recordCreatedDevice, t as forgetCreatedDevice } from "./created-devices-D0s26Hx5.mjs";
|
|
14
|
+
import { B as ownedAvdSystemImage, I as ownedAvdDirectory, J as resolveOwnedAvdSerial, L as ownedAvdMatchesConfiguration, M as listOrphanedAvdDirectories, O as listAdbDevices, X as shutdownAndroidEmulator, Z as sleepSync, _ as avdStorageRoots, dt as parkSim, et as waitForAndroidEmulatorShutdown, f as assertOwnedAvdStopped, h as avdPathExists, it as isStimOwnedAvd, lt as eraseParkedAfter, mt as removeParkedAfter, nt as wipeAvdUserData, pt as readParked, x as deleteAvd } from "./android-BdQC6zs1.mjs";
|
|
15
|
+
import { D as shutdownIosSim, T as resolveOwnedIosSim, _ as parkedSimName, c as eraseIosSim, o as deleteIosSim, p as listIosDeviceTypes, s as deleteParkedIosSim, v as parseRuntimeVersion, x as renameIosSim } from "./ios-BWbHFZVz.mjs";
|
|
16
|
+
import { a as withWorkspaceProcessLock, o as workspaceProcessLockError, r as releaseBrowserPort, s as workspaceProcessLockPath } from "./named-ports-B4iWH4E-.mjs";
|
|
17
|
+
import { a as macosRuntimeClaim, n as macosDir } from "./state-Tp4uImDS.mjs";
|
|
18
|
+
import { closeSync, existsSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, rmdirSync, statSync, unlinkSync } from "node:fs";
|
|
18
19
|
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
20
|
+
import { basename as basename$1, isAbsolute as isAbsolute$1, join as join$1, relative as relative$1, resolve as resolve$1 } from "path";
|
|
21
|
+
import { buildSlotsDir, hostedNativeRecords, isJsonObject, macosAppState, parseHostedNativePlacement, readJsonFile, readMacosRecord, requireAutomaticMachine, tryAcquireBuildSlotClaim, unreadableHostedNative } from "@stim-cli/core/state";
|
|
19
22
|
import { homedir } from "node:os";
|
|
23
|
+
import chalk from "chalk";
|
|
20
24
|
import { isDeepStrictEqual } from "node:util";
|
|
25
|
+
import { existsSync as existsSync$1, lstatSync as lstatSync$1, readdirSync as readdirSync$1, realpathSync as realpathSync$1, rmSync as rmSync$1, statSync as statSync$1 } from "fs";
|
|
26
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
21
27
|
import { resolve4 } from "node:dns/promises";
|
|
22
28
|
import { request } from "node:https";
|
|
29
|
+
//#region src/commands/gc/paths.ts
|
|
30
|
+
function canonicalPath(p) {
|
|
31
|
+
try {
|
|
32
|
+
return realpathSync$1(p);
|
|
33
|
+
} catch {
|
|
34
|
+
return resolve$1(p);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
//#region src/commands/gc/results.ts
|
|
39
|
+
const results = [];
|
|
40
|
+
function recordGcResult(kind, status, label, { id = null, bytes = null, detail = null } = {}) {
|
|
41
|
+
results.push({
|
|
42
|
+
kind,
|
|
43
|
+
status,
|
|
44
|
+
label,
|
|
45
|
+
id,
|
|
46
|
+
bytes,
|
|
47
|
+
detail
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
function takeGcResults() {
|
|
51
|
+
return results.splice(0);
|
|
52
|
+
}
|
|
53
|
+
//#endregion
|
|
54
|
+
//#region src/engine/build-slots.ts
|
|
55
|
+
const SLOT_PREFIX = "slot-";
|
|
56
|
+
const SLOT_POLL_MS = 1e3;
|
|
57
|
+
const SLOT_PROGRESS_MS = 3e4;
|
|
58
|
+
const SLOT_CEILING_MS = 54e5;
|
|
59
|
+
function sleepAsync$1(ms) {
|
|
60
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
61
|
+
}
|
|
62
|
+
function toRecord(holder) {
|
|
63
|
+
const details = holder.details;
|
|
64
|
+
return {
|
|
65
|
+
pid: holder.owner.pid,
|
|
66
|
+
index: typeof details.index === "number" ? details.index : null,
|
|
67
|
+
projectRoot: typeof details.projectRoot === "string" ? details.projectRoot : null,
|
|
68
|
+
startedAt: holder.startedAt || null,
|
|
69
|
+
logFile: typeof details.logFile === "string" ? details.logFile : null
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function tryAcquireBuildSlot({ max, root = null, logFile = null }) {
|
|
73
|
+
if (!max || max <= 0) return {
|
|
74
|
+
acquired: true,
|
|
75
|
+
unlimited: true
|
|
76
|
+
};
|
|
77
|
+
const got = tryAcquireBuildSlotClaim({
|
|
78
|
+
max,
|
|
79
|
+
details: {
|
|
80
|
+
projectRoot: root,
|
|
81
|
+
logFile
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
if (!got) return null;
|
|
85
|
+
declareSpawnsOn(got.claim);
|
|
86
|
+
return {
|
|
87
|
+
acquired: true,
|
|
88
|
+
path: got.path,
|
|
89
|
+
index: got.index,
|
|
90
|
+
slot: {
|
|
91
|
+
pid: got.claim.owner.pid,
|
|
92
|
+
index: got.index,
|
|
93
|
+
projectRoot: root,
|
|
94
|
+
startedAt: got.claim.startedAt,
|
|
95
|
+
logFile
|
|
96
|
+
},
|
|
97
|
+
claim: got.claim
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function slotWaitingLine({ max, elapsedMs }) {
|
|
101
|
+
return `${"build".padEnd(11)} waiting for a build slot (all ${max} in use, ${formatElapsed(elapsedMs)} elapsed) -- stim guide lifecycle concurrency`;
|
|
102
|
+
}
|
|
103
|
+
async function acquireBuildSlot({ max, root = null, logFile = null, now = Date.now, out = () => {}, waitingFor = () => {}, sleep = sleepAsync$1, intervalMs = SLOT_POLL_MS, progressMs = SLOT_PROGRESS_MS, ceilingMs = SLOT_CEILING_MS, automatic = false }) {
|
|
104
|
+
if (automatic) requireAutomaticMachine("build", "local");
|
|
105
|
+
if (!max || max <= 0) return {
|
|
106
|
+
acquired: true,
|
|
107
|
+
unlimited: true
|
|
108
|
+
};
|
|
109
|
+
const started = now();
|
|
110
|
+
let lastProgress = started;
|
|
111
|
+
let waited = false;
|
|
112
|
+
try {
|
|
113
|
+
for (;;) {
|
|
114
|
+
if (automatic) requireAutomaticMachine("build", "local");
|
|
115
|
+
const got = tryAcquireBuildSlot({
|
|
116
|
+
max,
|
|
117
|
+
root,
|
|
118
|
+
logFile
|
|
119
|
+
});
|
|
120
|
+
if (got) return {
|
|
121
|
+
...got,
|
|
122
|
+
slotWaitMs: waited ? Math.max(0, Math.round(now() - started)) : 0
|
|
123
|
+
};
|
|
124
|
+
waited = true;
|
|
125
|
+
waitingFor({
|
|
126
|
+
kind: "build-slot",
|
|
127
|
+
inUse: max,
|
|
128
|
+
max,
|
|
129
|
+
since: new Date(started).toISOString()
|
|
130
|
+
});
|
|
131
|
+
const elapsed = now() - started;
|
|
132
|
+
if (elapsed >= ceilingMs) {
|
|
133
|
+
const err = /* @__PURE__ */ new Error(`Waited ${formatElapsed(elapsed)} for one of ${max} build slots, and every slot is held by a process that is still running, or by a holder Stim cannot identify. Slots live under ` + buildSlotsDir() + "; remove a slot directory whose builder is not really building, or raise concurrency.maxBuilds.");
|
|
134
|
+
err.code = "STIM_BUILD_SLOT_TIMEOUT";
|
|
135
|
+
throw err;
|
|
136
|
+
}
|
|
137
|
+
if (now() - lastProgress >= progressMs) {
|
|
138
|
+
lastProgress = now();
|
|
139
|
+
out(slotWaitingLine({
|
|
140
|
+
max,
|
|
141
|
+
elapsedMs: elapsed
|
|
142
|
+
}));
|
|
143
|
+
}
|
|
144
|
+
await sleep(intervalMs);
|
|
145
|
+
}
|
|
146
|
+
} finally {
|
|
147
|
+
waitingFor(null);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
function releaseBuildSlot(handle) {
|
|
151
|
+
if (!handle || handle.unlimited) return false;
|
|
152
|
+
stopDeclaringSpawnsOn(handle.claim);
|
|
153
|
+
return (0, ownership_claim_exports.releaseClaim)(handle.claim);
|
|
154
|
+
}
|
|
155
|
+
function listBuildSlots() {
|
|
156
|
+
const dir = buildSlotsDir();
|
|
157
|
+
let names;
|
|
158
|
+
try {
|
|
159
|
+
names = readdirSync(dir);
|
|
160
|
+
} catch {
|
|
161
|
+
return [];
|
|
162
|
+
}
|
|
163
|
+
const slots = [];
|
|
164
|
+
for (const name of names) {
|
|
165
|
+
if (!name.startsWith(SLOT_PREFIX)) continue;
|
|
166
|
+
const path = join(dir, name);
|
|
167
|
+
try {
|
|
168
|
+
if (!statSync(path).isDirectory()) continue;
|
|
169
|
+
} catch {
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
const survey = (0, ownership_claim_exports.readClaimSet)(path);
|
|
173
|
+
const holder = survey.live[0] ?? survey.dead[0];
|
|
174
|
+
const info = holder ? toRecord(holder) : null;
|
|
175
|
+
const index = Number(name.slice(5));
|
|
176
|
+
slots.push({
|
|
177
|
+
path,
|
|
178
|
+
name,
|
|
179
|
+
index: Number.isFinite(index) ? index : null,
|
|
180
|
+
pid: info?.pid ?? null,
|
|
181
|
+
projectRoot: info?.projectRoot ?? null,
|
|
182
|
+
startedAt: info?.startedAt ?? null,
|
|
183
|
+
logFile: info?.logFile ?? null,
|
|
184
|
+
alive: survey.live.length > 0,
|
|
185
|
+
unresolved: survey.unresolved.length > 0
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
return slots;
|
|
189
|
+
}
|
|
190
|
+
//#endregion
|
|
23
191
|
//#region src/devices/agent-device-cleanup.ts
|
|
24
192
|
function parseAgentDeviceSessions(output) {
|
|
25
193
|
const result = JSON.parse(output);
|
|
@@ -392,7 +560,7 @@ function teardownOwnedIosSim(udid, { del = false, label, park, workspace, shutdo
|
|
|
392
560
|
}
|
|
393
561
|
}
|
|
394
562
|
function assertAvdReferences(avdName, owner) {
|
|
395
|
-
const config = loadConfig();
|
|
563
|
+
const config = loadConfig$1();
|
|
396
564
|
if (!config) throw new Error("Cannot verify AVD references without a Stim config; the data was kept.");
|
|
397
565
|
if (owner && "projectPath" in owner && owner.expectedRecord && !isDeepStrictEqual(deviceSlotPlatforms(config.projects[owner.projectPath], owner.slot)?.android, owner.expectedRecord)) throw new Error(`AVD ${avdName} has a changed workspace record; its data was kept.`);
|
|
398
566
|
for (const [projectPath, project] of Object.entries(config.projects)) for (const { slot, platforms } of projectDeviceSlots(project)) {
|
|
@@ -405,7 +573,7 @@ function removeOrphanedAvdDirectory(candidate, owner) {
|
|
|
405
573
|
const surveyed = listOrphanedAvdDirectories(candidate.name).find((entry) => entry.directory === candidate.directory);
|
|
406
574
|
if (!surveyed || surveyed.name !== candidate.name || surveyed.dev !== candidate.dev || surveyed.ino !== candidate.ino) throw new Error(`AVD data at ${candidate.directory} changed or is registered; it was kept.`);
|
|
407
575
|
assertOwnedAvdStopped(candidate.name, { resolveDirectory: () => candidate.directory });
|
|
408
|
-
const detached = withConfigLock(() => {
|
|
576
|
+
const detached = withConfigLock$1(() => {
|
|
409
577
|
const current = lstatSync(candidate.directory);
|
|
410
578
|
if (!current.isDirectory() || current.dev !== candidate.dev || current.ino !== candidate.ino) throw new Error(`AVD data at ${candidate.directory} changed or is registered; it was kept.`);
|
|
411
579
|
assertAvdReferences(candidate.name, owner);
|
|
@@ -481,7 +649,7 @@ function serialFree(serial) {
|
|
|
481
649
|
function teardownClaimedAvd(avdName, claim, { del = false, wipe = false, park, owner, workspace, orphanedDirectory, onlyIfMissing = false, onWipeStart, waitForShutdown = waitForAndroidEmulatorShutdown, assertStopped = assertOwnedAvdStopped, resolveAvd = resolveOwnedAvdSerial }) {
|
|
482
650
|
let parkFallback;
|
|
483
651
|
try {
|
|
484
|
-
if (del || wipe) withConfigLock(() => assertAvdReferences(avdName, owner));
|
|
652
|
+
if (del || wipe) withConfigLock$1(() => assertAvdReferences(avdName, owner));
|
|
485
653
|
const resolved = resolveAvd(avdName);
|
|
486
654
|
if (resolved.notOwned) return {
|
|
487
655
|
status: "skipped",
|
|
@@ -547,7 +715,7 @@ function teardownClaimedAvd(avdName, claim, { del = false, wipe = false, park, o
|
|
|
547
715
|
if (current.notOwned || current.missing) throw new Error(`AVD ${avdName} changed before its data was wiped.`);
|
|
548
716
|
if (current.serial) throw new Error(`Owned AVD ${avdName} started again before its data was wiped.`);
|
|
549
717
|
assertStopped(avdName);
|
|
550
|
-
withConfigLock(() => assertAvdReferences(avdName, owner));
|
|
718
|
+
withConfigLock$1(() => assertAvdReferences(avdName, owner));
|
|
551
719
|
const directory = ownedAvdDirectory(avdName);
|
|
552
720
|
if (!directory) throw new Error(`The data directory of AVD ${avdName} could not be resolved.`);
|
|
553
721
|
onWipeStart?.();
|
|
@@ -563,7 +731,7 @@ function teardownClaimedAvd(avdName, claim, { del = false, wipe = false, park, o
|
|
|
563
731
|
if (current.missing) return teardownUnregisteredAvd(avdName, del, owner);
|
|
564
732
|
if (current.serial) throw new Error(`Owned AVD ${avdName} started again before deletion.`);
|
|
565
733
|
assertStopped(avdName);
|
|
566
|
-
withConfigLock(() => assertAvdReferences(avdName, owner));
|
|
734
|
+
withConfigLock$1(() => assertAvdReferences(avdName, owner));
|
|
567
735
|
if (park && park.max > 0) try {
|
|
568
736
|
const systemImage = ownedAvdSystemImage(avdName);
|
|
569
737
|
if (!systemImage || !park.configuration || !ownedAvdMatchesConfiguration(avdName, park.configuration)) throw new Error("the AVD has no verified creation configuration");
|
|
@@ -720,7 +888,7 @@ async function teardownBrowserHeld(root, { deleteProfile = false } = {}) {
|
|
|
720
888
|
}
|
|
721
889
|
if (existsSync(profile)) removeSingletonFiles(profile);
|
|
722
890
|
if (deleteProfile && existsSync(profile)) {
|
|
723
|
-
if (!readCreatedDevices().web.has(profile)) return {
|
|
891
|
+
if (!readCreatedDevices$1().web.has(profile)) return {
|
|
724
892
|
status: "skipped",
|
|
725
893
|
kind: "not-owned",
|
|
726
894
|
reason: `${profile} is not in Stim's ledger; it was kept`
|
|
@@ -743,6 +911,83 @@ async function teardownBrowserHeld(root, { deleteProfile = false } = {}) {
|
|
|
743
911
|
}
|
|
744
912
|
}
|
|
745
913
|
//#endregion
|
|
914
|
+
//#region src/macos/instances.ts
|
|
915
|
+
const BUNDLE_EXECUTABLE = /^[^/]+\.app\/Contents\/MacOS\/[^/]+$/;
|
|
916
|
+
function bundleDirs(root) {
|
|
917
|
+
const dir = macosDir(root);
|
|
918
|
+
if (process.platform !== "darwin" || !existsSync(dir)) return [];
|
|
919
|
+
return [.../* @__PURE__ */ new Set([dir, realpathSync(dir)])];
|
|
920
|
+
}
|
|
921
|
+
function isBundleExecutable(path, dirs) {
|
|
922
|
+
return dirs.some((dir) => path.startsWith(`${dir}/`) && BUNDLE_EXECUTABLE.test(path.slice(dir.length + 1)));
|
|
923
|
+
}
|
|
924
|
+
/** Pids from `ps -axww -o pid=,comm=` output whose command names a bundle executable inside `dirs`. */
|
|
925
|
+
function bundleCandidates(psOutput, dirs) {
|
|
926
|
+
const pids = [];
|
|
927
|
+
for (const line of psOutput.split("\n")) {
|
|
928
|
+
const match = /^\s*(\d+)\s+(.+)$/.exec(line);
|
|
929
|
+
if (match && isBundleExecutable(match[2], dirs)) pids.push(Number(match[1]));
|
|
930
|
+
}
|
|
931
|
+
return pids;
|
|
932
|
+
}
|
|
933
|
+
/** Whether `lsof -Fn -d txt` output for one process names a bundle executable inside `dirs`. */
|
|
934
|
+
function lsofNamesBundleExecutable(lsofOutput, dirs) {
|
|
935
|
+
return lsofOutput.split("\n").some((line) => line.startsWith("n") && isBundleExecutable(line.slice(1), dirs));
|
|
936
|
+
}
|
|
937
|
+
function candidatePids(dirs) {
|
|
938
|
+
if (!dirs.length) return [];
|
|
939
|
+
return bundleCandidates(getExecutor().runFile("ps", [
|
|
940
|
+
"-axww",
|
|
941
|
+
"-o",
|
|
942
|
+
"pid=,comm="
|
|
943
|
+
], { timeoutMs: 1e4 }), dirs).filter((pid) => pid !== process.pid);
|
|
944
|
+
}
|
|
945
|
+
/** Pids that claim to run an app bundle from the workspace's macOS directory. `ps` reports argv[0], so this is a lead. */
|
|
946
|
+
function bundleInstancePids(root) {
|
|
947
|
+
return candidatePids(bundleDirs(root));
|
|
948
|
+
}
|
|
949
|
+
function unverified(pid, why) {
|
|
950
|
+
return Object.assign(/* @__PURE__ */ new Error(`Cannot verify macOS app pid ${pid}: ${why}; no signal was sent.`), { code: "STIM_MACOS_OWNER_UNVERIFIED" });
|
|
951
|
+
}
|
|
952
|
+
/**
|
|
953
|
+
* Every running process whose executable is an app bundle Stim built in the workspace's macOS directory, however it
|
|
954
|
+
* was launched. Each identity is captured before its executable is confirmed with lsof.
|
|
955
|
+
*/
|
|
956
|
+
function runningBundleInstances(root) {
|
|
957
|
+
const dirs = bundleDirs(root);
|
|
958
|
+
const instances = [];
|
|
959
|
+
for (const pid of candidatePids(dirs)) {
|
|
960
|
+
const processToken = (0, process_identity_exports.captureProcessToken)(pid);
|
|
961
|
+
const start = (0, process_identity_exports.processStartMicros)(pid);
|
|
962
|
+
if (!processToken || start.status !== "running") {
|
|
963
|
+
if (start.status === "gone") continue;
|
|
964
|
+
throw unverified(pid, "its identity cannot be captured");
|
|
965
|
+
}
|
|
966
|
+
const record = {
|
|
967
|
+
pid,
|
|
968
|
+
processToken,
|
|
969
|
+
startedAtMicros: start.startedAtMicros
|
|
970
|
+
};
|
|
971
|
+
let open;
|
|
972
|
+
try {
|
|
973
|
+
open = getExecutor().runFile("lsof", [
|
|
974
|
+
"-a",
|
|
975
|
+
"-p",
|
|
976
|
+
String(pid),
|
|
977
|
+
"-d",
|
|
978
|
+
"txt",
|
|
979
|
+
"-Fn"
|
|
980
|
+
], { timeoutMs: 1e4 });
|
|
981
|
+
} catch (error) {
|
|
982
|
+
if (["gone", "different"].includes((0, process_identity_exports.inspectProcessIdentity)(record))) continue;
|
|
983
|
+
open = String(error.stdout ?? "");
|
|
984
|
+
if (!open.trim()) throw unverified(pid, `lsof failed (${error.message.split("\n")[0]})`);
|
|
985
|
+
}
|
|
986
|
+
if (lsofNamesBundleExecutable(open, dirs)) instances.push(record);
|
|
987
|
+
}
|
|
988
|
+
return instances;
|
|
989
|
+
}
|
|
990
|
+
//#endregion
|
|
746
991
|
//#region src/engine/public-http-probe.ts
|
|
747
992
|
const DNS_TIMEOUT_MS = 5e3;
|
|
748
993
|
async function resolveAddresses(hostname, signal) {
|
|
@@ -1301,4 +1546,397 @@ function numberOrNull(v) {
|
|
|
1301
1546
|
return typeof v === "number" && Number.isSafeInteger(v) && v > 0 ? v : null;
|
|
1302
1547
|
}
|
|
1303
1548
|
//#endregion
|
|
1304
|
-
|
|
1549
|
+
//#region src/workspace/in-use.ts
|
|
1550
|
+
const NATIVE_RUN = "native-run";
|
|
1551
|
+
const NATIVE_RUN_HELD = "a stim ios, android or stop run holds its native-run.lock";
|
|
1552
|
+
function workspaceInUse(root, { supervisor = true, managedLocks = true, hosted = true, nativeRun = true } = {}) {
|
|
1553
|
+
const reasons = [];
|
|
1554
|
+
if (supervisor) {
|
|
1555
|
+
const project = getProject(root);
|
|
1556
|
+
const target = resolveSupervisorTarget({
|
|
1557
|
+
state: readWorkspaceState$1(root)?.supervisor,
|
|
1558
|
+
record: project?.supervisor,
|
|
1559
|
+
reservedPort: project?.metroPort
|
|
1560
|
+
});
|
|
1561
|
+
if (target.status === "ours") reasons.push(`its dev server supervisor (pid ${target.pid}) is running`);
|
|
1562
|
+
else if (target.status === "unverified") reasons.push(`its dev server supervisor cannot be verified: ${target.reason ?? "unknown identity"}`);
|
|
1563
|
+
const macos = macosAppState(readMacosRecord(root));
|
|
1564
|
+
if (macos?.host) reasons.push(`its macOS app runs on ${macos.host.machine}`);
|
|
1565
|
+
else if (macos?.state === "running" || macos?.state === "orphaned" || macos?.build.state === "running") reasons.push("its macOS app or build is running");
|
|
1566
|
+
else if (macos?.state === "unverified" || !macos && readWorkspaceState$1(root)?.macos !== void 0) reasons.push("its macOS process owner cannot be verified");
|
|
1567
|
+
else try {
|
|
1568
|
+
const pids = bundleInstancePids(root);
|
|
1569
|
+
if (pids.length) reasons.push(`its macOS app is running (pid ${pids.join(", ")})`);
|
|
1570
|
+
} catch (error) {
|
|
1571
|
+
reasons.push(`its running macOS apps cannot be listed: ${error.message}`);
|
|
1572
|
+
}
|
|
1573
|
+
const runtime = (0, ownership_claim_exports.readClaimSet)(macosRuntimeClaim(root));
|
|
1574
|
+
if (runtime.live.length) reasons.push("its macOS runtime claim is held");
|
|
1575
|
+
else if (runtime.unresolved[0]) reasons.push(`its macOS runtime cannot be verified: ${runtime.unresolved[0].reason}`);
|
|
1576
|
+
const browser = webFacts(readWebRecord(root));
|
|
1577
|
+
if (browser?.status === "running" || browser?.status === "orphaned") reasons.push(`its owned Chrome (pid ${browser.record.chromeProcess?.pid}) is running`);
|
|
1578
|
+
else if (browser?.status === "unverified") reasons.push("its owned Chrome cannot be verified");
|
|
1579
|
+
}
|
|
1580
|
+
if (nativeRun) {
|
|
1581
|
+
const claims = (0, ownership_claim_exports.readClaimSet)(workspaceProcessLockPath(workspaceDir(root), NATIVE_RUN, true));
|
|
1582
|
+
if (claims.live.length) reasons.push(NATIVE_RUN_HELD);
|
|
1583
|
+
else if (claims.unresolved[0]) reasons.push(`its native-run.lock cannot be resolved: ${claims.unresolved[0].reason}`);
|
|
1584
|
+
}
|
|
1585
|
+
for (const platform of ["ios", "android"]) for (const [slot, record] of Object.entries(hosted ? hostedNativeRecords(readWorkspaceState$1(root), platform) : {})) {
|
|
1586
|
+
const placement = parseHostedNativePlacement(record, platform);
|
|
1587
|
+
reasons.push(placement ? `its ${platform === "ios" ? "iOS simulator" : "Android emulator"} runs on ${placement.machine}` : unreadableHostedNative(slot, platform));
|
|
1588
|
+
}
|
|
1589
|
+
const self = canonicalPath(root);
|
|
1590
|
+
for (const [kind, entries] of [["build lock", listBuildLocks()], ["build slot", listBuildSlots()]]) for (const entry of entries) {
|
|
1591
|
+
if (!entry.alive && !entry.unresolved) continue;
|
|
1592
|
+
if (entry.projectRoot !== null && canonicalPath(entry.projectRoot) === self) reasons.push(`a ${entry.alive ? "live" : "unresolvable"} ${kind} at ${entry.path} names it`);
|
|
1593
|
+
}
|
|
1594
|
+
if (managedLocks) reasons.push(...managedLockHolders(root));
|
|
1595
|
+
return reasons;
|
|
1596
|
+
}
|
|
1597
|
+
function emptyWorkspaceDir(dir) {
|
|
1598
|
+
if (lstatSync(dir).isSymbolicLink()) {
|
|
1599
|
+
unlinkSync(dir);
|
|
1600
|
+
return;
|
|
1601
|
+
}
|
|
1602
|
+
for (const name of readdirSync(dir)) if (name !== `${NATIVE_RUN}.lock`) rmSync(join(dir, name), {
|
|
1603
|
+
recursive: true,
|
|
1604
|
+
force: true
|
|
1605
|
+
});
|
|
1606
|
+
}
|
|
1607
|
+
async function withIdleWorkspace(root, fn, { purpose, ...checks }) {
|
|
1608
|
+
try {
|
|
1609
|
+
return await withWorkspaceProcessLock(workspaceDir(root), NATIVE_RUN, async () => {
|
|
1610
|
+
const reasons = workspaceInUse(root, {
|
|
1611
|
+
...checks,
|
|
1612
|
+
nativeRun: false
|
|
1613
|
+
});
|
|
1614
|
+
if (reasons.length) return {
|
|
1615
|
+
ran: false,
|
|
1616
|
+
reasons
|
|
1617
|
+
};
|
|
1618
|
+
return {
|
|
1619
|
+
ran: true,
|
|
1620
|
+
value: await fn()
|
|
1621
|
+
};
|
|
1622
|
+
}, {
|
|
1623
|
+
external: true,
|
|
1624
|
+
waitMs: 0,
|
|
1625
|
+
ownerPurpose: purpose
|
|
1626
|
+
});
|
|
1627
|
+
} catch (error) {
|
|
1628
|
+
if (workspaceProcessLockError(error)) return {
|
|
1629
|
+
ran: false,
|
|
1630
|
+
reasons: [NATIVE_RUN_HELD]
|
|
1631
|
+
};
|
|
1632
|
+
if ((0, ownership_claim_exports.isClaimRefusal)(error) || (0, ownership_claim_exports.isClaimUnavailable)(error)) return {
|
|
1633
|
+
ran: false,
|
|
1634
|
+
reasons: [`its native-run.lock cannot be taken: ${error.reason}`]
|
|
1635
|
+
};
|
|
1636
|
+
throw error;
|
|
1637
|
+
} finally {
|
|
1638
|
+
try {
|
|
1639
|
+
const dir = workspaceDir(root);
|
|
1640
|
+
if (!lstatSync(dir).isSymbolicLink()) rmdirSync(dir);
|
|
1641
|
+
} catch {}
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
//#endregion
|
|
1645
|
+
//#region src/commands/gc/workspaces.ts
|
|
1646
|
+
function workspacesRoot() {
|
|
1647
|
+
return join$1(getConfigDir(), "workspaces");
|
|
1648
|
+
}
|
|
1649
|
+
function isInsideWorkspaces(dir) {
|
|
1650
|
+
const rel = relative$1(canonicalPath(workspacesRoot()), canonicalPath(dir));
|
|
1651
|
+
return rel !== "" && !rel.startsWith("..") && !isAbsolute$1(rel);
|
|
1652
|
+
}
|
|
1653
|
+
function readRecordedRoot(dir, name) {
|
|
1654
|
+
let parsed;
|
|
1655
|
+
try {
|
|
1656
|
+
parsed = readJsonFile(join$1(dir, "workspace.json"));
|
|
1657
|
+
} catch (error) {
|
|
1658
|
+
return {
|
|
1659
|
+
projectRoot: null,
|
|
1660
|
+
problem: error?.code === "ENOENT" ? "it has no workspace.json" : "its workspace.json does not parse"
|
|
1661
|
+
};
|
|
1662
|
+
}
|
|
1663
|
+
const projectRoot = isJsonObject(parsed) ? parsed.projectRoot : void 0;
|
|
1664
|
+
if (typeof projectRoot !== "string" || !isAbsolute$1(projectRoot)) return {
|
|
1665
|
+
projectRoot: null,
|
|
1666
|
+
problem: "its workspace.json records no absolute project root"
|
|
1667
|
+
};
|
|
1668
|
+
if (workspaceName(projectRoot) !== name) return {
|
|
1669
|
+
projectRoot: null,
|
|
1670
|
+
problem: `its workspace.json records ${projectRoot}, whose workspace directory has another name`
|
|
1671
|
+
};
|
|
1672
|
+
return {
|
|
1673
|
+
projectRoot,
|
|
1674
|
+
problem: null
|
|
1675
|
+
};
|
|
1676
|
+
}
|
|
1677
|
+
function listWorkspaceDirs() {
|
|
1678
|
+
const root = workspacesRoot();
|
|
1679
|
+
let names;
|
|
1680
|
+
try {
|
|
1681
|
+
names = readdirSync$1(root).toSorted();
|
|
1682
|
+
} catch {
|
|
1683
|
+
return [];
|
|
1684
|
+
}
|
|
1685
|
+
const entries = [];
|
|
1686
|
+
for (const name of names) {
|
|
1687
|
+
const dir = join$1(root, name);
|
|
1688
|
+
try {
|
|
1689
|
+
if (!statSync$1(dir).isDirectory()) continue;
|
|
1690
|
+
} catch {
|
|
1691
|
+
continue;
|
|
1692
|
+
}
|
|
1693
|
+
entries.push({
|
|
1694
|
+
dir,
|
|
1695
|
+
...readRecordedRoot(dir, name)
|
|
1696
|
+
});
|
|
1697
|
+
}
|
|
1698
|
+
return entries;
|
|
1699
|
+
}
|
|
1700
|
+
function classifyWorkspaceDirs(entries, { registryKeys, exists, isMounted, inUse }) {
|
|
1701
|
+
const orphaned = [];
|
|
1702
|
+
const skipped = [];
|
|
1703
|
+
for (const { dir, projectRoot, problem } of entries) {
|
|
1704
|
+
if (projectRoot === null) {
|
|
1705
|
+
skipped.push({
|
|
1706
|
+
dir,
|
|
1707
|
+
reason: `workspace directory not resolved: ${problem ?? "unknown project root"}`
|
|
1708
|
+
});
|
|
1709
|
+
continue;
|
|
1710
|
+
}
|
|
1711
|
+
const present = exists(projectRoot);
|
|
1712
|
+
if (present === null) {
|
|
1713
|
+
skipped.push({
|
|
1714
|
+
dir,
|
|
1715
|
+
reason: `cannot tell whether its project root ${projectRoot} still exists`
|
|
1716
|
+
});
|
|
1717
|
+
continue;
|
|
1718
|
+
}
|
|
1719
|
+
if (present) continue;
|
|
1720
|
+
if (registryKeys.some((key) => isPathPrefix(projectRoot, key))) continue;
|
|
1721
|
+
if (!isMounted(projectRoot)) {
|
|
1722
|
+
skipped.push({
|
|
1723
|
+
dir,
|
|
1724
|
+
reason: `the volume of its project root ${projectRoot} is not mounted`
|
|
1725
|
+
});
|
|
1726
|
+
continue;
|
|
1727
|
+
}
|
|
1728
|
+
const reasons = inUse(projectRoot);
|
|
1729
|
+
if (reasons.length) {
|
|
1730
|
+
skipped.push({
|
|
1731
|
+
dir,
|
|
1732
|
+
reason: `its project root ${projectRoot} is gone, but ${reasons.join("; ")}`
|
|
1733
|
+
});
|
|
1734
|
+
continue;
|
|
1735
|
+
}
|
|
1736
|
+
orphaned.push({
|
|
1737
|
+
dir,
|
|
1738
|
+
projectRoot
|
|
1739
|
+
});
|
|
1740
|
+
}
|
|
1741
|
+
return {
|
|
1742
|
+
orphaned,
|
|
1743
|
+
skipped
|
|
1744
|
+
};
|
|
1745
|
+
}
|
|
1746
|
+
function rootPresence(path) {
|
|
1747
|
+
try {
|
|
1748
|
+
lstatSync$1(path);
|
|
1749
|
+
return true;
|
|
1750
|
+
} catch (error) {
|
|
1751
|
+
const code = error?.code;
|
|
1752
|
+
return code === "ENOENT" || code === "ENOTDIR" ? false : null;
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1755
|
+
function collectOrphanedWorkspaces(registryKeys, mountedVolumes, { measure = true } = {}) {
|
|
1756
|
+
const classified = classifyWorkspaceDirs(listWorkspaceDirs(), {
|
|
1757
|
+
registryKeys,
|
|
1758
|
+
exists: rootPresence,
|
|
1759
|
+
isMounted: (path) => isOnMountedVolume(path, mountedVolumes),
|
|
1760
|
+
inUse: (root) => workspaceInUse(root)
|
|
1761
|
+
});
|
|
1762
|
+
return {
|
|
1763
|
+
orphaned: measure ? classified.orphaned.map((entry) => Object.assign({}, entry, { bytes: sizeOf(entry.dir) })) : classified.orphaned,
|
|
1764
|
+
skipped: classified.skipped
|
|
1765
|
+
};
|
|
1766
|
+
}
|
|
1767
|
+
const REBUILD_COST = "The next build of an unchanged app installs from the shared build cache; after a native change the compilation cache speeds the rebuild, but on React Native 0.86 Swift does not use it (explicit modules are off), so that build recompiles Swift. The next stim macos in a cleared workspace is a full Swift build.";
|
|
1768
|
+
const WORKSPACE_OUTPUT_DIRS = [
|
|
1769
|
+
"derived-data",
|
|
1770
|
+
"gradle-build",
|
|
1771
|
+
"android-cas",
|
|
1772
|
+
"cache-provider"
|
|
1773
|
+
];
|
|
1774
|
+
const DAY_MS = 864e5;
|
|
1775
|
+
function planWorkspaceOutputs(entries, { olderThan, now }) {
|
|
1776
|
+
return entries.map(({ dir, projectRoot, problem, bytes, lastUsed, inUse }) => {
|
|
1777
|
+
const idleDays = Number.isFinite(lastUsed) ? Math.max(0, Math.floor((now - lastUsed) / DAY_MS)) : null;
|
|
1778
|
+
const [keptCode, keptReason] = projectRoot === null ? ["unresolved", `workspace directory not resolved: ${problem ?? "unknown project root"}`] : inUse.length ? ["in-use", `in use: ${inUse.join("; ")}`] : olderThan === null ? [null, null] : idleDays === null ? ["last-use-unknown", "its last use is unknown"] : idleDays < olderThan ? ["recently-used", `used ${idleDays}d ago, within --older-than ${olderThan}`] : [null, null];
|
|
1779
|
+
return {
|
|
1780
|
+
dir,
|
|
1781
|
+
projectRoot,
|
|
1782
|
+
bytes,
|
|
1783
|
+
idleDays,
|
|
1784
|
+
willClear: keptCode === null,
|
|
1785
|
+
keptCode,
|
|
1786
|
+
keptReason
|
|
1787
|
+
};
|
|
1788
|
+
});
|
|
1789
|
+
}
|
|
1790
|
+
const SIZE_TIMEOUT_MS = 6e4;
|
|
1791
|
+
function sizeOf(dir, timeoutMs = SIZE_TIMEOUT_MS) {
|
|
1792
|
+
return measuredDirectorySize(dir, { timeoutMs });
|
|
1793
|
+
}
|
|
1794
|
+
function outputPaths(dir) {
|
|
1795
|
+
const paths = WORKSPACE_OUTPUT_DIRS.map((name) => join$1(dir, name)).filter((path) => existsSync$1(path));
|
|
1796
|
+
const macos = join$1(dir, "macos");
|
|
1797
|
+
try {
|
|
1798
|
+
if (!lstatSync$1(macos).isDirectory()) return paths;
|
|
1799
|
+
for (const entry of readdirSync$1(macos, { withFileTypes: true })) if (entry.name === "build" || entry.name.endsWith(".app") || entry.name.startsWith("staging-") && entry.isDirectory()) paths.push(join$1(macos, entry.name));
|
|
1800
|
+
} catch {}
|
|
1801
|
+
return paths;
|
|
1802
|
+
}
|
|
1803
|
+
function outputBytes(paths, timeoutMs) {
|
|
1804
|
+
let total = 0;
|
|
1805
|
+
for (const path of paths) {
|
|
1806
|
+
const size = sizeOf(path, timeoutMs);
|
|
1807
|
+
if (size === null) return null;
|
|
1808
|
+
total += size;
|
|
1809
|
+
}
|
|
1810
|
+
return total;
|
|
1811
|
+
}
|
|
1812
|
+
function collectWorkspaceOutputs({ olderThan, now, exclude = [], measure = true, sizeTimeoutMs = SIZE_TIMEOUT_MS }) {
|
|
1813
|
+
const mountedVolumes = listMountedVolumes();
|
|
1814
|
+
const entries = listWorkspaceDirs().filter((entry) => !exclude.includes(entry.dir)).filter((entry) => entry.projectRoot === null || isOnMountedVolume(entry.projectRoot, mountedVolumes)).map((entry) => Object.assign({}, entry, { paths: outputPaths(entry.dir) })).filter((entry) => entry.paths.length > 0).map(({ paths, ...entry }) => Object.assign({}, entry, { bytes: measure ? outputBytes(paths, sizeTimeoutMs) : null })).map((entry) => Object.assign({}, entry, {
|
|
1815
|
+
lastUsed: entry.projectRoot === null ? NaN : workspaceLastUsed(entry.projectRoot),
|
|
1816
|
+
inUse: entry.projectRoot === null ? [] : workspaceInUse(entry.projectRoot)
|
|
1817
|
+
}));
|
|
1818
|
+
return {
|
|
1819
|
+
root: workspacesRoot(),
|
|
1820
|
+
workspaces: planWorkspaceOutputs(entries, {
|
|
1821
|
+
olderThan,
|
|
1822
|
+
now
|
|
1823
|
+
})
|
|
1824
|
+
};
|
|
1825
|
+
}
|
|
1826
|
+
async function clearWorkspaceOutputs(report, { olderThan, now = Date.now() }) {
|
|
1827
|
+
let failures = 0;
|
|
1828
|
+
let cleared = 0;
|
|
1829
|
+
const clearedRoots = [];
|
|
1830
|
+
for (const entry of report.workspaces) {
|
|
1831
|
+
const root = entry.projectRoot;
|
|
1832
|
+
if (!entry.willClear || root === null) {
|
|
1833
|
+
if (entry.keptReason) {
|
|
1834
|
+
console.log(chalk.dim(`Kept the build outputs of ${root ?? entry.dir}: ${entry.keptReason}`));
|
|
1835
|
+
recordGcResult("workspaceOutputs", "kept", root ?? entry.dir, {
|
|
1836
|
+
bytes: entry.bytes,
|
|
1837
|
+
detail: entry.keptReason
|
|
1838
|
+
});
|
|
1839
|
+
}
|
|
1840
|
+
continue;
|
|
1841
|
+
}
|
|
1842
|
+
let run;
|
|
1843
|
+
try {
|
|
1844
|
+
run = await withIdleWorkspace(root, () => {
|
|
1845
|
+
const [again] = planWorkspaceOutputs([{
|
|
1846
|
+
...entry,
|
|
1847
|
+
problem: null,
|
|
1848
|
+
lastUsed: workspaceLastUsed(root),
|
|
1849
|
+
inUse: []
|
|
1850
|
+
}], {
|
|
1851
|
+
olderThan,
|
|
1852
|
+
now
|
|
1853
|
+
});
|
|
1854
|
+
if (again?.keptReason) return again.keptReason;
|
|
1855
|
+
for (const path of outputPaths(entry.dir)) rmSync$1(path, {
|
|
1856
|
+
recursive: true,
|
|
1857
|
+
force: true
|
|
1858
|
+
});
|
|
1859
|
+
return null;
|
|
1860
|
+
}, { purpose: "gc" });
|
|
1861
|
+
} catch (error) {
|
|
1862
|
+
failures++;
|
|
1863
|
+
console.log(chalk.red(`Could not clear the build outputs of ${root}: ${error?.message || String(error)}`));
|
|
1864
|
+
recordGcResult("workspaceOutputs", "failed", root, {
|
|
1865
|
+
bytes: entry.bytes,
|
|
1866
|
+
detail: error?.message || String(error)
|
|
1867
|
+
});
|
|
1868
|
+
continue;
|
|
1869
|
+
}
|
|
1870
|
+
const kept = run.ran ? run.value : `in use: ${run.reasons.join("; ")}`;
|
|
1871
|
+
if (kept) {
|
|
1872
|
+
console.log(chalk.yellow(`Kept the build outputs of ${root}: ${kept}`));
|
|
1873
|
+
recordGcResult("workspaceOutputs", "kept", root, {
|
|
1874
|
+
bytes: entry.bytes,
|
|
1875
|
+
detail: kept
|
|
1876
|
+
});
|
|
1877
|
+
continue;
|
|
1878
|
+
}
|
|
1879
|
+
cleared += entry.bytes ?? 0;
|
|
1880
|
+
clearedRoots.push(root);
|
|
1881
|
+
console.log(chalk.green(`Cleared the build outputs of ${root} (${sizeText(entry.bytes)})`));
|
|
1882
|
+
recordGcResult("workspaceOutputs", "done", root, { bytes: entry.bytes });
|
|
1883
|
+
}
|
|
1884
|
+
if (cleared) console.log(chalk.dim(`Cleared ${formatBytes(cleared)} of workspace build outputs. ${REBUILD_COST}`));
|
|
1885
|
+
return {
|
|
1886
|
+
failures,
|
|
1887
|
+
cleared: clearedRoots
|
|
1888
|
+
};
|
|
1889
|
+
}
|
|
1890
|
+
function stillOrphaned({ dir, projectRoot }) {
|
|
1891
|
+
if (readRecordedRoot(dir, basename$1(dir)).projectRoot !== projectRoot || rootPresence(projectRoot) !== false || !isOnMountedVolume(projectRoot)) return false;
|
|
1892
|
+
return !Object.keys(loadConfig$1()?.projects ?? {}).some((key) => isPathPrefix(projectRoot, key));
|
|
1893
|
+
}
|
|
1894
|
+
async function deleteOrphanedWorkspaces(orphaned) {
|
|
1895
|
+
let failures = 0;
|
|
1896
|
+
for (const entry of orphaned) {
|
|
1897
|
+
if (!existsSync$1(entry.dir)) continue;
|
|
1898
|
+
let run;
|
|
1899
|
+
try {
|
|
1900
|
+
run = await withIdleWorkspace(entry.projectRoot, async () => {
|
|
1901
|
+
if (!stillOrphaned(entry)) return false;
|
|
1902
|
+
const browser = await teardownOwnedBrowser(entry.projectRoot, { deleteProfile: true });
|
|
1903
|
+
if (browser.status === "failed" || browser.status === "skipped") throw new Error(`its owned Chrome profile was kept: ${browser.reason}`);
|
|
1904
|
+
emptyWorkspaceDir(entry.dir);
|
|
1905
|
+
return true;
|
|
1906
|
+
}, { purpose: "gc" });
|
|
1907
|
+
} catch (error) {
|
|
1908
|
+
failures++;
|
|
1909
|
+
console.log(chalk.red(`Could not remove ${entry.dir}: ${error?.message || error}`));
|
|
1910
|
+
recordGcResult("workspaceDirectory", "failed", entry.dir, {
|
|
1911
|
+
bytes: entry.bytes ?? null,
|
|
1912
|
+
detail: error?.message || String(error)
|
|
1913
|
+
});
|
|
1914
|
+
continue;
|
|
1915
|
+
}
|
|
1916
|
+
if (!run.ran) {
|
|
1917
|
+
console.log(chalk.yellow(`Kept ${entry.dir}: ${run.reasons.join("; ")}`));
|
|
1918
|
+
recordGcResult("workspaceDirectory", "kept", entry.dir, {
|
|
1919
|
+
bytes: entry.bytes ?? null,
|
|
1920
|
+
detail: run.reasons.join("; ")
|
|
1921
|
+
});
|
|
1922
|
+
continue;
|
|
1923
|
+
}
|
|
1924
|
+
if (!run.value) {
|
|
1925
|
+
console.log(chalk.yellow(`Kept ${entry.dir}: it can no longer be confirmed orphaned.`));
|
|
1926
|
+
recordGcResult("workspaceDirectory", "kept", entry.dir, {
|
|
1927
|
+
bytes: entry.bytes ?? null,
|
|
1928
|
+
detail: "it can no longer be confirmed orphaned"
|
|
1929
|
+
});
|
|
1930
|
+
continue;
|
|
1931
|
+
}
|
|
1932
|
+
const size = entry.bytes === void 0 ? "" : ` (${sizeText(entry.bytes)})`;
|
|
1933
|
+
console.log(chalk.green(`Removed the orphaned workspace directory ${entry.dir}${size}`));
|
|
1934
|
+
recordGcResult("workspaceDirectory", "done", entry.dir, { bytes: entry.bytes ?? null });
|
|
1935
|
+
}
|
|
1936
|
+
return failures;
|
|
1937
|
+
}
|
|
1938
|
+
function sizeText(bytes) {
|
|
1939
|
+
return bytes === null ? "size unknown" : formatBytes(bytes);
|
|
1940
|
+
}
|
|
1941
|
+
//#endregion
|
|
1942
|
+
export { teardownParkedAvd as A, runningBundleInstances as C, teardownOwnedAvd as D, teardownBrowserHeld as E, recordGcResult as F, takeGcResults as I, canonicalPath as L, acquireBuildSlot as M, listBuildSlots as N, teardownOwnedBrowser as O, releaseBuildSlot as P, probePublicHttp as S, eraseParkedIosSim as T, terminateChild as _, collectWorkspaceOutputs as a, withManagedTunnelLock as b, listWorkspaceDirs as c, emptyWorkspaceDir as d, withIdleWorkspace as f, stopTunnel as g, startTunnelSequence as h, collectOrphanedWorkspaces as i, teardownParkedIosSim as j, teardownOwnedIosSim as k, rootPresence as l, resolveSupervisorTarget as m, WORKSPACE_OUTPUT_DIRS as n, deleteOrphanedWorkspaces as o, workspaceInUse as p, clearWorkspaceOutputs as r, isInsideWorkspaces as s, REBUILD_COST as t, sizeText as u, withManagedRemoteWorktreeLock as v, eraseParkedAvd as w, withManagedTunnelRemovalLock as x, withManagedRemoteWorktreeRemovalLock as y };
|