@theford/runly 0.0.0-stage → 0.20.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/LICENSE +202 -0
- package/README.md +157 -2
- package/dist/cli/client.d.ts +80 -0
- package/dist/cli/client.js +294 -0
- package/dist/cli/client.js.map +1 -0
- package/dist/cli/daemon-unit.d.ts +58 -0
- package/dist/cli/daemon-unit.js +289 -0
- package/dist/cli/daemon-unit.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +1482 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/install.d.ts +9 -0
- package/dist/cli/install.js +53 -0
- package/dist/cli/install.js.map +1 -0
- package/dist/cli/logs.d.ts +43 -0
- package/dist/cli/logs.js +232 -0
- package/dist/cli/logs.js.map +1 -0
- package/dist/cli/tether.d.ts +3 -0
- package/dist/cli/tether.js +65 -0
- package/dist/cli/tether.js.map +1 -0
- package/dist/cli/watchdog.d.ts +1 -0
- package/dist/cli/watchdog.js +81 -0
- package/dist/cli/watchdog.js.map +1 -0
- package/dist/core/builds.d.ts +20 -0
- package/dist/core/builds.js +93 -0
- package/dist/core/builds.js.map +1 -0
- package/dist/core/caller-env.d.ts +17 -0
- package/dist/core/caller-env.js +77 -0
- package/dist/core/caller-env.js.map +1 -0
- package/dist/core/diagnostics.d.ts +68 -0
- package/dist/core/diagnostics.js +34 -0
- package/dist/core/diagnostics.js.map +1 -0
- package/dist/core/env-vars.d.ts +35 -0
- package/dist/core/env-vars.js +32 -0
- package/dist/core/env-vars.js.map +1 -0
- package/dist/core/events.d.ts +10 -0
- package/dist/core/events.js +42 -0
- package/dist/core/events.js.map +1 -0
- package/dist/core/exec.d.ts +22 -0
- package/dist/core/exec.js +84 -0
- package/dist/core/exec.js.map +1 -0
- package/dist/core/journal.d.ts +227 -0
- package/dist/core/journal.js +395 -0
- package/dist/core/journal.js.map +1 -0
- package/dist/core/logs.d.ts +56 -0
- package/dist/core/logs.js +259 -0
- package/dist/core/logs.js.map +1 -0
- package/dist/core/manifest.d.ts +292 -0
- package/dist/core/manifest.js +179 -0
- package/dist/core/manifest.js.map +1 -0
- package/dist/core/namespaces.d.ts +7 -0
- package/dist/core/namespaces.js +59 -0
- package/dist/core/namespaces.js.map +1 -0
- package/dist/core/paths.d.ts +31 -0
- package/dist/core/paths.js +65 -0
- package/dist/core/paths.js.map +1 -0
- package/dist/core/policy.d.ts +94 -0
- package/dist/core/policy.js +122 -0
- package/dist/core/policy.js.map +1 -0
- package/dist/core/ports.d.ts +52 -0
- package/dist/core/ports.js +94 -0
- package/dist/core/ports.js.map +1 -0
- package/dist/core/presets.d.ts +25 -0
- package/dist/core/presets.js +86 -0
- package/dist/core/presets.js.map +1 -0
- package/dist/core/procscan.d.ts +111 -0
- package/dist/core/procscan.js +342 -0
- package/dist/core/procscan.js.map +1 -0
- package/dist/core/retention.d.ts +97 -0
- package/dist/core/retention.js +322 -0
- package/dist/core/retention.js.map +1 -0
- package/dist/core/sleep.d.ts +51 -0
- package/dist/core/sleep.js +99 -0
- package/dist/core/sleep.js.map +1 -0
- package/dist/core/tree-ledger.d.ts +39 -0
- package/dist/core/tree-ledger.js +92 -0
- package/dist/core/tree-ledger.js.map +1 -0
- package/dist/core/types.d.ts +21 -0
- package/dist/core/types.js +6 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/units.d.ts +20 -0
- package/dist/core/units.js +69 -0
- package/dist/core/units.js.map +1 -0
- package/dist/core/upkeep.d.ts +90 -0
- package/dist/core/upkeep.js +176 -0
- package/dist/core/upkeep.js.map +1 -0
- package/dist/core/util.d.ts +46 -0
- package/dist/core/util.js +168 -0
- package/dist/core/util.js.map +1 -0
- package/dist/core/version.d.ts +39 -0
- package/dist/core/version.js +144 -0
- package/dist/core/version.js.map +1 -0
- package/dist/core/worktree.d.ts +51 -0
- package/dist/core/worktree.js +309 -0
- package/dist/core/worktree.js.map +1 -0
- package/dist/daemon/budget.d.ts +155 -0
- package/dist/daemon/budget.js +288 -0
- package/dist/daemon/budget.js.map +1 -0
- package/dist/daemon/election.d.ts +9 -0
- package/dist/daemon/election.js +140 -0
- package/dist/daemon/election.js.map +1 -0
- package/dist/daemon/engine.d.ts +1703 -0
- package/dist/daemon/engine.js +5305 -0
- package/dist/daemon/engine.js.map +1 -0
- package/dist/daemon/index.d.ts +2 -0
- package/dist/daemon/index.js +412 -0
- package/dist/daemon/index.js.map +1 -0
- package/dist/daemon/proxy.d.ts +108 -0
- package/dist/daemon/proxy.js +615 -0
- package/dist/daemon/proxy.js.map +1 -0
- package/dist/daemon/supervisor.d.ts +101 -0
- package/dist/daemon/supervisor.js +493 -0
- package/dist/daemon/supervisor.js.map +1 -0
- package/dist/drivers/appliances.d.ts +11 -0
- package/dist/drivers/appliances.js +174 -0
- package/dist/drivers/appliances.js.map +1 -0
- package/dist/drivers/datastores.d.ts +144 -0
- package/dist/drivers/datastores.js +775 -0
- package/dist/drivers/datastores.js.map +1 -0
- package/dist/drivers/preview.d.ts +69 -0
- package/dist/drivers/preview.js +605 -0
- package/dist/drivers/preview.js.map +1 -0
- package/dist/drivers/types.d.ts +70 -0
- package/dist/drivers/types.js +13 -0
- package/dist/drivers/types.js.map +1 -0
- package/package.json +63 -4
- package/schema/backlot.schema.json +549 -0
- package/schema/runly.schema.json +556 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export declare const VERSION: string;
|
|
2
|
+
export declare const BUILD: string;
|
|
3
|
+
/** True only when both builds are known and differ — a same-version rebuild or reinstall. */
|
|
4
|
+
export declare function rebuiltSince(cliBuild: string, daemonBuild: string | undefined): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* Order two versions, or return undefined when they cannot be ordered.
|
|
7
|
+
*
|
|
8
|
+
* Deliberately not a full semver implementation: the only question asked of it
|
|
9
|
+
* is "is the daemon behind or ahead of this CLI", and the answer drives a
|
|
10
|
+
* message plus a refusal, never an install decision. A prerelease suffix makes
|
|
11
|
+
* two versions UNORDERED rather than guessing at precedence — a wrong guess
|
|
12
|
+
* would let `update` silently downgrade a daemon.
|
|
13
|
+
*/
|
|
14
|
+
export declare function compareVersions(a: string, b: string): number | undefined;
|
|
15
|
+
export type SkewDirection = 'daemon-older' | 'daemon-newer' | 'daemon-unversioned' | 'unordered';
|
|
16
|
+
export interface VersionSkew {
|
|
17
|
+
cli: string;
|
|
18
|
+
daemon: string;
|
|
19
|
+
direction: SkewDirection;
|
|
20
|
+
/** Human sentence, ending in the remedy. Reused verbatim by CLI and doctor. */
|
|
21
|
+
message: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Compare this CLI against the daemon actually answering on the socket.
|
|
25
|
+
*
|
|
26
|
+
* This exists because `ensureDaemon` spawns the daemon from the CLI's own
|
|
27
|
+
* `dist/`, which means an upgrade replaces both files but does NOT replace the
|
|
28
|
+
* RUNNING daemon — it keeps serving old code for as long as it lives, and the
|
|
29
|
+
* socket carries no version. An old daemon does not reject arguments it has
|
|
30
|
+
* never heard of, it ignores them: a 0.8.0 daemon asked for `up --data-only`
|
|
31
|
+
* boots the whole application instead, and the caller is told nothing. That is
|
|
32
|
+
* the same failure shape as issue #41 — a wrong result that reads as a bug in
|
|
33
|
+
* a different subsystem — which is why skew is refused rather than warned
|
|
34
|
+
* about.
|
|
35
|
+
*
|
|
36
|
+
* `daemon` is undefined when the daemon predates version reporting (<= 0.8.0),
|
|
37
|
+
* which is itself proof of skew for any build that has this function.
|
|
38
|
+
*/
|
|
39
|
+
export declare function versionSkew(cli: string, daemon: string | undefined): VersionSkew | null;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one place that knows what version this build is, and the only comparison
|
|
3
|
+
* of a CLI against the daemon it is talking to.
|
|
4
|
+
*
|
|
5
|
+
* Read from package.json rather than baked into a constant: the MCP adapter
|
|
6
|
+
* carried a hand-maintained version that drifted (0.4.0 while the package
|
|
7
|
+
* shipped 0.5.0), so clients were told the wrong version of the tool they were
|
|
8
|
+
* driving. One path serves both installs — the npm tarball ships package.json
|
|
9
|
+
* beside `dist/`, and a git checkout has it at the same relative position.
|
|
10
|
+
*/
|
|
11
|
+
import { readFileSync, statSync } from 'node:fs';
|
|
12
|
+
import { dirname, join } from 'node:path';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
function readVersion() {
|
|
15
|
+
// A test cannot provoke version skew from a single build any other way: the
|
|
16
|
+
// CLI spawns the daemon from its OWN dist, so both sides always agree. This
|
|
17
|
+
// lets a test stand up a daemon that CLAIMS a different version than the CLI
|
|
18
|
+
// driving it. Never set it in real use.
|
|
19
|
+
const fake = process.env.BACKLOT_FAKE_VERSION;
|
|
20
|
+
if (fake)
|
|
21
|
+
return fake;
|
|
22
|
+
try {
|
|
23
|
+
const pkg = join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'package.json');
|
|
24
|
+
const v = JSON.parse(readFileSync(pkg, 'utf8')).version;
|
|
25
|
+
if (typeof v === 'string' && v !== '')
|
|
26
|
+
return v;
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
/* fall through — see below */
|
|
30
|
+
}
|
|
31
|
+
// A build whose package.json is missing or unreadable still has to work: the
|
|
32
|
+
// version is a diagnostic, and failing every verb over an unreadable
|
|
33
|
+
// diagnostic would be worse than not knowing. `doctor` reports the gap, and
|
|
34
|
+
// an unknown version never counts as skew (it cannot be compared, and
|
|
35
|
+
// refusing on "I don't know" would wedge such an install completely).
|
|
36
|
+
return 'unknown';
|
|
37
|
+
}
|
|
38
|
+
export const VERSION = readVersion();
|
|
39
|
+
/**
|
|
40
|
+
* Which BUILD of this version is running — the modification time of this very
|
|
41
|
+
* module, as the process saw it when it loaded.
|
|
42
|
+
*
|
|
43
|
+
* The version alone cannot tell a rebuilt checkout from the build it replaced:
|
|
44
|
+
* `git pull && npm run build` on a checkout install keeps package.json at the
|
|
45
|
+
* same version while every file under dist/ changes, so `runly update` used to
|
|
46
|
+
* report "already runly 0.12.0 — nothing to do" and leave the old code serving.
|
|
47
|
+
* The daemon reads this once at start and keeps it; the CLI reads it fresh, so
|
|
48
|
+
* any rebuild or reinstall since the daemon started shows up as a difference.
|
|
49
|
+
* `unknown` (unreadable) never counts as a difference, for the same reason an
|
|
50
|
+
* unknown version never counts as skew.
|
|
51
|
+
*/
|
|
52
|
+
function readBuild() {
|
|
53
|
+
const fake = process.env.BACKLOT_FAKE_BUILD;
|
|
54
|
+
if (fake)
|
|
55
|
+
return fake;
|
|
56
|
+
try {
|
|
57
|
+
return String(Math.trunc(statSync(fileURLToPath(import.meta.url)).mtimeMs));
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return 'unknown';
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
export const BUILD = readBuild();
|
|
64
|
+
/** True only when both builds are known and differ — a same-version rebuild or reinstall. */
|
|
65
|
+
export function rebuiltSince(cliBuild, daemonBuild) {
|
|
66
|
+
if (daemonBuild === undefined || daemonBuild === 'unknown' || cliBuild === 'unknown')
|
|
67
|
+
return false;
|
|
68
|
+
return cliBuild !== daemonBuild;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Order two versions, or return undefined when they cannot be ordered.
|
|
72
|
+
*
|
|
73
|
+
* Deliberately not a full semver implementation: the only question asked of it
|
|
74
|
+
* is "is the daemon behind or ahead of this CLI", and the answer drives a
|
|
75
|
+
* message plus a refusal, never an install decision. A prerelease suffix makes
|
|
76
|
+
* two versions UNORDERED rather than guessing at precedence — a wrong guess
|
|
77
|
+
* would let `update` silently downgrade a daemon.
|
|
78
|
+
*/
|
|
79
|
+
export function compareVersions(a, b) {
|
|
80
|
+
if (a === b)
|
|
81
|
+
return 0;
|
|
82
|
+
if (a === 'unknown' || b === 'unknown')
|
|
83
|
+
return undefined;
|
|
84
|
+
const parse = (v) => {
|
|
85
|
+
const m = /^(\d+)\.(\d+)\.(\d+)(.*)$/.exec(v.trim());
|
|
86
|
+
if (!m)
|
|
87
|
+
return undefined;
|
|
88
|
+
return { nums: [Number(m[1]), Number(m[2]), Number(m[3])], suffix: m[4] ?? '' };
|
|
89
|
+
};
|
|
90
|
+
const pa = parse(a);
|
|
91
|
+
const pb = parse(b);
|
|
92
|
+
if (!pa || !pb)
|
|
93
|
+
return undefined;
|
|
94
|
+
for (let i = 0; i < 3; i++) {
|
|
95
|
+
if (pa.nums[i] !== pb.nums[i])
|
|
96
|
+
return pa.nums[i] < pb.nums[i] ? -1 : 1;
|
|
97
|
+
}
|
|
98
|
+
// Same release numbers, different text (a prerelease against its release, or
|
|
99
|
+
// two prereleases): different builds, no defensible order.
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Compare this CLI against the daemon actually answering on the socket.
|
|
104
|
+
*
|
|
105
|
+
* This exists because `ensureDaemon` spawns the daemon from the CLI's own
|
|
106
|
+
* `dist/`, which means an upgrade replaces both files but does NOT replace the
|
|
107
|
+
* RUNNING daemon — it keeps serving old code for as long as it lives, and the
|
|
108
|
+
* socket carries no version. An old daemon does not reject arguments it has
|
|
109
|
+
* never heard of, it ignores them: a 0.8.0 daemon asked for `up --data-only`
|
|
110
|
+
* boots the whole application instead, and the caller is told nothing. That is
|
|
111
|
+
* the same failure shape as issue #41 — a wrong result that reads as a bug in
|
|
112
|
+
* a different subsystem — which is why skew is refused rather than warned
|
|
113
|
+
* about.
|
|
114
|
+
*
|
|
115
|
+
* `daemon` is undefined when the daemon predates version reporting (<= 0.8.0),
|
|
116
|
+
* which is itself proof of skew for any build that has this function.
|
|
117
|
+
*/
|
|
118
|
+
export function versionSkew(cli, daemon) {
|
|
119
|
+
if (daemon === undefined) {
|
|
120
|
+
return {
|
|
121
|
+
cli,
|
|
122
|
+
daemon: 'pre-0.9.0',
|
|
123
|
+
direction: 'daemon-unversioned',
|
|
124
|
+
message: `the running daemon predates version reporting (runly <= 0.8.0) while this CLI is ${cli} — ` +
|
|
125
|
+
`it would serve your request with the old code, silently ignoring anything this version added. ` +
|
|
126
|
+
`Run 'runly update' to restart the daemon onto the installed version.`,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
if (daemon === cli)
|
|
130
|
+
return null;
|
|
131
|
+
const order = compareVersions(daemon, cli);
|
|
132
|
+
const direction = order === undefined ? 'unordered' : order < 0 ? 'daemon-older' : 'daemon-newer';
|
|
133
|
+
const tail = direction === 'daemon-newer'
|
|
134
|
+
? `You are running an OLDER CLI than the daemon; 'runly update' would DOWNGRADE it, so it refuses without --force. ` +
|
|
135
|
+
`Prefer invoking the newer CLI, or upgrade this one.`
|
|
136
|
+
: `Run 'runly update' to restart the daemon onto the installed version.`;
|
|
137
|
+
return {
|
|
138
|
+
cli,
|
|
139
|
+
daemon,
|
|
140
|
+
direction,
|
|
141
|
+
message: `the running daemon is runly ${daemon} but this CLI is ${cli} — it would serve your request with the other build's code. ${tail}`,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/core/version.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { Manifest } from './manifest.js';
|
|
2
|
+
/**
|
|
3
|
+
* The literal directory (or file) each glob can only match under, as git
|
|
4
|
+
* pathspecs — or null when one of them can match anywhere (a bare name, a
|
|
5
|
+
* leading `**`), so the whole worktree has to be listed.
|
|
6
|
+
*/
|
|
7
|
+
export declare function globPathspecs(globs: string[]): string[] | null;
|
|
8
|
+
/**
|
|
9
|
+
* The worktree's files that match `only` (globs relative to the stack root),
|
|
10
|
+
* or all of them when `only` is omitted.
|
|
11
|
+
*
|
|
12
|
+
* The listing is git's (one `ls-files`, limited to the globs' literal
|
|
13
|
+
* prefixes); the globs are applied BEFORE anything is stat'ed, so a bind
|
|
14
|
+
* whose upkeep rules name a lockfile and a migrations folder touches those
|
|
15
|
+
* files and nothing else of a 35k-file worktree.
|
|
16
|
+
*
|
|
17
|
+
* A submodule appears in ls-files only as its gitlink path, which stats as a
|
|
18
|
+
* directory, so a CHECKED-OUT submodule is enumerated recursively — a trigger
|
|
19
|
+
* inside it must fire like any other. An uninitialised submodule is an empty
|
|
20
|
+
* directory and contributes nothing. A repository without a .gitmodules has
|
|
21
|
+
* no gitlinks, and the second listing is skipped.
|
|
22
|
+
*/
|
|
23
|
+
export declare function enumerateSource(stackRoot: string, manifest: Manifest, only?: string[]): Promise<string[]>;
|
|
24
|
+
/** Let the proxy and every other request run between two slices of a long loop. */
|
|
25
|
+
export declare const yieldLoop: () => Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* `enumerateSource` with each file's size and mtime — ONE stat per file,
|
|
28
|
+
* which the build key needs anyway (decision 0038). The stats run in slices,
|
|
29
|
+
* yielding the event loop between them: on a 35k-file worktree the stat loop
|
|
30
|
+
* alone held every proxied connection of every environment for 100+ ms.
|
|
31
|
+
*/
|
|
32
|
+
export declare function enumerateSourceStats(stackRoot: string, manifest: Manifest, only?: string[]): Promise<Array<{
|
|
33
|
+
path: string;
|
|
34
|
+
size: number;
|
|
35
|
+
mtimeMs: number;
|
|
36
|
+
}>>;
|
|
37
|
+
/**
|
|
38
|
+
* A snapshot of a service's declared build outputs: every file matching the
|
|
39
|
+
* globs, with its size and mtime, as one comparable string. `up` takes one
|
|
40
|
+
* before and one after the service's build and restarts the service only when
|
|
41
|
+
* they differ (decision 0032). Path + size + mtime is what a build tool
|
|
42
|
+
* changes when it writes; content is read only with `compare: content`
|
|
43
|
+
* (decision 0038).
|
|
44
|
+
*
|
|
45
|
+
* Each glob is walked from its literal prefix only, so `backend/host/bin/**`
|
|
46
|
+
* never walks the rest of the worktree. A glob that escapes the worktree is
|
|
47
|
+
* ignored.
|
|
48
|
+
*/
|
|
49
|
+
export declare function snapshotOutputs(stackRoot: string, globs: string[], compare?: 'stat' | 'content'): Promise<string>;
|
|
50
|
+
/** sha256 of a file, streamed; null when it cannot be read. */
|
|
51
|
+
export declare function hashFile(path: string): Promise<string | null>;
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the worktree contains, for the few things runly still has to know about
|
|
3
|
+
* it (decision 0032). Environments run IN the caller's worktree and runly does
|
|
4
|
+
* not cache builds, so there is no whole-worktree source identity any more:
|
|
5
|
+
* MSBuild, pnpm and the Angular CLI decide their own incrementality.
|
|
6
|
+
*
|
|
7
|
+
* Two questions remain, and both are about DECLARED paths only:
|
|
8
|
+
*
|
|
9
|
+
* - which files match an upkeep rule's `when:` glob (and a datastore's
|
|
10
|
+
* @rebake-template trigger) — `enumerateSource` lists the candidates the
|
|
11
|
+
* globs are matched against; only the matching files are ever read;
|
|
12
|
+
* - whether a service's declared build `outputs:` changed across its build —
|
|
13
|
+
* `snapshotOutputs`, which decides whether `up` restarts the service.
|
|
14
|
+
*
|
|
15
|
+
* Git decides what belongs to the worktree (tracked + untracked-unignored
|
|
16
|
+
* under the stack root, plus sync.include and checked-out submodules, minus
|
|
17
|
+
* declared `caches:`).
|
|
18
|
+
*/
|
|
19
|
+
import { execFile } from 'node:child_process';
|
|
20
|
+
import { createReadStream, existsSync, lstatSync, readdirSync } from 'node:fs';
|
|
21
|
+
import { createHash } from 'node:crypto';
|
|
22
|
+
import { dirname, join } from 'node:path';
|
|
23
|
+
import { promisify } from 'node:util';
|
|
24
|
+
import { isFile, matchesAny, safeJoin, statOf } from './util.js';
|
|
25
|
+
const execFileP = promisify(execFile);
|
|
26
|
+
/**
|
|
27
|
+
* git's file list under `root`, restricted to `pathspecs` when given (literal
|
|
28
|
+
* directory or file paths relative to `root`). Asynchronous: on a large
|
|
29
|
+
* worktree the listing takes long enough that a synchronous call stalled every
|
|
30
|
+
* proxied connection of every environment while it ran.
|
|
31
|
+
*/
|
|
32
|
+
async function gitFiles(root, pathspecs = ['.']) {
|
|
33
|
+
try {
|
|
34
|
+
const { stdout } = await execFileP('git', ['-C', root, 'ls-files', '-z', '--cached', '--others', '--exclude-standard', '--', ...pathspecs], { encoding: 'utf8', maxBuffer: 256 * 1024 * 1024, env: { ...process.env, GIT_LITERAL_PATHSPECS: '1' } });
|
|
35
|
+
return stdout.split('\0').filter(Boolean);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/** Does the repository `root` belongs to declare submodules at all? (No .gitmodules, no gitlinks to look for.) */
|
|
42
|
+
function declaresSubmodules(root) {
|
|
43
|
+
for (let dir = root;; dir = dirname(dir)) {
|
|
44
|
+
if (existsSync(join(dir, '.gitmodules')))
|
|
45
|
+
return true;
|
|
46
|
+
if (existsSync(join(dir, '.git')) || dirname(dir) === dir)
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/** Gitlink (submodule) paths under `root`, as `git ls-files -s` reports them. */
|
|
51
|
+
async function gitlinks(root) {
|
|
52
|
+
try {
|
|
53
|
+
const { stdout } = await execFileP('git', ['-C', root, 'ls-files', '-s', '-z', '--', '.'], {
|
|
54
|
+
encoding: 'utf8',
|
|
55
|
+
maxBuffer: 256 * 1024 * 1024,
|
|
56
|
+
});
|
|
57
|
+
return stdout
|
|
58
|
+
.split('\0')
|
|
59
|
+
.filter((l) => l.startsWith('160000 '))
|
|
60
|
+
.map((l) => l.split('\t')[1] ?? '')
|
|
61
|
+
.filter(Boolean);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The literal directory (or file) each glob can only match under, as git
|
|
69
|
+
* pathspecs — or null when one of them can match anywhere (a bare name, a
|
|
70
|
+
* leading `**`), so the whole worktree has to be listed.
|
|
71
|
+
*/
|
|
72
|
+
export function globPathspecs(globs) {
|
|
73
|
+
const specs = new Set();
|
|
74
|
+
for (const g of globs) {
|
|
75
|
+
const p = g.replace(/^glob\((.*)\)$/, '$1').replace(/^\.\//, '');
|
|
76
|
+
const parts = p.split('/');
|
|
77
|
+
if (parts.length === 1 && !/[*?[]/.test(p))
|
|
78
|
+
return null; // a bare name matches a segment anywhere
|
|
79
|
+
const stop = parts.findIndex((seg) => /[*?[]/.test(seg));
|
|
80
|
+
const prefix = (stop === -1 ? parts : parts.slice(0, stop)).filter(Boolean).join('/');
|
|
81
|
+
if (!prefix || prefix.split('/').includes('..'))
|
|
82
|
+
return null;
|
|
83
|
+
specs.add(prefix);
|
|
84
|
+
}
|
|
85
|
+
return [...specs];
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* The worktree's files that match `only` (globs relative to the stack root),
|
|
89
|
+
* or all of them when `only` is omitted.
|
|
90
|
+
*
|
|
91
|
+
* The listing is git's (one `ls-files`, limited to the globs' literal
|
|
92
|
+
* prefixes); the globs are applied BEFORE anything is stat'ed, so a bind
|
|
93
|
+
* whose upkeep rules name a lockfile and a migrations folder touches those
|
|
94
|
+
* files and nothing else of a 35k-file worktree.
|
|
95
|
+
*
|
|
96
|
+
* A submodule appears in ls-files only as its gitlink path, which stats as a
|
|
97
|
+
* directory, so a CHECKED-OUT submodule is enumerated recursively — a trigger
|
|
98
|
+
* inside it must fire like any other. An uninitialised submodule is an empty
|
|
99
|
+
* directory and contributes nothing. A repository without a .gitmodules has
|
|
100
|
+
* no gitlinks, and the second listing is skipped.
|
|
101
|
+
*/
|
|
102
|
+
export async function enumerateSource(stackRoot, manifest, only) {
|
|
103
|
+
return (await enumerateSourceStats(stackRoot, manifest, only)).map((f) => f.path);
|
|
104
|
+
}
|
|
105
|
+
/** Files between two yields to the event loop while a listing is stat'ed. */
|
|
106
|
+
const YIELD_EVERY = 2000;
|
|
107
|
+
/** Let the proxy and every other request run between two slices of a long loop. */
|
|
108
|
+
export const yieldLoop = () => new Promise((r) => setImmediate(r));
|
|
109
|
+
/**
|
|
110
|
+
* `enumerateSource` with each file's size and mtime — ONE stat per file,
|
|
111
|
+
* which the build key needs anyway (decision 0038). The stats run in slices,
|
|
112
|
+
* yielding the event loop between them: on a 35k-file worktree the stat loop
|
|
113
|
+
* alone held every proxied connection of every environment for 100+ ms.
|
|
114
|
+
*/
|
|
115
|
+
export async function enumerateSourceStats(stackRoot, manifest, only) {
|
|
116
|
+
const pathspecs = only === undefined ? null : globPathspecs(only);
|
|
117
|
+
const narrowed = pathspecs !== null && pathspecs.length > 0;
|
|
118
|
+
const listed = (await gitFiles(stackRoot, narrowed ? pathspecs : undefined)) ?? walkAll(stackRoot);
|
|
119
|
+
const seen = new Set(listed);
|
|
120
|
+
const nested = async (root, prefix, depth) => {
|
|
121
|
+
if (depth > 8)
|
|
122
|
+
return; // a submodule cycle
|
|
123
|
+
if (!declaresSubmodules(root))
|
|
124
|
+
return;
|
|
125
|
+
for (const gl of await gitlinks(root)) {
|
|
126
|
+
const sub = join(root, gl);
|
|
127
|
+
if (!existsSync(join(sub, '.git')))
|
|
128
|
+
continue;
|
|
129
|
+
for (const f of (await gitFiles(sub)) ?? []) {
|
|
130
|
+
const rel = `${prefix}${gl}/${f}`;
|
|
131
|
+
if (!seen.has(rel)) {
|
|
132
|
+
seen.add(rel);
|
|
133
|
+
listed.push(rel);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
await nested(sub, `${prefix}${gl}/`, depth + 1);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
await nested(stackRoot, '', 0);
|
|
140
|
+
// sync.include: git-ignored files that still count as source (an .env.local
|
|
141
|
+
// an upkeep rule may name as its trigger).
|
|
142
|
+
for (const inc of manifest.sync?.include ?? []) {
|
|
143
|
+
safeJoin(stackRoot, inc, 'sync.include'); // reject ../ or absolute before use
|
|
144
|
+
if (isFile(join(stackRoot, inc)) && !seen.has(inc)) {
|
|
145
|
+
seen.add(inc);
|
|
146
|
+
listed.push(inc);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// `caches:` names what builds and installs write INTO the worktree. That is
|
|
150
|
+
// output, not source: a trigger glob like `**/package.json` must not fire
|
|
151
|
+
// because an install rewrote node_modules/. (git-ignored output is already
|
|
152
|
+
// absent; this covers a repo that has not ignored its own.) sync.include
|
|
153
|
+
// outranks it.
|
|
154
|
+
const caches = manifest.caches ?? [];
|
|
155
|
+
const included = new Set(manifest.sync?.include ?? []);
|
|
156
|
+
// Tracked-but-deleted files still appear in ls-files --cached.
|
|
157
|
+
const candidates = listed
|
|
158
|
+
.filter((f) => only === undefined || matchesAny(f, only))
|
|
159
|
+
.filter((f) => included.has(f) || caches.length === 0 || !matchesAny(f, caches))
|
|
160
|
+
.sort();
|
|
161
|
+
const out = [];
|
|
162
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
163
|
+
if (i > 0 && i % YIELD_EVERY === 0)
|
|
164
|
+
await yieldLoop();
|
|
165
|
+
const f = candidates[i];
|
|
166
|
+
const st = statOf(join(stackRoot, f));
|
|
167
|
+
if (st?.isFile())
|
|
168
|
+
out.push({ path: f, size: st.size, mtimeMs: st.mtimeMs });
|
|
169
|
+
}
|
|
170
|
+
return out;
|
|
171
|
+
}
|
|
172
|
+
function walkAll(root, prefix = '') {
|
|
173
|
+
const out = [];
|
|
174
|
+
let names;
|
|
175
|
+
try {
|
|
176
|
+
names = readdirSync(join(root, prefix));
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
return out;
|
|
180
|
+
}
|
|
181
|
+
for (const name of names) {
|
|
182
|
+
if (name === '.git' || name === 'node_modules')
|
|
183
|
+
continue;
|
|
184
|
+
const rel = prefix ? `${prefix}/${name}` : name;
|
|
185
|
+
// lstat, not stat: a DANGLING symlink makes stat throw ENOENT, which took
|
|
186
|
+
// down the whole enumeration. Directory symlinks are listed but not
|
|
187
|
+
// followed, so a link pointing at an ancestor cannot recurse forever.
|
|
188
|
+
let st;
|
|
189
|
+
try {
|
|
190
|
+
st = lstatSync(join(root, rel));
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
continue; // vanished mid-walk
|
|
194
|
+
}
|
|
195
|
+
if (st.isSymbolicLink())
|
|
196
|
+
out.push(rel);
|
|
197
|
+
else if (st.isDirectory())
|
|
198
|
+
out.push(...walkAll(root, rel));
|
|
199
|
+
else
|
|
200
|
+
out.push(rel);
|
|
201
|
+
}
|
|
202
|
+
return out;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* A snapshot of a service's declared build outputs: every file matching the
|
|
206
|
+
* globs, with its size and mtime, as one comparable string. `up` takes one
|
|
207
|
+
* before and one after the service's build and restarts the service only when
|
|
208
|
+
* they differ (decision 0032). Path + size + mtime is what a build tool
|
|
209
|
+
* changes when it writes; content is read only with `compare: content`
|
|
210
|
+
* (decision 0038).
|
|
211
|
+
*
|
|
212
|
+
* Each glob is walked from its literal prefix only, so `backend/host/bin/**`
|
|
213
|
+
* never walks the rest of the worktree. A glob that escapes the worktree is
|
|
214
|
+
* ignored.
|
|
215
|
+
*/
|
|
216
|
+
export async function snapshotOutputs(stackRoot, globs, compare = 'stat') {
|
|
217
|
+
const seen = new Map();
|
|
218
|
+
let visited = 0;
|
|
219
|
+
// Sliced like the source listing: a bin/ of thousands of files must not
|
|
220
|
+
// hold the daemon's event loop (and every proxied connection) for its walk.
|
|
221
|
+
const walk = async (dir, rel, all = false) => {
|
|
222
|
+
let names;
|
|
223
|
+
try {
|
|
224
|
+
names = readdirSync(dir);
|
|
225
|
+
}
|
|
226
|
+
catch {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
for (const name of names) {
|
|
230
|
+
if (name === '.git')
|
|
231
|
+
continue;
|
|
232
|
+
if (++visited % YIELD_EVERY === 0)
|
|
233
|
+
await yieldLoop();
|
|
234
|
+
const childRel = rel ? `${rel}/${name}` : name;
|
|
235
|
+
const full = join(dir, name);
|
|
236
|
+
let st;
|
|
237
|
+
try {
|
|
238
|
+
st = lstatSync(full);
|
|
239
|
+
}
|
|
240
|
+
catch {
|
|
241
|
+
continue; // vanished mid-walk
|
|
242
|
+
}
|
|
243
|
+
if (st.isDirectory())
|
|
244
|
+
await walk(full, childRel, all);
|
|
245
|
+
else if (all || matchesAny(childRel, globs))
|
|
246
|
+
seen.set(childRel, { full, size: st.size, mtimeMs: st.mtimeMs });
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
for (const glob of globs) {
|
|
250
|
+
const parts = glob.replace(/^\.\//, '').split('/');
|
|
251
|
+
const stop = parts.findIndex((seg) => /[*?[]/.test(seg));
|
|
252
|
+
const base = (stop === -1 ? parts.slice(0, -1) : parts.slice(0, stop)).join('/');
|
|
253
|
+
let start;
|
|
254
|
+
try {
|
|
255
|
+
start = base ? safeJoin(stackRoot, base, 'outputs') : stackRoot;
|
|
256
|
+
}
|
|
257
|
+
catch {
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
if (stop === -1) {
|
|
261
|
+
// A literal file path: stat it directly.
|
|
262
|
+
try {
|
|
263
|
+
const literal = safeJoin(stackRoot, parts.join('/'), 'outputs');
|
|
264
|
+
const st = lstatSync(literal);
|
|
265
|
+
if (st.isFile())
|
|
266
|
+
seen.set(parts.join('/'), { full: literal, size: st.size, mtimeMs: st.mtimeMs });
|
|
267
|
+
else if (st.isDirectory())
|
|
268
|
+
await walk(join(stackRoot, parts.join('/')), parts.join('/'), true); // a directory: everything under it
|
|
269
|
+
}
|
|
270
|
+
catch {
|
|
271
|
+
/* absent */
|
|
272
|
+
}
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
await walk(start, base);
|
|
276
|
+
}
|
|
277
|
+
const entries = [...seen].sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
|
|
278
|
+
// `content` (decision 0038): a build tool that rewrites identical files
|
|
279
|
+
// (the Angular CLI does) moves every mtime and would restart the service
|
|
280
|
+
// for nothing; hashing what is IN the files ignores that. Streamed, a few
|
|
281
|
+
// at a time, so a large bundle never blocks the daemon's event loop.
|
|
282
|
+
const prints = compare === 'stat'
|
|
283
|
+
? entries.map(([, st]) => `${st.size}:${st.mtimeMs}`)
|
|
284
|
+
: await mapLimit(entries, 8, async ([, st]) => `${st.size}:${(await hashFile(st.full)) ?? 'unreadable'}`);
|
|
285
|
+
return entries.map(([f], i) => `${f}:${prints[i]}`).join('\n');
|
|
286
|
+
}
|
|
287
|
+
/** sha256 of a file, streamed; null when it cannot be read. */
|
|
288
|
+
export function hashFile(path) {
|
|
289
|
+
return new Promise((resolve) => {
|
|
290
|
+
const hash = createHash('sha256');
|
|
291
|
+
const stream = createReadStream(path);
|
|
292
|
+
stream.on('data', (chunk) => hash.update(chunk));
|
|
293
|
+
stream.on('end', () => resolve(hash.digest('hex')));
|
|
294
|
+
stream.on('error', () => resolve(null));
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
async function mapLimit(items, limit, fn) {
|
|
298
|
+
const out = new Array(items.length);
|
|
299
|
+
let next = 0;
|
|
300
|
+
const worker = async () => {
|
|
301
|
+
while (next < items.length) {
|
|
302
|
+
const i = next++;
|
|
303
|
+
out[i] = await fn(items[i]);
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker));
|
|
307
|
+
return out;
|
|
308
|
+
}
|
|
309
|
+
//# sourceMappingURL=worktree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worktree.js","sourceRoot":"","sources":["../../src/core/worktree.ts"],"names":[],"mappings":"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"}
|