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
package/dist/maintenance-run.mjs
CHANGED
|
@@ -1,16 +1,193 @@
|
|
|
1
|
-
import { j as formatBytes } from "./config-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { r as
|
|
5
|
-
import {
|
|
6
|
-
import { r as
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
1
|
+
import { M as isOnMountedVolume, N as listMountedVolumes, _ as loadConfig$1, j as formatBytes } from "./config-DC5jEtd1.mjs";
|
|
2
|
+
import { D as projectMaintenancePinned } from "./settings-CxgngUZk.mjs";
|
|
3
|
+
import { a as unregister, n as readManifest } from "./cache-manifest-DiwKSyv9.mjs";
|
|
4
|
+
import { Nt as discoverCaches, Pt as pruneCache, l as resolveBudget, pt as planCacheEmptying, r as cacheLabel, st as emptyCaches } from "./budget-Dz-kyXLv.mjs";
|
|
5
|
+
import { I as takeGcResults, L as canonicalPath, a as collectWorkspaceOutputs, i as collectOrphanedWorkspaces, o as deleteOrphanedWorkspaces, r as clearWorkspaceOutputs } from "./workspaces-Djb2gfi9.mjs";
|
|
6
|
+
import { r as createNdjsonWriter } from "./ndjson-LGZAMsqa.mjs";
|
|
7
|
+
import { t as relaunchWithLogFile } from "./detached-entry-pB_IPfV4.mjs";
|
|
8
|
+
import { t as resolveMaintenanceSettings } from "./settings-Djl-mpjX.mjs";
|
|
9
|
+
import { r as due, t as capChildLog } from "./attempt-DW-mQqM7.mjs";
|
|
10
|
+
import { a as recentUse, d as measureDisk, f as measurePressure, i as cacheEntryProtection, m as sizeScanDeferred, p as measureSizes, r as triggeringRoot, s as removeWorktrees, t as plannedMaintenance, u as cacheBlocked } from "./preview-BbSottmO.mjs";
|
|
11
|
+
import { appendFileSync, existsSync, mkdirSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
12
|
+
import { basename, join, resolve, sep } from "node:path";
|
|
13
|
+
import { MAINTENANCE_LOG_NAME, maintenanceChildLogFile, maintenanceDir, maintenanceNdjsonFile, maintenanceRunClaims, maintenanceStateFile, readMaintenanceState, sharedCompilationCache, workspaceLogsDir } from "@stim-cli/core/state";
|
|
13
14
|
import { fileURLToPath } from "node:url";
|
|
15
|
+
import { LOG_ROTATE_BYTES, buildCacheRoot, metroCacheRoot, rotatedLogPath, withDirLock } from "@stim-cli/core";
|
|
16
|
+
import { releaseClaim, tryAcquireClaim } from "@stim-cli/core/ownership-claim";
|
|
17
|
+
//#region src/maintenance/act.ts
|
|
18
|
+
const kept = (detail) => ({
|
|
19
|
+
status: "kept",
|
|
20
|
+
bytes: 0,
|
|
21
|
+
detail
|
|
22
|
+
});
|
|
23
|
+
async function quietly(run) {
|
|
24
|
+
const { log, error } = {
|
|
25
|
+
log: console.log,
|
|
26
|
+
error: console.error
|
|
27
|
+
};
|
|
28
|
+
const exitCode = process.exitCode;
|
|
29
|
+
takeGcResults();
|
|
30
|
+
console.log = () => {};
|
|
31
|
+
console.error = () => {};
|
|
32
|
+
try {
|
|
33
|
+
return {
|
|
34
|
+
value: await run(),
|
|
35
|
+
results: takeGcResults()
|
|
36
|
+
};
|
|
37
|
+
} finally {
|
|
38
|
+
console.log = log;
|
|
39
|
+
console.error = error;
|
|
40
|
+
process.exitCode = exitCode;
|
|
41
|
+
takeGcResults();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function outcomeOf(results, fallbackBytes) {
|
|
45
|
+
const failed = results.find((result) => result.status === "failed");
|
|
46
|
+
if (failed) return {
|
|
47
|
+
status: "failed",
|
|
48
|
+
bytes: 0,
|
|
49
|
+
detail: failed.detail ?? `could not process ${failed.label}`
|
|
50
|
+
};
|
|
51
|
+
const done = results.filter((result) => result.status === "done");
|
|
52
|
+
if (done.length) return {
|
|
53
|
+
status: "done",
|
|
54
|
+
bytes: done.reduce((sum, result) => sum + (result.bytes ?? 0), 0) || fallbackBytes,
|
|
55
|
+
detail: done.map((result) => result.label).join(", ")
|
|
56
|
+
};
|
|
57
|
+
return kept(results.find((result) => result.detail)?.detail ?? "nothing was eligible when the action ran");
|
|
58
|
+
}
|
|
59
|
+
function rootCache(dir) {
|
|
60
|
+
const name = canonicalPath(dir) === canonicalPath(buildCacheRoot()) ? "Build cache" : canonicalPath(dir) === canonicalPath(metroCacheRoot()) ? "Metro transform caches" : null;
|
|
61
|
+
return name ? {
|
|
62
|
+
name,
|
|
63
|
+
dir,
|
|
64
|
+
prune: "entries",
|
|
65
|
+
entriesDepth: name === "Build cache" ? 2 : 3,
|
|
66
|
+
note: ""
|
|
67
|
+
} : null;
|
|
68
|
+
}
|
|
69
|
+
function cacheOf(action) {
|
|
70
|
+
const discovered = discoverCaches();
|
|
71
|
+
if (action.dir) {
|
|
72
|
+
const dir = canonicalPath(action.dir);
|
|
73
|
+
return discovered.find((cache) => canonicalPath(cache.dir) === dir) ?? rootCache(action.dir);
|
|
74
|
+
}
|
|
75
|
+
return discovered.find((cache) => cacheLabel(cache) === action.target) ?? null;
|
|
76
|
+
}
|
|
77
|
+
function trimCache(action, context) {
|
|
78
|
+
const cache = cacheOf(action);
|
|
79
|
+
if (!cache) return kept("it is no longer a cache Stim may trim");
|
|
80
|
+
const [planned] = planCacheEmptying([cache], false);
|
|
81
|
+
if (!planned || planned.machineGlobal) return kept(planned?.machineGlobal ?? "it is no longer a cache Stim may trim");
|
|
82
|
+
const aged = action.olderThanDays !== void 0;
|
|
83
|
+
const result = pruneCache(cache, {
|
|
84
|
+
olderThanDays: Math.max(action.olderThanDays ?? 0, context.settings.protectRecentHours / 24),
|
|
85
|
+
byMtime: true,
|
|
86
|
+
protect: context.protect ??= cacheEntryProtection(),
|
|
87
|
+
...aged ? {} : { evictBytes: action.bytes }
|
|
88
|
+
});
|
|
89
|
+
if (result.skipped) return kept(result.skipped);
|
|
90
|
+
if (result.failed) return {
|
|
91
|
+
status: "failed",
|
|
92
|
+
bytes: result.bytes,
|
|
93
|
+
detail: `${result.failed} entries could not be removed`
|
|
94
|
+
};
|
|
95
|
+
const protectedNote = result.protectedEntries ? `; ${result.protectedEntries} protected entries kept` : "";
|
|
96
|
+
if (result.removed === 0) return kept(`no unprotected entry was old enough${protectedNote}`);
|
|
97
|
+
return {
|
|
98
|
+
status: "done",
|
|
99
|
+
bytes: result.bytes,
|
|
100
|
+
detail: `${result.removed} entries${protectedNote}`,
|
|
101
|
+
...!aged && result.bytes < action.bytes ? { shortfall: `${action.target} reached ${result.bytes} of ${action.bytes} bytes${protectedNote}` } : {}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
async function emptyCompilationCache(action, context) {
|
|
105
|
+
if (!measureDisk().some((disk) => disk.freeMb < context.budget.hardFloorDiskMb)) return kept("free disk is above budget.hardFloorDiskGb, and this cache can only be emptied whole");
|
|
106
|
+
const dir = sharedCompilationCache();
|
|
107
|
+
const blocked = cacheBlocked({
|
|
108
|
+
name: "Swift compilation cache",
|
|
109
|
+
dir,
|
|
110
|
+
category: "compilation-cache"
|
|
111
|
+
});
|
|
112
|
+
if (blocked) return kept(blocked);
|
|
113
|
+
const cache = discoverCaches().find((candidate) => canonicalPath(candidate.dir) === canonicalPath(dir));
|
|
114
|
+
if (cache?.prune !== "atomic") return kept("it is not a registered cache Stim empties whole");
|
|
115
|
+
const [planned] = planCacheEmptying([{
|
|
116
|
+
...cache,
|
|
117
|
+
bytes: action.bytes
|
|
118
|
+
}], true);
|
|
119
|
+
const { results } = await quietly(() => emptyCaches(planned ? [planned] : []));
|
|
120
|
+
return outcomeOf(results, action.bytes);
|
|
121
|
+
}
|
|
122
|
+
async function clearOutputs(action, context) {
|
|
123
|
+
const root = action.workspace;
|
|
124
|
+
if (!root) return kept("the action names no workspace");
|
|
125
|
+
if (root === context.protectedRoot) return kept("the command that started this pass runs in it");
|
|
126
|
+
if (projectMaintenancePinned(root)) return kept("pinned by maintenance.keep");
|
|
127
|
+
const recent = recentUse(root, context.settings);
|
|
128
|
+
if (recent) return kept(recent);
|
|
129
|
+
const olderThan = action.olderThanDays ?? null;
|
|
130
|
+
const now = Date.now();
|
|
131
|
+
const report = collectWorkspaceOutputs({
|
|
132
|
+
olderThan,
|
|
133
|
+
now,
|
|
134
|
+
measure: false
|
|
135
|
+
});
|
|
136
|
+
const entry = report.workspaces.find((candidate) => candidate.projectRoot && canonicalPath(candidate.projectRoot) === root);
|
|
137
|
+
if (!entry) return kept("it has no build outputs any more");
|
|
138
|
+
const { results } = await quietly(() => clearWorkspaceOutputs({
|
|
139
|
+
...report,
|
|
140
|
+
workspaces: [entry]
|
|
141
|
+
}, {
|
|
142
|
+
olderThan,
|
|
143
|
+
now
|
|
144
|
+
}));
|
|
145
|
+
return outcomeOf(results, action.bytes);
|
|
146
|
+
}
|
|
147
|
+
async function removeWorktree(action, context) {
|
|
148
|
+
const sweep = context.worktreeSweep;
|
|
149
|
+
const worktree = sweep?.worktrees.find((candidate) => candidate.path === action.target);
|
|
150
|
+
if (!sweep || !worktree) return kept("it is not in this pass's worktree check");
|
|
151
|
+
const path = canonicalPath(worktree.path);
|
|
152
|
+
if (context.protectedRoot === path || context.protectedRoot.startsWith(`${path}${sep}`)) return kept("the command that started this pass runs inside it");
|
|
153
|
+
if (worktree.keys.some(projectMaintenancePinned)) return kept("pinned by maintenance.keep");
|
|
154
|
+
const { results } = await quietly(() => removeWorktrees({
|
|
155
|
+
...sweep,
|
|
156
|
+
worktrees: [worktree]
|
|
157
|
+
}));
|
|
158
|
+
return outcomeOf(results, 0);
|
|
159
|
+
}
|
|
160
|
+
async function removeOrphan(action) {
|
|
161
|
+
const orphan = collectOrphanedWorkspaces(Object.keys(loadConfig$1()?.projects ?? {}), listMountedVolumes()).orphaned.find((candidate) => candidate.dir === action.target);
|
|
162
|
+
if (!orphan) return kept("it is no longer an orphaned workspace directory");
|
|
163
|
+
const { results } = await quietly(() => deleteOrphanedWorkspaces([orphan]));
|
|
164
|
+
return outcomeOf(results, orphan.bytes ?? 0);
|
|
165
|
+
}
|
|
166
|
+
function unregisterCache(action) {
|
|
167
|
+
const dir = action.dir ?? action.target;
|
|
168
|
+
if (existsSync(dir) || !isOnMountedVolume(dir, listMountedVolumes()) || !readManifest().caches.some((c) => c.dir === dir)) return kept("it can no longer be shown to be a stale registration");
|
|
169
|
+
return unregister(dir) ? {
|
|
170
|
+
status: "done",
|
|
171
|
+
bytes: 0,
|
|
172
|
+
detail: dir
|
|
173
|
+
} : kept("it was already unregistered");
|
|
174
|
+
}
|
|
175
|
+
async function executeAction(action, context) {
|
|
176
|
+
switch (action.kind) {
|
|
177
|
+
case "would-clear-outputs": return clearOutputs(action, context);
|
|
178
|
+
case "would-trim-cache": return trimCache(action, context);
|
|
179
|
+
case "would-empty-cache": return emptyCompilationCache(action, context);
|
|
180
|
+
case "would-remove-worktree": return removeWorktree(action, context);
|
|
181
|
+
case "would-remove-orphan": return removeOrphan(action);
|
|
182
|
+
case "would-unregister-cache": return unregisterCache(action);
|
|
183
|
+
default: return kept("automatic maintenance does not stop devices or dev servers yet");
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function diskRecovered(budget) {
|
|
187
|
+
const floor = Math.max(budget.minFreeDiskMb, budget.hardFloorDiskMb);
|
|
188
|
+
return measureDisk().every((disk) => disk.freeMb >= floor);
|
|
189
|
+
}
|
|
190
|
+
//#endregion
|
|
14
191
|
//#region src/maintenance/log.ts
|
|
15
192
|
function maintenanceLogger(settings, pass, trigger) {
|
|
16
193
|
const file = maintenanceNdjsonFile();
|
|
@@ -33,7 +210,7 @@ function maintenanceLogger(settings, pass, trigger) {
|
|
|
33
210
|
event,
|
|
34
211
|
pass,
|
|
35
212
|
trigger,
|
|
36
|
-
mode: "report"
|
|
213
|
+
mode: settings.mode === "on" ? "on" : "report"
|
|
37
214
|
};
|
|
38
215
|
if (!writer.write(record)) throw writer.lastError ?? /* @__PURE__ */ new Error(`Could not append ${file}`);
|
|
39
216
|
if (typeof fields.workspace === "string" && [
|
|
@@ -41,7 +218,7 @@ function maintenanceLogger(settings, pass, trigger) {
|
|
|
41
218
|
"maintenance_failure",
|
|
42
219
|
"maintenance_skip"
|
|
43
220
|
].includes(event)) {
|
|
44
|
-
const workspace = createNdjsonWriter(join(workspaceLogsDir(fields.workspace),
|
|
221
|
+
const workspace = createNdjsonWriter(join(workspaceLogsDir(fields.workspace), MAINTENANCE_LOG_NAME), { maxBytes: LOG_ROTATE_BYTES });
|
|
45
222
|
try {
|
|
46
223
|
if (!workspace.write(record)) throw workspace.lastError ?? /* @__PURE__ */ new Error("Could not append workspace maintenance log");
|
|
47
224
|
} finally {
|
|
@@ -70,6 +247,37 @@ function writeState(state) {
|
|
|
70
247
|
}
|
|
71
248
|
});
|
|
72
249
|
}
|
|
250
|
+
const EXECUTION_ORDER = {
|
|
251
|
+
"would-unregister-cache": 0,
|
|
252
|
+
"would-remove-orphan": 0,
|
|
253
|
+
"would-clear-outputs": 1,
|
|
254
|
+
"would-trim-cache": 2,
|
|
255
|
+
"would-empty-cache": 3,
|
|
256
|
+
"would-remove-worktree": 4
|
|
257
|
+
};
|
|
258
|
+
function shrinkSizes(state, action, outcome) {
|
|
259
|
+
state.sizes = state.sizes.flatMap((size) => {
|
|
260
|
+
if (action.kind === "would-clear-outputs" && size.category === "workspace-outputs" && size.workspace === action.workspace) return [];
|
|
261
|
+
if (action.kind === "would-trim-cache" && action.dir && size.dir === action.dir) return [{
|
|
262
|
+
...size,
|
|
263
|
+
bytes: Math.max(0, size.bytes - outcome.bytes)
|
|
264
|
+
}];
|
|
265
|
+
if (action.kind === "would-empty-cache" && size.dir === action.target) return [];
|
|
266
|
+
return [size];
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
const executedKind = (action) => action.kind.replace(/^would-/, "");
|
|
270
|
+
function describeDone(action) {
|
|
271
|
+
const name = basename(action.workspace ?? action.target);
|
|
272
|
+
switch (action.kind) {
|
|
273
|
+
case "would-clear-outputs": return `Cleared build outputs of ${name}`;
|
|
274
|
+
case "would-trim-cache": return `Trimmed ${action.target}`;
|
|
275
|
+
case "would-empty-cache": return `Emptied ${action.target}`;
|
|
276
|
+
case "would-remove-worktree": return `Removed the worktree ${action.target}`;
|
|
277
|
+
case "would-remove-orphan": return `Removed the orphaned workspace directory ${action.target}`;
|
|
278
|
+
default: return `Unregistered the stale cache ${action.target}`;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
73
281
|
const actionKey = (action) => JSON.stringify([action.kind, action.target]);
|
|
74
282
|
const stableText = (text) => text.replace(/\d+(?:\.\d+)?/g, "#");
|
|
75
283
|
const sameSet = (before, after) => before.size === after.size && [...before].every((key) => after.has(key));
|
|
@@ -113,10 +321,11 @@ async function runMaintenance(trigger) {
|
|
|
113
321
|
const failures = [];
|
|
114
322
|
let measuredSizes = false;
|
|
115
323
|
const budget = resolveBudget().budget;
|
|
324
|
+
const ran = /* @__PURE__ */ new Set();
|
|
116
325
|
for (const check of checks) {
|
|
117
|
-
if (check
|
|
118
|
-
record("maintenance_skip", "debug",
|
|
119
|
-
target:
|
|
326
|
+
if (check !== "pressure" && sizeScanDeferred(settings)) {
|
|
327
|
+
record("maintenance_skip", "debug", `${check} check deferred: host load exceeds maintenance.maxLoadPerCore`, {
|
|
328
|
+
target: check,
|
|
120
329
|
reason: "host load exceeds maintenance.maxLoadPerCore"
|
|
121
330
|
});
|
|
122
331
|
state.deferredAt = {
|
|
@@ -127,9 +336,11 @@ async function runMaintenance(trigger) {
|
|
|
127
336
|
continue;
|
|
128
337
|
}
|
|
129
338
|
state.lastAt[check] = Date.now();
|
|
339
|
+
ran.add(check);
|
|
130
340
|
delete state.deferredAt?.[check];
|
|
131
341
|
writeState(state);
|
|
132
342
|
try {
|
|
343
|
+
if (check === "worktree" || check === "sweep") continue;
|
|
133
344
|
if (check === "pressure") {
|
|
134
345
|
state.pressure = measurePressure(settings, state.pressure, state.lastAt[check]);
|
|
135
346
|
const floor = Math.max(budget.minFreeDiskMb, budget.hardFloorDiskMb);
|
|
@@ -175,7 +386,10 @@ async function runMaintenance(trigger) {
|
|
|
175
386
|
}
|
|
176
387
|
let result;
|
|
177
388
|
try {
|
|
178
|
-
result = await plannedMaintenance(state.pressure, state.sizes, settings
|
|
389
|
+
result = await plannedMaintenance(state.pressure, state.sizes, settings, {
|
|
390
|
+
sweep: ran.has("sweep"),
|
|
391
|
+
worktrees: ran.has("worktree")
|
|
392
|
+
});
|
|
179
393
|
} catch (error) {
|
|
180
394
|
const message = error instanceof Error ? error.message : String(error);
|
|
181
395
|
failures.push(message);
|
|
@@ -188,42 +402,130 @@ async function runMaintenance(trigger) {
|
|
|
188
402
|
blocked: [],
|
|
189
403
|
skips: []
|
|
190
404
|
};
|
|
405
|
+
for (const check of ["sweep", "worktree"]) if (ran.has(check)) delete state.lastAt[check];
|
|
406
|
+
}
|
|
407
|
+
async function executePlan(actions) {
|
|
408
|
+
const context = {
|
|
409
|
+
settings,
|
|
410
|
+
budget,
|
|
411
|
+
protectedRoot: triggeringRoot(),
|
|
412
|
+
worktreeSweep: result.worktreeSweep
|
|
413
|
+
};
|
|
414
|
+
const results = [];
|
|
415
|
+
const ordered = actions.toSorted((a, b) => (EXECUTION_ORDER[a.kind] ?? 9) - (EXECUTION_ORDER[b.kind] ?? 9));
|
|
416
|
+
for (const action of ordered) {
|
|
417
|
+
const began = Date.now();
|
|
418
|
+
let outcome;
|
|
419
|
+
try {
|
|
420
|
+
outcome = action.check === "disk" && diskRecovered(budget) ? {
|
|
421
|
+
status: "kept",
|
|
422
|
+
bytes: 0,
|
|
423
|
+
detail: "free disk recovered before this action"
|
|
424
|
+
} : await executeAction(action, context);
|
|
425
|
+
} catch (error) {
|
|
426
|
+
outcome = {
|
|
427
|
+
status: "failed",
|
|
428
|
+
bytes: 0,
|
|
429
|
+
detail: error instanceof Error ? error.message : String(error)
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
results.push({
|
|
433
|
+
action,
|
|
434
|
+
outcome
|
|
435
|
+
});
|
|
436
|
+
const fields = {
|
|
437
|
+
action: {
|
|
438
|
+
kind: executedKind(action),
|
|
439
|
+
target: action.target,
|
|
440
|
+
bytes: outcome.bytes,
|
|
441
|
+
durationMs: Date.now() - began
|
|
442
|
+
},
|
|
443
|
+
...action.workspace && action.kind !== "would-remove-worktree" ? { workspace: action.workspace } : {}
|
|
444
|
+
};
|
|
445
|
+
if (outcome.status === "done") record("maintenance_action", action.kind === "would-remove-worktree" ? "warn" : "info", `${describeDone(action)} (${formatBytes(outcome.bytes)}): ${action.reason}`, fields);
|
|
446
|
+
else if (outcome.status === "failed") {
|
|
447
|
+
failures.push(`${describeDone(action)} failed: ${outcome.detail}`);
|
|
448
|
+
record("maintenance_failure", "error", `Could not process ${action.target}: ${outcome.detail}`, {
|
|
449
|
+
target: action.target,
|
|
450
|
+
error: outcome.detail,
|
|
451
|
+
...fields.workspace ? { workspace: fields.workspace } : {}
|
|
452
|
+
});
|
|
453
|
+
} else if (!loggedSkips.has(action.target)) {
|
|
454
|
+
loggedSkips.add(action.target);
|
|
455
|
+
record("maintenance_skip", "info", `Kept ${action.target}: ${outcome.detail}`, {
|
|
456
|
+
target: action.target,
|
|
457
|
+
reason: outcome.detail,
|
|
458
|
+
...fields.workspace ? { workspace: fields.workspace } : {}
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
return results;
|
|
191
463
|
}
|
|
464
|
+
const retained = state.plan.filter((action) => (action.check === "sweep" || action.check === "worktree") && !ran.has(action.check));
|
|
192
465
|
const previousActions = new Set(state.plan.map(actionKey));
|
|
193
|
-
const nextActions = new Set(result.actions.map(actionKey));
|
|
194
466
|
const previousSkips = new Set(state.skipKeys ?? []);
|
|
195
467
|
const nextSkips = new Set(result.skips.map((skip) => skip.target));
|
|
196
|
-
const
|
|
197
|
-
const changed = !state.lastPass || !sameSet(previousActions, nextActions) || !sameSet(previousSkips, nextSkips) || !sameSet(new Set(state.lastPass.blocked.map(stableText)), new Set(blocked.map(stableText)));
|
|
198
|
-
for (const action of result.actions) {
|
|
199
|
-
if (previousActions.has(actionKey(action))) continue;
|
|
200
|
-
previousActions.add(actionKey(action));
|
|
201
|
-
record("maintenance_action", "info", `Would ${action.kind === "would-clear-outputs" ? `clear build outputs of ${basename(action.workspace ?? action.target)}` : `${action.kind.slice(6).replaceAll("-", " ")} ${action.target}`} (${formatBytes(action.bytes)}): ${action.reason}`, {
|
|
202
|
-
action: {
|
|
203
|
-
...action,
|
|
204
|
-
durationMs: 0
|
|
205
|
-
},
|
|
206
|
-
...action.workspace ? { workspace: action.workspace } : {}
|
|
207
|
-
});
|
|
208
|
-
}
|
|
468
|
+
const loggedSkips = new Set(previousSkips);
|
|
209
469
|
for (const skip of result.skips) {
|
|
210
|
-
if (
|
|
211
|
-
|
|
470
|
+
if (loggedSkips.has(skip.target)) continue;
|
|
471
|
+
loggedSkips.add(skip.target);
|
|
212
472
|
record("maintenance_skip", "info", `Kept ${skip.target}: ${skip.reason}`, skip);
|
|
213
473
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
474
|
+
if (settings.mode === "on") {
|
|
475
|
+
const outcomes = await executePlan(result.actions);
|
|
476
|
+
const blockedNow = [
|
|
477
|
+
...result.blocked,
|
|
478
|
+
...failures,
|
|
479
|
+
...outcomes.flatMap(({ outcome }) => outcome.shortfall ? [outcome.shortfall] : [])
|
|
480
|
+
];
|
|
481
|
+
const done = outcomes.filter(({ outcome }) => outcome.status === "done");
|
|
482
|
+
const freedBytes = done.reduce((sum, { outcome }) => sum + outcome.bytes, 0);
|
|
483
|
+
for (const { action, outcome } of done) shrinkSizes(state, action, outcome);
|
|
484
|
+
state.plan = outcomes.filter(({ outcome }) => outcome.status !== "done").map(({ action }) => action);
|
|
485
|
+
state.skipKeys = [...nextSkips, ...outcomes.filter(({ outcome }) => outcome.status === "kept").map(({ action }) => action.target)];
|
|
486
|
+
const previousBlocked = state.lastPass?.blocked;
|
|
487
|
+
state.lastPass = {
|
|
488
|
+
startedAt,
|
|
489
|
+
durationMs: Date.now() - startedAt,
|
|
490
|
+
trigger,
|
|
491
|
+
mode: "on",
|
|
492
|
+
freedBytes,
|
|
493
|
+
actions: done.length,
|
|
494
|
+
stopped: 0,
|
|
495
|
+
blocked: blockedNow
|
|
496
|
+
};
|
|
497
|
+
const blockedChanged = !sameSet(new Set((previousBlocked ?? []).map(stableText)), new Set(state.lastPass.blocked.map(stableText)));
|
|
498
|
+
if (done.length || measuredSizes || blockedChanged) record("maintenance_pass", state.lastPass.blocked.length ? "warn" : "info", `Pass: ${done.length} actions, ${formatBytes(freedBytes)} freed${state.lastPass.blocked.length ? `; ${state.lastPass.blocked.join("; ")}` : ""}`, { ...state.lastPass });
|
|
499
|
+
} else {
|
|
500
|
+
const blocked = [...result.blocked, ...failures];
|
|
501
|
+
const planned = [...result.actions, ...retained];
|
|
502
|
+
const nextActions = new Set(planned.map(actionKey));
|
|
503
|
+
const changed = !state.lastPass || !sameSet(previousActions, nextActions) || !sameSet(previousSkips, nextSkips) || !sameSet(new Set(state.lastPass.blocked.map(stableText)), new Set(blocked.map(stableText)));
|
|
504
|
+
for (const action of result.actions) {
|
|
505
|
+
if (previousActions.has(actionKey(action))) continue;
|
|
506
|
+
previousActions.add(actionKey(action));
|
|
507
|
+
record("maintenance_action", "info", `Would ${action.kind === "would-clear-outputs" ? `clear build outputs of ${basename(action.workspace ?? action.target)}` : `${action.kind.slice(6).replaceAll("-", " ")} ${action.target}`} (${formatBytes(action.bytes)}): ${action.reason}`, {
|
|
508
|
+
action: {
|
|
509
|
+
...action,
|
|
510
|
+
durationMs: 0
|
|
511
|
+
},
|
|
512
|
+
...action.workspace ? { workspace: action.workspace } : {}
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
state.plan = planned;
|
|
516
|
+
state.skipKeys = [...nextSkips];
|
|
517
|
+
state.lastPass = {
|
|
518
|
+
startedAt,
|
|
519
|
+
durationMs: Date.now() - startedAt,
|
|
520
|
+
trigger,
|
|
521
|
+
mode: "report",
|
|
522
|
+
freedBytes: 0,
|
|
523
|
+
actions: planned.length,
|
|
524
|
+
stopped: 0,
|
|
525
|
+
blocked
|
|
526
|
+
};
|
|
527
|
+
if (measuredSizes || changed) record("maintenance_pass", state.lastPass.blocked.length ? "warn" : "info", `Report-only pass: ${planned.length} planned actions${state.lastPass.blocked.length ? `; ${state.lastPass.blocked.join("; ")}` : ""}`, { ...state.lastPass });
|
|
528
|
+
}
|
|
227
529
|
writeState(state);
|
|
228
530
|
} catch (error) {
|
|
229
531
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { t as getExecutor } from "./exec-i8GDPwq9.mjs";
|
|
2
|
+
import { l as phaseLine } from "./command-output-Bt8ygHE7.mjs";
|
|
3
|
+
import { _ as loadConfig } from "./config-DC5jEtd1.mjs";
|
|
4
|
+
import { f as sharedCcache, x as workspaceLogsDir } from "./workspace-state-CR9X86xJ.mjs";
|
|
5
|
+
import { r as register } from "./cache-manifest-DiwKSyv9.mjs";
|
|
6
|
+
import { o as machineNumber } from "./budget-Dz-kyXLv.mjs";
|
|
7
|
+
import { readFileSync, realpathSync } from "node:fs";
|
|
8
|
+
import { isAbsolute, join } from "node:path";
|
|
9
|
+
import chalk from "chalk";
|
|
10
|
+
import { createHash } from "node:crypto";
|
|
11
|
+
const CCACHE_SLOPPINESS = "pch_defines,time_macros";
|
|
12
|
+
const STATS_LOG_FILE = "ccache-stats.log";
|
|
13
|
+
const CCACHE_UNAVAILABLE = {
|
|
14
|
+
status: "unavailable",
|
|
15
|
+
hits: null,
|
|
16
|
+
misses: null,
|
|
17
|
+
hitRatePercent: null
|
|
18
|
+
};
|
|
19
|
+
const CCACHE_NOT_RUN = {
|
|
20
|
+
status: "not-run",
|
|
21
|
+
hits: null,
|
|
22
|
+
misses: null,
|
|
23
|
+
hitRatePercent: null
|
|
24
|
+
};
|
|
25
|
+
function ccacheStatsLog(root) {
|
|
26
|
+
return join(workspaceLogsDir(root), STATS_LOG_FILE);
|
|
27
|
+
}
|
|
28
|
+
function ccacheMaxSize(config = loadConfig(), env = process.env) {
|
|
29
|
+
const { value, error } = machineNumber("caches.ccacheMaxGb", config, env);
|
|
30
|
+
return error || value === void 0 ? "5G" : `${value}G`;
|
|
31
|
+
}
|
|
32
|
+
function ccacheEnvironment({ binary, dir, workspaceRoot, statsLog, maxSize = "5G" }) {
|
|
33
|
+
return {
|
|
34
|
+
CMAKE_C_COMPILER_LAUNCHER: binary,
|
|
35
|
+
CMAKE_CXX_COMPILER_LAUNCHER: binary,
|
|
36
|
+
CCACHE_DIR: dir,
|
|
37
|
+
CCACHE_BASEDIR: workspaceRoot,
|
|
38
|
+
CCACHE_NOHASHDIR: "true",
|
|
39
|
+
CCACHE_SLOPPINESS,
|
|
40
|
+
CCACHE_MAXSIZE: maxSize,
|
|
41
|
+
CCACHE_STATSLOG: statsLog
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function parseCcacheBinary(output) {
|
|
45
|
+
const first = String(output ?? "").split("\n")[0]?.trim();
|
|
46
|
+
if (!first || !isAbsolute(first)) return null;
|
|
47
|
+
return first;
|
|
48
|
+
}
|
|
49
|
+
const CMAKE_LAUNCHER = /CMAKE_(C|CXX)_COMPILER_LAUNCHER\b/;
|
|
50
|
+
function projectCmakeLauncher(source) {
|
|
51
|
+
if (typeof source !== "string") return null;
|
|
52
|
+
const match = CMAKE_LAUNCHER.exec(source);
|
|
53
|
+
return match ? `CMAKE_${match[1]}_COMPILER_LAUNCHER` : null;
|
|
54
|
+
}
|
|
55
|
+
const HIT_COUNTERS = /* @__PURE__ */ new Set(["direct_cache_hit", "preprocessed_cache_hit"]);
|
|
56
|
+
function parseCcacheStatsLog(text) {
|
|
57
|
+
if (typeof text !== "string") return null;
|
|
58
|
+
let hits = 0;
|
|
59
|
+
let misses = 0;
|
|
60
|
+
for (const raw of text.split("\n")) {
|
|
61
|
+
const line = raw.trim();
|
|
62
|
+
if (HIT_COUNTERS.has(line)) hits += 1;
|
|
63
|
+
else if (line === "cache_miss") misses += 1;
|
|
64
|
+
}
|
|
65
|
+
const total = hits + misses;
|
|
66
|
+
if (total === 0) return null;
|
|
67
|
+
return {
|
|
68
|
+
status: "reported",
|
|
69
|
+
hits,
|
|
70
|
+
misses,
|
|
71
|
+
hitRatePercent: Math.round(hits / total * 1e3) / 10
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function readCcacheActivity(statsLog) {
|
|
75
|
+
let text;
|
|
76
|
+
try {
|
|
77
|
+
text = readFileSync(statsLog, "utf-8");
|
|
78
|
+
} catch {
|
|
79
|
+
return CCACHE_UNAVAILABLE;
|
|
80
|
+
}
|
|
81
|
+
return parseCcacheStatsLog(text) ?? CCACHE_UNAVAILABLE;
|
|
82
|
+
}
|
|
83
|
+
function ccacheActivityLine(activity) {
|
|
84
|
+
if (activity.status === "reported") return `${activity.hits} hits / ${activity.misses} misses (${activity.hitRatePercent}%)`;
|
|
85
|
+
if (activity.status === "not-run") return "not run; artifact cache supplied the app";
|
|
86
|
+
return "unavailable; no C++ compile went through ccache";
|
|
87
|
+
}
|
|
88
|
+
function lookupCcache() {
|
|
89
|
+
try {
|
|
90
|
+
return parseCcacheBinary(getExecutor().findExecutable("ccache"));
|
|
91
|
+
} catch {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function readOrNull(file) {
|
|
96
|
+
try {
|
|
97
|
+
return readFileSync(file, "utf-8");
|
|
98
|
+
} catch {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function canonical(root) {
|
|
103
|
+
try {
|
|
104
|
+
return realpathSync(root);
|
|
105
|
+
} catch {
|
|
106
|
+
return root;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function resolveCcache({ root, dir = sharedCcache(), lookup = lookupCcache, onNote = (line) => console.error(line) }) {
|
|
110
|
+
const binary = lookup();
|
|
111
|
+
if (!binary) {
|
|
112
|
+
onNote(chalk.dim(phaseLine("cache", "ccache off (not installed; brew install ccache)")));
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
const declared = projectCmakeLauncher(readOrNull(join(root, "android", "app", "build.gradle")));
|
|
116
|
+
if (declared) {
|
|
117
|
+
onNote(chalk.dim(phaseLine("cache", `ccache off (android/app/build.gradle sets ${declared} itself)`)));
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
const statsLog = ccacheStatsLog(root);
|
|
121
|
+
register({
|
|
122
|
+
dir,
|
|
123
|
+
name: "ccache",
|
|
124
|
+
prune: "atomic",
|
|
125
|
+
note: "ccache keeps itself under CCACHE_MAXSIZE, so it is emptied whole or not at all"
|
|
126
|
+
});
|
|
127
|
+
onNote(chalk.dim(phaseLine("cache", `ccache on (${dir})`)));
|
|
128
|
+
return {
|
|
129
|
+
dir,
|
|
130
|
+
statsLog,
|
|
131
|
+
env: ccacheEnvironment({
|
|
132
|
+
binary,
|
|
133
|
+
dir,
|
|
134
|
+
workspaceRoot: canonical(root),
|
|
135
|
+
statsLog,
|
|
136
|
+
maxSize: ccacheMaxSize()
|
|
137
|
+
})
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
//#endregion
|
|
141
|
+
//#region src/offload/manifest.ts
|
|
142
|
+
function manifestDigest(entries) {
|
|
143
|
+
const hash = createHash("sha256");
|
|
144
|
+
for (const entry of entries.toSorted((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0)) hash.update(`${entry.path}\0${entry.kind}\0${entry.sha256}\n`);
|
|
145
|
+
return hash.digest("hex");
|
|
146
|
+
}
|
|
147
|
+
//#endregion
|
|
148
|
+
export { projectCmakeLauncher as a, ccacheActivityLine as i, CCACHE_NOT_RUN as n, readCcacheActivity as o, CCACHE_UNAVAILABLE as r, resolveCcache as s, manifestDigest as t };
|