stim 1.3.0 → 1.3.1
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 +4 -3
- package/dist/{android-CqcW1hrP.mjs → android-CT-PkXUh.mjs} +22 -19
- package/dist/{android-cas-BR5YJBYv.mjs → android-cas-DXorPCi_.mjs} +2 -2
- package/dist/{android-BqVIJoG7.mjs → android-hMj83VpN.mjs} +12 -6
- package/dist/{build-slots-C3-R4XQr.mjs → build-slots-C4AouyNz.mjs} +1 -1
- package/dist/cli.mjs +12 -12
- package/dist/{deps-Spz8ozgo.mjs → deps-0Yt0Inly.mjs} +1 -1
- package/dist/{dev-client-BjisHDB5.mjs → dev-client-VfSZNLT4.mjs} +4 -4
- package/dist/{device-BkiW-gkk.mjs → device-Bbv45Z9Z.mjs} +3 -3
- package/dist/{device-lease-BJYybF-2.mjs → device-lease-CJwK7OZr.mjs} +1 -1
- package/dist/{device-pool-CPbLGfrB.mjs → device-pool-CcBNTvM7.mjs} +1 -1
- package/dist/{device-remote-AkYCKS8U.mjs → device-remote-DAA0Zm6Z.mjs} +1 -1
- package/dist/{doctor-Bmv86UUW.mjs → doctor-B4EITgQB.mjs} +5 -5
- package/dist/{doctor-fAMXC4vl.mjs → doctor-BueDfLYG.mjs} +2 -2
- package/dist/{error-diagnostics-DHC31rXA.mjs → error-diagnostics-DwhNBFDU.mjs} +3 -3
- package/dist/{gc-GGu-8ULN.mjs → gc-BRu62v4o.mjs} +7 -7
- package/dist/{guide-92CPA21s.mjs → guide-D95E0u3i.mjs} +111 -61
- package/dist/{ios-BSXFeO5H.mjs → ios-DAju3TVy.mjs} +12 -12
- package/dist/{logs-DPNF4C9s.mjs → logs-CAdwW6c2.mjs} +29 -4
- package/dist/{logs-query-R-Cqc6Qv.mjs → logs-query-Cdp5aaCH.mjs} +1 -1
- package/dist/{ownership-BpCKiRlQ.mjs → ownership-DXcol4oU.mjs} +1 -1
- package/dist/{ownership-claim-Bf2CAfw3.mjs → ownership-claim-BtPtJVmq.mjs} +35 -21
- package/dist/{reclaim-iW9Cad7k.mjs → reclaim-CUmwucWG.mjs} +4 -4
- package/dist/{reload-DazHDSFA.mjs → reload-CWpXoOgN.mjs} +2 -2
- package/dist/{slot-launch-cci0xvmC.mjs → slot-launch-CZklomj6.mjs} +1 -1
- package/dist/{start-mObjQH2W.mjs → start-B5G4NjiA.mjs} +3 -3
- package/dist/{status-BxQNqu9t.mjs → status-CI9clRSb.mjs} +6 -6
- package/dist/{stop-CrHGcjrV.mjs → stop-CQhffpmX.mjs} +2 -2
- package/dist/{stop-DimGXEKY.mjs → stop-CjlgDk0a.mjs} +5 -5
- package/dist/{teardown-ClFp1Aax.mjs → teardown--gESs9ck.mjs} +103 -4
- package/dist/{workspace-process-lock-3NcQnZ0X.mjs → workspace-process-lock-IBj1Towr.mjs} +1 -1
- package/dist/{worktree-DxTgN1CJ.mjs → worktree-DtQGO9-s.mjs} +116 -106
- package/dist/{xcode-D7sAp-Zy.mjs → xcode-xnxyvJkD.mjs} +81 -44
- package/package.json +5 -5
|
@@ -5,13 +5,13 @@ import { f as listWorktrees } from "./worktree-Eq6TLH8N.mjs";
|
|
|
5
5
|
import { a as findProjectRoot, c as projectShortcut } from "./project-fKmuIaMI.mjs";
|
|
6
6
|
import { a as isMetroRunning, i as resolveProjectMetro } from "./metro-CORqXyTL.mjs";
|
|
7
7
|
import { a as formatElapsed, r as clockTime, u as plural } from "./command-output-8wHMEde0.mjs";
|
|
8
|
-
import { N as resolveOwnedAvdSerial } from "./android-
|
|
9
|
-
import { a as POOL_SETTING_REMEDY, c as parkedMaxSetting, l as readParked } from "./teardown
|
|
8
|
+
import { N as resolveOwnedAvdSerial } from "./android-hMj83VpN.mjs";
|
|
9
|
+
import { a as POOL_SETTING_REMEDY, c as parkedMaxSetting, l as readParked } from "./teardown--gESs9ck.mjs";
|
|
10
10
|
import { u as listAllIosSims } from "./ios-Deh5js5V.mjs";
|
|
11
|
-
import { i as listLeaseFiles } from "./device-lease-
|
|
12
|
-
import {
|
|
13
|
-
import { t as resolveSupervisorTarget } from "./ownership-
|
|
14
|
-
import { i as readSupervisorState } from "./stop-
|
|
11
|
+
import { i as listLeaseFiles } from "./device-lease-CJwK7OZr.mjs";
|
|
12
|
+
import { c as queryLogs } from "./logs-query-Cdp5aaCH.mjs";
|
|
13
|
+
import { t as resolveSupervisorTarget } from "./ownership-DXcol4oU.mjs";
|
|
14
|
+
import { i as readSupervisorState } from "./stop-CjlgDk0a.mjs";
|
|
15
15
|
import { existsSync } from "fs";
|
|
16
16
|
import { totalmem } from "os";
|
|
17
17
|
import chalk from "chalk";
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { t as resolveSupervisorTarget } from "./ownership-
|
|
2
|
-
import { a as resolveCollectorTargets, i as readSupervisorState, n as clearSupervisorState, o as runStop, r as readCollectorState, s as stopCommand, t as clearCollectorState } from "./stop-
|
|
1
|
+
import { t as resolveSupervisorTarget } from "./ownership-DXcol4oU.mjs";
|
|
2
|
+
import { a as resolveCollectorTargets, i as readSupervisorState, n as clearSupervisorState, o as runStop, r as readCollectorState, s as stopCommand, t as clearCollectorState } from "./stop-CjlgDk0a.mjs";
|
|
3
3
|
export { clearCollectorState, clearSupervisorState, stopCommand as default, readCollectorState, readSupervisorState, resolveCollectorTargets, resolveSupervisorTarget, runStop };
|
|
@@ -4,11 +4,11 @@ import { a as findProjectRoot } from "./project-fKmuIaMI.mjs";
|
|
|
4
4
|
import { i as resolveProjectMetro, n as killMetroTree, r as pidExists } from "./metro-CORqXyTL.mjs";
|
|
5
5
|
import { n as verifyCollectorOwnership } from "./ownership-8O073GIC.mjs";
|
|
6
6
|
import { f as releasedLeaseFact, l as phaseLine, u as plural } from "./command-output-8wHMEde0.mjs";
|
|
7
|
-
import { n as teardownOwnedIosSim, t as teardownOwnedAvd, x as resolveEasCliBin } from "./teardown
|
|
8
|
-
import { t as withWorkspaceProcessLock } from "./workspace-process-lock-
|
|
9
|
-
import { l as releaseWorkspaceLeases } from "./device-lease-
|
|
10
|
-
import { i as endRecordedSession } from "./device-remote-
|
|
11
|
-
import { r as stopTunnel, t as resolveSupervisorTarget } from "./ownership-
|
|
7
|
+
import { n as teardownOwnedIosSim, t as teardownOwnedAvd, x as resolveEasCliBin } from "./teardown--gESs9ck.mjs";
|
|
8
|
+
import { t as withWorkspaceProcessLock } from "./workspace-process-lock-IBj1Towr.mjs";
|
|
9
|
+
import { l as releaseWorkspaceLeases } from "./device-lease-CJwK7OZr.mjs";
|
|
10
|
+
import { i as endRecordedSession } from "./device-remote-DAA0Zm6Z.mjs";
|
|
11
|
+
import { r as stopTunnel, t as resolveSupervisorTarget } from "./ownership-DXcol4oU.mjs";
|
|
12
12
|
import chalk from "chalk";
|
|
13
13
|
//#region src/commands/stop.ts
|
|
14
14
|
const DEFAULT_WAIT_MS = 1e4;
|
|
@@ -4,7 +4,8 @@ import { r as createNdjsonWriter } from "./ndjson-BZwI1NBv.mjs";
|
|
|
4
4
|
import { D as workspaceLogsDir } from "./state-B3XA2wp2.mjs";
|
|
5
5
|
import { l as resolvePackageJson } from "./project-fKmuIaMI.mjs";
|
|
6
6
|
import { n as stripAnsi } from "./process-output-C7IlIzd7.mjs";
|
|
7
|
-
import {
|
|
7
|
+
import { l as phaseLine } from "./command-output-8wHMEde0.mjs";
|
|
8
|
+
import { E as ownedAvdMatchesConfiguration, F as shutdownAndroidEmulator, I as waitForAndroidEmulatorShutdown, N as resolveOwnedAvdSerial, O as ownedAvdSystemImage, S as listOrphanedAvdDirectories, T as ownedAvdDirectory, l as avdStorageRoots, o as assertOwnedAvdStopped, p as deleteAvd, s as avdPathExists } from "./android-hMj83VpN.mjs";
|
|
8
9
|
import { n as expoBinFromPackage, r as expoBinPath, s as findBinUpward } from "./server-expo-DizT4OMk.mjs";
|
|
9
10
|
import { C as shutdownIosSim, _ as renameIosSim, a as deleteIosSim, d as listIosDeviceTypes, g as parseRuntimeVersion, h as parkedSimName, o as deleteParkedIosSim, r as clearAppDataContainer, s as findAppDataContainer, x as resolveOwnedIosSim } from "./ios-Deh5js5V.mjs";
|
|
10
11
|
import { createRequire } from "node:module";
|
|
@@ -720,6 +721,77 @@ function dropParked(platform, udid) {
|
|
|
720
721
|
return removeParkedAfter(platform, udid, () => {}) !== null;
|
|
721
722
|
}
|
|
722
723
|
//#endregion
|
|
724
|
+
//#region src/agent-device-cleanup.ts
|
|
725
|
+
function parseAgentDeviceSessions(output) {
|
|
726
|
+
const result = JSON.parse(output);
|
|
727
|
+
if (result?.success !== true || !Array.isArray(result.data?.sessions)) throw new Error("agent-device did not return a successful session list");
|
|
728
|
+
return result.data.sessions.flatMap((entry) => {
|
|
729
|
+
if (!entry || typeof entry !== "object") return [];
|
|
730
|
+
const row = entry;
|
|
731
|
+
const id = row.platform === "ios" ? row.device_udid : row.id;
|
|
732
|
+
if (row.platform !== "ios" && row.platform !== "android" || typeof row.name !== "string" || !row.name.trim() || typeof id !== "string" || !id || typeof row.createdAt !== "number" || !Number.isFinite(row.createdAt) || row.platform === "ios" && row.id !== void 0 && row.id !== id) return [];
|
|
733
|
+
return [{
|
|
734
|
+
name: row.name,
|
|
735
|
+
platform: row.platform,
|
|
736
|
+
id,
|
|
737
|
+
createdAt: row.createdAt
|
|
738
|
+
}];
|
|
739
|
+
});
|
|
740
|
+
}
|
|
741
|
+
function report(message) {
|
|
742
|
+
process.stderr.write(`${phaseLine("device", message)}\n`);
|
|
743
|
+
}
|
|
744
|
+
function closeOwnedDeviceSessions(device, stillOwned) {
|
|
745
|
+
const exec = getExecutor();
|
|
746
|
+
const deadline = Date.now() + 15e3;
|
|
747
|
+
const run = (args) => {
|
|
748
|
+
const remaining = deadline - Date.now();
|
|
749
|
+
if (remaining <= 0) throw new Error("agent-device cleanup timed out");
|
|
750
|
+
return exec.runFile("agent-device", [
|
|
751
|
+
...args,
|
|
752
|
+
"--json",
|
|
753
|
+
"--daemon-transport",
|
|
754
|
+
"socket"
|
|
755
|
+
], {
|
|
756
|
+
timeoutMs: Math.min(5e3, remaining),
|
|
757
|
+
killSignal: "SIGKILL"
|
|
758
|
+
});
|
|
759
|
+
};
|
|
760
|
+
const list = () => parseAgentDeviceSessions(run([
|
|
761
|
+
"session",
|
|
762
|
+
"list",
|
|
763
|
+
"--session",
|
|
764
|
+
"stim-teardown-inventory"
|
|
765
|
+
]));
|
|
766
|
+
try {
|
|
767
|
+
if (!exec.runQuiet("command -v agent-device", {
|
|
768
|
+
timeoutMs: 2e3,
|
|
769
|
+
killSignal: "SIGKILL"
|
|
770
|
+
})) return;
|
|
771
|
+
const sessions = list().filter((session) => session.platform === device.platform && session.id === device.id);
|
|
772
|
+
for (const session of sessions) try {
|
|
773
|
+
if (!list().some((current) => current.name === session.name && current.platform === session.platform && current.id === session.id && current.createdAt === session.createdAt)) continue;
|
|
774
|
+
if (!stillOwned()) return;
|
|
775
|
+
if (JSON.parse(run([
|
|
776
|
+
"close",
|
|
777
|
+
"--session",
|
|
778
|
+
session.name,
|
|
779
|
+
"--session-lock",
|
|
780
|
+
"reject",
|
|
781
|
+
"--platform",
|
|
782
|
+
device.platform,
|
|
783
|
+
device.platform === "ios" ? "--udid" : "--serial",
|
|
784
|
+
device.id
|
|
785
|
+
]))?.success !== true) throw new Error("agent-device did not confirm session close");
|
|
786
|
+
report(`closed agent-device session ${session.name} on ${device.id}`);
|
|
787
|
+
} catch (error) {
|
|
788
|
+
report(`could not close agent-device session ${session.name} on ${device.id}: ${error.message}`);
|
|
789
|
+
}
|
|
790
|
+
} catch (error) {
|
|
791
|
+
report(`could not list agent-device sessions for ${device.id}: ${error.message}`);
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
//#endregion
|
|
723
795
|
//#region src/teardown.ts
|
|
724
796
|
function teardownParkedAvd(avdName) {
|
|
725
797
|
try {
|
|
@@ -746,7 +818,13 @@ function teardownParkedAvd(avdName) {
|
|
|
746
818
|
}
|
|
747
819
|
function teardownParkedIosSim(udid, { label, deleteSim = deleteParkedIosSim } = {}) {
|
|
748
820
|
try {
|
|
749
|
-
const removed = removeParkedAfter("ios", udid, () =>
|
|
821
|
+
const removed = removeParkedAfter("ios", udid, () => {
|
|
822
|
+
closeOwnedDeviceSessions({
|
|
823
|
+
platform: "ios",
|
|
824
|
+
id: udid
|
|
825
|
+
}, () => Boolean(resolveOwnedIosSim(udid).sim));
|
|
826
|
+
deleteSim(udid);
|
|
827
|
+
});
|
|
750
828
|
if (!removed) return {
|
|
751
829
|
status: "skipped",
|
|
752
830
|
kind: "not-parked",
|
|
@@ -812,6 +890,17 @@ function teardownOwnedIosSim(udid, { del = false, label, park } = {}) {
|
|
|
812
890
|
reason: `sim is now named "${resolved.notOwned}", not Stim-owned by name`
|
|
813
891
|
};
|
|
814
892
|
if (resolved.missing) return { status: "missing" };
|
|
893
|
+
closeOwnedDeviceSessions({
|
|
894
|
+
platform: "ios",
|
|
895
|
+
id: udid
|
|
896
|
+
}, () => Boolean(resolveOwnedIosSim(udid).sim));
|
|
897
|
+
const current = resolveOwnedIosSim(udid);
|
|
898
|
+
if (current.missing) return { status: "missing" };
|
|
899
|
+
if (current.notOwned) return {
|
|
900
|
+
status: "skipped",
|
|
901
|
+
kind: "not-owned",
|
|
902
|
+
reason: "simulator ownership changed before shutdown"
|
|
903
|
+
};
|
|
815
904
|
shutdownIosSim(udid);
|
|
816
905
|
const sim = resolved.sim;
|
|
817
906
|
if (del && park && park.max > 0) try {
|
|
@@ -916,8 +1005,18 @@ function teardownOwnedAvd(avdName, { del = false, park, owner, orphanedDirectory
|
|
|
916
1005
|
reason: "AVD registration appeared; its data was kept."
|
|
917
1006
|
};
|
|
918
1007
|
const serial = resolved.serial;
|
|
919
|
-
if (serial)
|
|
920
|
-
|
|
1008
|
+
if (serial) {
|
|
1009
|
+
const stillOwned = () => {
|
|
1010
|
+
const current = resolveAvd(avdName);
|
|
1011
|
+
return !current.notOwned && !current.missing && current.serial === serial;
|
|
1012
|
+
};
|
|
1013
|
+
closeOwnedDeviceSessions({
|
|
1014
|
+
platform: "android",
|
|
1015
|
+
id: serial
|
|
1016
|
+
}, stillOwned);
|
|
1017
|
+
if (!stillOwned()) throw new Error(`Owned AVD ${avdName} changed before shutdown; it was kept.`);
|
|
1018
|
+
waitForShutdown(avdName, (timeoutMs) => shutdownAndroidEmulator(serial, timeoutMs));
|
|
1019
|
+
} else assertStopped(avdName);
|
|
921
1020
|
if (del) {
|
|
922
1021
|
const current = resolveAvd(avdName);
|
|
923
1022
|
if (current.notOwned) return {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { d as releaseClaim, m as tryAcquireClaim } from "./ownership-claim-BtPtJVmq.mjs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
//#region src/engine/workspace-process-lock.ts
|
|
4
4
|
const DEFAULT_WAIT_MS = 6e4;
|
|
@@ -4,14 +4,14 @@ import { c as readMetroTunnel, u as readRemoteSession } from "./state-B3XA2wp2.m
|
|
|
4
4
|
import { _ as resolveFullRef, b as warmWorktreePaths, c as hasRemote, d as isPodInstallChurn, f as listWorktrees, h as removeWorktree, i as depsOutOfSync, l as hasUncommittedWork, m as readWorktreeExclude, n as cloneIgnoredEntries, o as dirtyPaths, p as podsOutOfSync, r as deleteBranch, s as gitCommonDir, t as branchExists, u as isMainWorkingTree, v as restoreFile, y as unpushedCommits } from "./worktree-Eq6TLH8N.mjs";
|
|
5
5
|
import { a as findProjectRoot } from "./project-fKmuIaMI.mjs";
|
|
6
6
|
import { a as formatElapsed, f as releasedLeaseFact, i as formatDuration, l as phaseLine, m as shortUdid, u as plural } from "./command-output-8wHMEde0.mjs";
|
|
7
|
-
import { a as clearClaimChild, c as
|
|
7
|
+
import { a as clearClaimChild, c as markClaimChildPending, d as releaseClaim, f as setClaimChild, i as claimRemoveCommand, l as processGroupAlive, m as tryAcquireClaim, n as ClaimUnavailableError, p as settleClaim, r as claimFailure } from "./ownership-claim-BtPtJVmq.mjs";
|
|
8
8
|
import { A as resolveSettings, I as unknownSettingKeys, N as settingShapeErrors, a as SETTING_SHAPE_REMEDY } from "./settings-DEJf6_kJ.mjs";
|
|
9
|
-
import { a as POOL_SETTING_REMEDY, c as parkedMaxSetting } from "./teardown
|
|
10
|
-
import { g as podInstallCommand, r as HEARTBEAT_INTERVAL_MS } from "./xcode-
|
|
11
|
-
import { a as installedNpmTreeIsValid, i as hasInstalledDependencies, o as locallyKnownUpstream, t as dependencyState } from "./doctor-
|
|
12
|
-
import { c as withManagedTunnelRemovalLock, o as withManagedRemoteWorktreeRemovalLock } from "./ownership-
|
|
13
|
-
import { n as reclaimProject } from "./reclaim-
|
|
14
|
-
import { a as runPodInstall, i as runCaptured, n as podsAreStale, r as readPodState, t as DEPS_ERROR } from "./deps-
|
|
9
|
+
import { a as POOL_SETTING_REMEDY, c as parkedMaxSetting } from "./teardown--gESs9ck.mjs";
|
|
10
|
+
import { g as podInstallCommand, r as HEARTBEAT_INTERVAL_MS } from "./xcode-xnxyvJkD.mjs";
|
|
11
|
+
import { a as installedNpmTreeIsValid, i as hasInstalledDependencies, o as locallyKnownUpstream, t as dependencyState } from "./doctor-B4EITgQB.mjs";
|
|
12
|
+
import { c as withManagedTunnelRemovalLock, o as withManagedRemoteWorktreeRemovalLock } from "./ownership-DXcol4oU.mjs";
|
|
13
|
+
import { n as reclaimProject } from "./reclaim-CUmwucWG.mjs";
|
|
14
|
+
import { a as runPodInstall, i as runCaptured, n as podsAreStale, r as readPodState, t as DEPS_ERROR } from "./deps-0Yt0Inly.mjs";
|
|
15
15
|
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
16
16
|
import { createHash, randomUUID } from "node:crypto";
|
|
17
17
|
import { join, relative } from "node:path";
|
|
@@ -56,57 +56,10 @@ function asHolder(holder) {
|
|
|
56
56
|
function waitingLine(holder, elapsedMs) {
|
|
57
57
|
return phaseLine("lock", `waiting ${formatElapsed(elapsedMs)} for ${warmCommand(holder.phase)} (pid ${holder.pid}) -- stim guide lifecycle options`);
|
|
58
58
|
}
|
|
59
|
-
function warmClaimAcquiredLine(wait) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
function warmClaimUnavailableLine(reason) {
|
|
64
|
-
return phaseLine("lock", `unavailable (${reason}); copying without it`);
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* The reason a copy may proceed without the claim, or null when it must refuse. Plain `warm` worked on an
|
|
68
|
-
* unwritable STIM_HOME before any claim existed and only reads, so the states in which no claim can be
|
|
69
|
-
* recorded at all degrade to an unsynchronised copy: no process identity, a claim store the filesystem
|
|
70
|
-
* rejects, and a claim store whose own path holds a file. A claim record Stim cannot resolve and a wait
|
|
71
|
-
* that ran out are refusals: neither state can exist unless the claim does, so refusing them takes
|
|
72
|
-
* nothing away.
|
|
73
|
-
*/
|
|
74
|
-
function warmClaimDegradation(error) {
|
|
75
|
-
if (isClaimUnavailable(error)) return error.message;
|
|
76
|
-
if (isClaimRefusal(error)) return error.reason === "the claim path is a file, not a claim directory" ? `${error.claimPath}: ${error.reason}` : null;
|
|
77
|
-
const code = error?.code;
|
|
78
|
-
if (typeof code === "string" && code.startsWith("STIM_")) return null;
|
|
79
|
-
return error?.message ?? String(error);
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* What a copy that could not record a claim of its own would overlap if it went ahead anyway. Reading the
|
|
83
|
-
* claim set classifies without writing anything and without refusing, so it works in exactly the states
|
|
84
|
-
* that made the claim unrecordable -- a read-only STIM_HOME included. It is a read before a copy rather
|
|
85
|
-
* than a held claim, so a refresh that starts after it is not covered; it closes the window in which one
|
|
86
|
-
* is already installing, which is the window a refresh actually occupies.
|
|
87
|
-
*/
|
|
88
|
-
function warmClaimBlocker(repositoryRoot) {
|
|
89
|
-
const survey = readClaimSet(warmClaimPath(repositoryRoot));
|
|
90
|
-
const refresh = survey.live.find((holder) => holder.mode === "exclusive");
|
|
91
|
-
if (refresh) return {
|
|
92
|
-
kind: "refresh",
|
|
93
|
-
holder: asHolder(refresh)
|
|
94
|
-
};
|
|
95
|
-
const unresolved = survey.unresolved.find((problem) => problem.path.endsWith(".claim"));
|
|
96
|
-
if (unresolved) return {
|
|
97
|
-
kind: "unresolved",
|
|
98
|
-
path: unresolved.path,
|
|
99
|
-
reason: unresolved.reason
|
|
100
|
-
};
|
|
101
|
-
return null;
|
|
102
|
-
}
|
|
103
|
-
function warmClaimBlockedLine(reason, blocker) {
|
|
104
|
-
const what = blocker.kind === "refresh" ? `${warmCommand(blocker.holder.phase)} (pid ${blocker.holder.pid}) holds this repository` : `a claim at ${blocker.path} cannot be resolved: ${blocker.reason}`;
|
|
105
|
-
return phaseLine("lock", `unavailable (${reason}); ${what}`);
|
|
106
|
-
}
|
|
107
|
-
function warmClaimBlockedRefusal(blocker) {
|
|
108
|
-
if (blocker.kind === "refresh") return "Refusing to copy from a source checkout a refresh is rewriting, even without a claim of its own. Wait for that refresh to finish, then run warm again.";
|
|
109
|
-
return `Refusing to copy past a claim Stim cannot resolve. If nothing is warming, remove it and run warm again:\n ${claimRemoveCommand(blocker.path)}`;
|
|
59
|
+
function warmClaimAcquiredLine(wait, detail = "") {
|
|
60
|
+
const acquired = `acquired${detail ? ` ${detail}` : ""}`;
|
|
61
|
+
if (!wait.holder || wait.waitedMs < 1e3) return phaseLine("lock", acquired);
|
|
62
|
+
return phaseLine("lock", `${acquired} (waited ${formatElapsed(wait.waitedMs)} for ${warmCommand(wait.holder.phase)} pid ${wait.holder.pid}) -- stim guide lifecycle options`);
|
|
110
63
|
}
|
|
111
64
|
function installerWaitingLine(pid, elapsedMs) {
|
|
112
65
|
return phaseLine("lock", `waiting on the install this refresh spawned (pid ${pid}, ${formatElapsed(elapsedMs)} elapsed) -- stim guide lifecycle options`);
|
|
@@ -183,7 +136,7 @@ function timeoutError(holder, elapsedMs, root) {
|
|
|
183
136
|
error.code = "STIM_LOCK_TIMEOUT";
|
|
184
137
|
return error;
|
|
185
138
|
}
|
|
186
|
-
async function acquireWarmClaim({ repositoryRoot, phase, now = Date.now, sleep = defaultSleep, pollMs = WARM_CLAIM_POLL_MS, progressMs = WARM_CLAIM_PROGRESS_MS, ceilingMs = WARM_CLAIM_CEILING_MS, out = () => {} }) {
|
|
139
|
+
async function acquireWarmClaim({ repositoryRoot, phase, mode = phase === "refresh" ? "exclusive" : "shared", now = Date.now, sleep = defaultSleep, pollMs = WARM_CLAIM_POLL_MS, progressMs = WARM_CLAIM_PROGRESS_MS, ceilingMs = WARM_CLAIM_CEILING_MS, out = () => {} }) {
|
|
187
140
|
const root = warmClaimPath(repositoryRoot);
|
|
188
141
|
const started = now();
|
|
189
142
|
let lastProgress = started;
|
|
@@ -193,7 +146,7 @@ async function acquireWarmClaim({ repositoryRoot, phase, now = Date.now, sleep =
|
|
|
193
146
|
for (;;) {
|
|
194
147
|
const attempt = pending ? settleClaim(pending) : tryAcquireClaim({
|
|
195
148
|
root,
|
|
196
|
-
mode
|
|
149
|
+
mode,
|
|
197
150
|
label: WARM_CLAIM_LABEL
|
|
198
151
|
});
|
|
199
152
|
const acquired = attempt.acquired;
|
|
@@ -228,6 +181,12 @@ async function acquireWarmClaim({ repositoryRoot, phase, now = Date.now, sleep =
|
|
|
228
181
|
}
|
|
229
182
|
} catch (error) {
|
|
230
183
|
releaseClaim(pending);
|
|
184
|
+
if ([
|
|
185
|
+
"EACCES",
|
|
186
|
+
"EPERM",
|
|
187
|
+
"EROFS",
|
|
188
|
+
"ENOENT"
|
|
189
|
+
].includes(error?.code ?? "")) throw new ClaimUnavailableError(error.message, { claimPath: root });
|
|
231
190
|
throw error;
|
|
232
191
|
}
|
|
233
192
|
}
|
|
@@ -521,6 +480,38 @@ function branchRemote(root, branch) {
|
|
|
521
480
|
`branch.${branch}.remote`
|
|
522
481
|
])?.trim() || "origin";
|
|
523
482
|
}
|
|
483
|
+
function remoteUpstreamIsCurrent(root, branch) {
|
|
484
|
+
const exec = getExecutor();
|
|
485
|
+
const merge = exec.runFileQuiet("git", [
|
|
486
|
+
"-C",
|
|
487
|
+
root,
|
|
488
|
+
"config",
|
|
489
|
+
"--get-all",
|
|
490
|
+
`branch.${branch}.merge`
|
|
491
|
+
])?.trim();
|
|
492
|
+
if (!merge) return true;
|
|
493
|
+
if (!merge.startsWith("refs/") || merge.includes("\n")) return false;
|
|
494
|
+
const remote = branchRemote(root, branch);
|
|
495
|
+
if (remote === ".") return true;
|
|
496
|
+
const local = resolveFullRef(root, "@{upstream}");
|
|
497
|
+
if (!local) return false;
|
|
498
|
+
const advertised = exec.runFileQuiet("git", [
|
|
499
|
+
"-C",
|
|
500
|
+
root,
|
|
501
|
+
"ls-remote",
|
|
502
|
+
"--exit-code",
|
|
503
|
+
"--refs",
|
|
504
|
+
"--",
|
|
505
|
+
remote,
|
|
506
|
+
merge
|
|
507
|
+
], {
|
|
508
|
+
timeoutMs: FETCH_TIMEOUT_MS,
|
|
509
|
+
env: { GIT_TERMINAL_PROMPT: "0" }
|
|
510
|
+
});
|
|
511
|
+
if (advertised === null) return false;
|
|
512
|
+
const refs = advertised.trim().split("\n").map((line) => line.split(/\s+/));
|
|
513
|
+
return refs.length === 1 && refs[0]?.[0] === local && refs[0]?.[1] === merge;
|
|
514
|
+
}
|
|
524
515
|
function gitPath(...parts) {
|
|
525
516
|
return parts.filter((part) => part && part !== ".").join("/");
|
|
526
517
|
}
|
|
@@ -603,7 +594,7 @@ function fastForward(root, upstream) {
|
|
|
603
594
|
};
|
|
604
595
|
}
|
|
605
596
|
}
|
|
606
|
-
async function refreshMainCheckout({ root, appDir, settings, emit, spawnFn = null, now = Date.now, heartbeatMs = HEARTBEAT_INTERVAL_MS, installer = NO_INSTALLER_CLAIM }) {
|
|
597
|
+
async function refreshMainCheckout({ root, appDir, settings, emit, spawnFn = null, now = Date.now, heartbeatMs = HEARTBEAT_INTERVAL_MS, installer = NO_INSTALLER_CLAIM, inspectOnly = false }) {
|
|
607
598
|
const state = readMainCheckoutState(root);
|
|
608
599
|
const refusal = mainCheckoutRefusal(root, state);
|
|
609
600
|
if (refusal) return refusal;
|
|
@@ -640,7 +631,9 @@ async function refreshMainCheckout({ root, appDir, settings, emit, spawnFn = nul
|
|
|
640
631
|
};
|
|
641
632
|
};
|
|
642
633
|
const before = resolveFullRef(root, "HEAD") ?? "";
|
|
643
|
-
if (
|
|
634
|
+
if (inspectOnly) {
|
|
635
|
+
if (!remoteUpstreamIsCurrent(root, branch)) return { mutation: "fetch upstream" };
|
|
636
|
+
} else if (getExecutor().runFileQuiet("git", [
|
|
644
637
|
"-C",
|
|
645
638
|
root,
|
|
646
639
|
"fetch",
|
|
@@ -652,6 +645,7 @@ async function refreshMainCheckout({ root, appDir, settings, emit, spawnFn = nul
|
|
|
652
645
|
}) === null) emit(phaseLine("checkout", "could not fetch; continuing with the local state"));
|
|
653
646
|
const plan = checkoutPlan(locallyKnownUpstream(root));
|
|
654
647
|
if (plan.kind === "diverged") return divergedRefusal(root, branch, plan);
|
|
648
|
+
if (inspectOnly && plan.kind === "fast-forward") return { mutation: `fast-forward ${plan.behind} commit${plan.behind === 1 ? "" : "s"}` };
|
|
655
649
|
const depsBefore = dependencyState(existsSync(appDir) ? appDir : root);
|
|
656
650
|
const priorInstall = depsBefore ? readInstallEvidence(depsBefore.root) : null;
|
|
657
651
|
const marked = plan.kind === "fast-forward" ? depsBefore : null;
|
|
@@ -683,6 +677,7 @@ async function refreshMainCheckout({ root, appDir, settings, emit, spawnFn = nul
|
|
|
683
677
|
});
|
|
684
678
|
const depsSubject = `source ${dependencies?.root ?? root}: ${deps.reason}`;
|
|
685
679
|
if (deps.run && dependencies) {
|
|
680
|
+
if (inspectOnly) return { mutation: deps.reason };
|
|
686
681
|
writeInstallEvidence(dependencies.root, dependencies.lock, consumed, false);
|
|
687
682
|
const install = await runInstallCommand({
|
|
688
683
|
command: dependencies.command,
|
|
@@ -719,6 +714,7 @@ async function refreshMainCheckout({ root, appDir, settings, emit, spawnFn = nul
|
|
|
719
714
|
emit(stepLine("pods", `${where}${pods.reason}`, "skipped"));
|
|
720
715
|
return null;
|
|
721
716
|
}
|
|
717
|
+
if (inspectOnly) return { mutation: pods.reason };
|
|
722
718
|
const result = await runPodInstall(appDir, null, {
|
|
723
719
|
spawnFn: spawn,
|
|
724
720
|
now,
|
|
@@ -793,31 +789,6 @@ function registerWarm(worktree) {
|
|
|
793
789
|
for (const key of unknownSettingKeys(settings)) console.error(chalk.yellow(`Warning: setting "${key}" is not read by Stim and will be ignored.`));
|
|
794
790
|
return settings;
|
|
795
791
|
};
|
|
796
|
-
if (opts.refresh) {
|
|
797
|
-
const settings = readSettings();
|
|
798
|
-
if (!settings) {
|
|
799
|
-
process.exitCode = 1;
|
|
800
|
-
return;
|
|
801
|
-
}
|
|
802
|
-
const failure = await withWarmClaim({
|
|
803
|
-
repositoryRoot: root,
|
|
804
|
-
phase: "refresh",
|
|
805
|
-
out: console.error
|
|
806
|
-
}, (hold) => {
|
|
807
|
-
console.error(warmClaimAcquiredLine(hold.wait));
|
|
808
|
-
return refreshMainCheckout({
|
|
809
|
-
root,
|
|
810
|
-
appDir: mainCheckoutAppDir(root, target),
|
|
811
|
-
settings,
|
|
812
|
-
emit: (line) => console.error(line),
|
|
813
|
-
installer: hold.installer
|
|
814
|
-
});
|
|
815
|
-
});
|
|
816
|
-
if (failure) {
|
|
817
|
-
reportRefreshFailure(failure);
|
|
818
|
-
return;
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
792
|
const copy = (wait) => {
|
|
822
793
|
if (wait?.holder) console.error(warmClaimAcquiredLine(wait));
|
|
823
794
|
const incomplete = incompleteInstallRefusal(root, mainCheckoutAppDir(root, target));
|
|
@@ -846,30 +817,69 @@ function registerWarm(worktree) {
|
|
|
846
817
|
console.error(phaseLine("carry", `${result.failed.length ? "incomplete" : "complete"}: ${result.copied.length} ignored entries copied, ${result.skipped.length} kept, ${result.failed.length} failed`));
|
|
847
818
|
if (result.failed.length) process.exitCode = 1;
|
|
848
819
|
};
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
hold = await acquireWarmClaim({
|
|
820
|
+
if (opts.refresh) {
|
|
821
|
+
const inspect = await acquireWarmClaim({
|
|
852
822
|
repositoryRoot: root,
|
|
853
|
-
phase: "
|
|
823
|
+
phase: "refresh",
|
|
824
|
+
mode: "shared",
|
|
854
825
|
out: console.error
|
|
855
826
|
});
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
827
|
+
let mutation;
|
|
828
|
+
try {
|
|
829
|
+
const settings = readSettings();
|
|
830
|
+
if (!settings) {
|
|
831
|
+
process.exitCode = 1;
|
|
832
|
+
return;
|
|
833
|
+
}
|
|
834
|
+
const lines = [];
|
|
835
|
+
const result = await refreshMainCheckout({
|
|
836
|
+
root,
|
|
837
|
+
appDir: mainCheckoutAppDir(root, target),
|
|
838
|
+
settings,
|
|
839
|
+
emit: (line) => lines.push(line),
|
|
840
|
+
inspectOnly: true
|
|
841
|
+
});
|
|
842
|
+
if (!result || !("mutation" in result)) {
|
|
843
|
+
console.error(warmClaimAcquiredLine(inspect.wait, result ? "shared" : "shared (seed current)"));
|
|
844
|
+
for (const line of lines) console.error(line);
|
|
845
|
+
if (result) reportRefreshFailure(result);
|
|
846
|
+
else copy(null);
|
|
847
|
+
return;
|
|
848
|
+
}
|
|
849
|
+
mutation = result.mutation;
|
|
850
|
+
} finally {
|
|
851
|
+
inspect.release();
|
|
864
852
|
}
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
853
|
+
const failure = await withWarmClaim({
|
|
854
|
+
repositoryRoot: root,
|
|
855
|
+
phase: "refresh",
|
|
856
|
+
out: console.error
|
|
857
|
+
}, async (hold) => {
|
|
858
|
+
console.error(warmClaimAcquiredLine(hold.wait, `exclusive (${mutation})`));
|
|
859
|
+
const settings = readSettings();
|
|
860
|
+
if (!settings) {
|
|
861
|
+
process.exitCode = 1;
|
|
862
|
+
return null;
|
|
863
|
+
}
|
|
864
|
+
return refreshMainCheckout({
|
|
865
|
+
root,
|
|
866
|
+
appDir: mainCheckoutAppDir(root, target),
|
|
867
|
+
settings,
|
|
868
|
+
emit: (line) => console.error(line),
|
|
869
|
+
installer: hold.installer
|
|
870
|
+
});
|
|
871
|
+
});
|
|
872
|
+
if (failure) {
|
|
873
|
+
reportRefreshFailure(failure);
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
876
|
+
if (process.exitCode) return;
|
|
872
877
|
}
|
|
878
|
+
await withWarmClaim({
|
|
879
|
+
repositoryRoot: root,
|
|
880
|
+
phase: "copy",
|
|
881
|
+
out: console.error
|
|
882
|
+
}, (hold) => copy(hold.wait));
|
|
873
883
|
} catch (error) {
|
|
874
884
|
const code = error?.code;
|
|
875
885
|
console.error(chalk.red(`Could not warm this worktree: ${error.message}`));
|