@geonosis/doctor 2.12.0 → 2.13.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/CHANGELOG.md +13 -0
- package/bin/geonosis-doctor.mjs +6 -191
- package/dist/{chunk-VWMNCBDP.js → chunk-IJXJ2EF4.js} +77 -172
- package/dist/doctor-cli.js +6 -73
- package/dist/index.d.ts +4 -31
- package/dist/index.js +1 -3
- package/package.json +4 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @geonosis/doctor
|
|
2
2
|
|
|
3
|
+
## 2.13.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [f422aea]
|
|
8
|
+
- Updated dependencies [f422aea]
|
|
9
|
+
- Updated dependencies [f422aea]
|
|
10
|
+
- Updated dependencies [f422aea]
|
|
11
|
+
- Updated dependencies [ae995df]
|
|
12
|
+
- @geonosis/ratchet@2.13.0
|
|
13
|
+
- @geonosis/release@2.13.0
|
|
14
|
+
- @geonosis/lint-parity@2.13.0
|
|
15
|
+
|
|
3
16
|
## 2.12.0
|
|
4
17
|
|
|
5
18
|
### Patch Changes
|
package/bin/geonosis-doctor.mjs
CHANGED
|
@@ -1,196 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
// than src answered for a fix it did not carry once (#62); the published package ships no src, so
|
|
6
|
-
// there the check is skipped.
|
|
7
|
-
import { createHash } from 'node:crypto'
|
|
8
|
-
import {
|
|
9
|
-
existsSync,
|
|
10
|
-
mkdirSync,
|
|
11
|
-
readdirSync,
|
|
12
|
-
readFileSync,
|
|
13
|
-
realpathSync,
|
|
14
|
-
statSync,
|
|
15
|
-
writeFileSync
|
|
16
|
-
} from 'node:fs'
|
|
17
|
-
import { dirname, join } from 'node:path'
|
|
18
|
-
import { fileURLToPath } from 'node:url'
|
|
2
|
+
// Generated by pnpm generate:shims. In the kit's own tree src/ sits beside dist/, and a dist older
|
|
3
|
+
// than src once answered for a fix it did not carry (#62); a published package ships no src/.
|
|
4
|
+
import { existsSync } from 'node:fs'
|
|
19
5
|
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
// not gone stale (#399).
|
|
24
|
-
const NOT_SOURCE = (name) =>
|
|
25
|
-
name.endsWith('.test.ts') ||
|
|
26
|
-
name.endsWith('.test.tsx') ||
|
|
27
|
-
name === '__tests__' ||
|
|
28
|
-
name === '__fixtures__'
|
|
29
|
-
const newest = (dir, skip) =>
|
|
30
|
-
existsSync(dir)
|
|
31
|
-
? readdirSync(dir, { withFileTypes: true }).reduce((most, entry) => {
|
|
32
|
-
if (skip !== undefined && skip(entry.name)) return most
|
|
33
|
-
const at = join(dir, entry.name)
|
|
34
|
-
return Math.max(most, entry.isDirectory() ? newest(at, skip) : statSync(at).mtimeMs)
|
|
35
|
-
}, 0)
|
|
36
|
-
: 0
|
|
37
|
-
const digestOf = (dir, hash) => {
|
|
38
|
-
for (const name of readdirSync(dir).toSorted()) {
|
|
39
|
-
if (NOT_SOURCE(name)) continue
|
|
40
|
-
const at = join(dir, name)
|
|
41
|
-
if (statSync(at).isDirectory()) {
|
|
42
|
-
digestOf(at, hash)
|
|
43
|
-
continue
|
|
44
|
-
}
|
|
45
|
-
hash.update(name).update('\0').update(readFileSync(at)).update('\0')
|
|
46
|
-
}
|
|
47
|
-
return hash
|
|
48
|
-
}
|
|
49
|
-
const digest = (...dirs) => {
|
|
50
|
-
const hash = createHash('sha256')
|
|
51
|
-
for (const dir of dirs) if (existsSync(dir)) digestOf(dir, hash)
|
|
52
|
-
return hash.digest('hex')
|
|
53
|
-
}
|
|
54
|
-
// mtime says a source file was WRITTEN, not that it CHANGED. A checkout rewrites it with the same
|
|
55
|
-
// content, and the next `pnpm build` is a cache hit that leaves the current dist exactly as it
|
|
56
|
-
// was — so every bin refused with "run pnpm build", the action that had just changed nothing and
|
|
57
|
-
// would change nothing again. So a digest of the sources is kept from the last run that found
|
|
58
|
-
// dist current: the same digest later is the same sources, whatever their timestamps say, and a
|
|
59
|
-
// different one is the staleness this guard exists for. The newest mtime is kept beside it so a
|
|
60
|
-
// run over untouched sources need not hash them again. The second line is the same record for
|
|
61
|
-
// the dists of the workspace dependencies this build read (row 584, below).
|
|
62
|
-
const recordOf = (pkg) => join(pkg, '.turbo', 'geonosis-src-digest')
|
|
63
|
-
const recorded = (pkg) => {
|
|
64
|
-
try {
|
|
65
|
-
const [first = '', second = ''] = readFileSync(recordOf(pkg), 'utf8').split('\n')
|
|
66
|
-
const [seen = '', sum = ''] = first.trim().split(' ')
|
|
67
|
-
const [depsSeen = '', depsSum = ''] = second.trim().split(' ')
|
|
68
|
-
return { depsSeen: Number(depsSeen), depsSum, seen: Number(seen), sum }
|
|
69
|
-
} catch {
|
|
70
|
-
return { depsSeen: 0, depsSum: '', seen: 0, sum: '' }
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
const remember = (pkg, known) => {
|
|
74
|
-
try {
|
|
75
|
-
mkdirSync(dirname(recordOf(pkg)), { recursive: true })
|
|
76
|
-
writeFileSync(recordOf(pkg), `${known.seen} ${known.sum}\n${known.depsSeen} ${known.depsSum}\n`)
|
|
77
|
-
} catch {
|
|
78
|
-
// A record nothing could write costs one more hash next time, never a wrong answer.
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
// The refusal names a package for turbo's --filter. A manifest that cannot be read costs the
|
|
82
|
-
// name, never the decision: the first digest shim threw here — exit 1 and a node:fs frame — over a
|
|
83
|
-
// tree with no package.json, and a guard that throws neither answers nor refuses.
|
|
84
|
-
const manifestOf = (pkg) => {
|
|
85
|
-
try {
|
|
86
|
-
return JSON.parse(readFileSync(join(pkg, 'package.json'), 'utf8'))
|
|
87
|
-
} catch {
|
|
88
|
-
return {}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
const refuse = (sentence) => {
|
|
92
|
-
process.stderr.write(`geonosis-doctor: ${sentence}\n`)
|
|
93
|
-
process.exit(2)
|
|
94
|
-
}
|
|
95
|
-
// Whether one package's dist reflects its src: 'unbuilt', 'stale' or 'current'. The same reading
|
|
96
|
-
// for this package and for each workspace dependency, off that package's own record.
|
|
97
|
-
const standing = (pkg) => {
|
|
98
|
-
const src = join(pkg, 'src')
|
|
99
|
-
const dist = join(pkg, 'dist')
|
|
100
|
-
if (!existsSync(src)) return 'current'
|
|
101
|
-
const built = newest(dist)
|
|
102
|
-
if (built === 0) return 'unbuilt'
|
|
103
|
-
const written = newest(src, NOT_SOURCE)
|
|
104
|
-
const known = recorded(pkg)
|
|
105
|
-
if (written > built) {
|
|
106
|
-
const sum = known.seen === written ? known.sum : digest(src)
|
|
107
|
-
if (sum !== known.sum) return 'stale'
|
|
108
|
-
if (known.seen !== written) remember(pkg, { ...known, seen: written, sum })
|
|
109
|
-
} else if (known.seen !== written) {
|
|
110
|
-
remember(pkg, { ...known, seen: written, sum: digest(src) })
|
|
111
|
-
}
|
|
112
|
-
return 'current'
|
|
113
|
-
}
|
|
114
|
-
// Row 584: a dist current against its own src can still have been built against an OLDER
|
|
115
|
-
// dependency's dist — literally, when tsup inlined a workspace devDependency into it — and a
|
|
116
|
-
// dependency whose src has moved past its dist is read by this dist stale, inlined or imported.
|
|
117
|
-
// So every workspace dependency this manifest names (`workspace:`, resolved through the link pnpm
|
|
118
|
-
// made, transitively) is held to the same reading as this package, and this dist must not be
|
|
119
|
-
// older than any of theirs — by content: a dependency rebuilt to the same bytes is the same build,
|
|
120
|
-
// so the `--force --filter` escape below cannot poison the tree it was run in.
|
|
121
|
-
const workspaceDependencies = (pkg, found = new Map()) => {
|
|
122
|
-
const manifest = manifestOf(pkg)
|
|
123
|
-
for (const block of [
|
|
124
|
-
manifest.dependencies,
|
|
125
|
-
manifest.devDependencies,
|
|
126
|
-
manifest.optionalDependencies,
|
|
127
|
-
manifest.peerDependencies
|
|
128
|
-
]) {
|
|
129
|
-
for (const [name, range] of Object.entries(block ?? {})) {
|
|
130
|
-
if (typeof range !== 'string' || !range.startsWith('workspace:')) continue
|
|
131
|
-
let dir
|
|
132
|
-
try {
|
|
133
|
-
dir = realpathSync(join(pkg, 'node_modules', name))
|
|
134
|
-
} catch {
|
|
135
|
-
continue // Not linked: the import will say so far more loudly than this guard can.
|
|
136
|
-
}
|
|
137
|
-
if (found.has(dir)) continue
|
|
138
|
-
found.set(dir, name)
|
|
139
|
-
workspaceDependencies(dir, found)
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
return found
|
|
143
|
-
}
|
|
144
|
-
// `newest` answers 0 for a directory that is NOT THERE and for one that is merely old, so the
|
|
145
|
-
// two conditions were one comparison and one sentence — and the staleness sentence was printed
|
|
146
|
-
// over trees that had never been built at all, blocking a turn in a fresh git worktree, which
|
|
147
|
-
// has no dist because dist is not committed (#357). Only the second is evidence of a mistake.
|
|
148
|
-
if (existsSync(join(own, 'src'))) {
|
|
149
|
-
const built = newest(join(own, 'dist'))
|
|
150
|
-
if (built === 0) {
|
|
151
|
-
refuse(
|
|
152
|
-
'this tree has never been built — run pnpm build. No dist here at all, which is what a fresh clone or a linked git worktree starts with; nothing is stale.'
|
|
153
|
-
)
|
|
154
|
-
}
|
|
155
|
-
const name = manifestOf(own).name ?? '<this package>'
|
|
156
|
-
if (standing(own) === 'stale') {
|
|
157
|
-
refuse(
|
|
158
|
-
`dist is older than src — run pnpm build before trusting this bin. If that build just said FULL TURBO, the cache hit left a current dist with its old timestamp and this bin had not yet recorded the sources it was current for: pnpm build --force --filter=${name} rebuilds this one package and clears it.`
|
|
159
|
-
)
|
|
160
|
-
}
|
|
161
|
-
const dependencies = workspaceDependencies(own)
|
|
162
|
-
const dists = new Map()
|
|
163
|
-
for (const [dir, dependency] of dependencies) {
|
|
164
|
-
const state = standing(dir)
|
|
165
|
-
if (state === 'unbuilt') {
|
|
166
|
-
refuse(
|
|
167
|
-
`${dependency}, which this dist reads, has never been built — run pnpm build. No dist there at all; nothing is stale.`
|
|
168
|
-
)
|
|
169
|
-
}
|
|
170
|
-
if (state === 'stale') {
|
|
171
|
-
refuse(
|
|
172
|
-
`${dependency}'s dist is older than its src, and this dist reads that dist — run pnpm build before trusting this bin. If that build just said FULL TURBO, the cache hit left that dist current with its old timestamp and nothing had yet recorded the sources it was current for: pnpm build --force --filter=${dependency} rebuilds that one package and clears it.`
|
|
173
|
-
)
|
|
174
|
-
}
|
|
175
|
-
dists.set(join(dir, 'dist'), dependency)
|
|
176
|
-
}
|
|
177
|
-
if (dists.size > 0) {
|
|
178
|
-
const known = recorded(own)
|
|
179
|
-
const theirs = [...dists.keys()].toSorted()
|
|
180
|
-
const depsNewest = theirs.reduce((most, dist) => Math.max(most, newest(dist)), 0)
|
|
181
|
-
if (depsNewest > built) {
|
|
182
|
-
const depsSum = known.depsSeen === depsNewest ? known.depsSum : digest(...theirs)
|
|
183
|
-
if (depsSum !== known.depsSum) {
|
|
184
|
-
const newer = theirs.filter((dist) => newest(dist) > built).map((dist) => dists.get(dist))
|
|
185
|
-
refuse(
|
|
186
|
-
`dist was built before the current dist of ${newer.join(', ')}, which it reads — run pnpm build before trusting this bin. If that build just said FULL TURBO, the dependency was rebuilt alone and this bin had not yet recorded the build it was current against: pnpm build --force --filter=${name} rebuilds this one package against it.`
|
|
187
|
-
)
|
|
188
|
-
}
|
|
189
|
-
if (known.depsSeen !== depsNewest) remember(own, { ...known, depsSeen: depsNewest, depsSum })
|
|
190
|
-
} else if (known.depsSeen !== depsNewest) {
|
|
191
|
-
remember(own, { ...known, depsSeen: depsNewest, depsSum: digest(...theirs) })
|
|
192
|
-
}
|
|
193
|
-
}
|
|
6
|
+
if (existsSync(new URL('../src', import.meta.url))) {
|
|
7
|
+
const { guardBuild } = await import('../../../tooling/bin-guard.mjs')
|
|
8
|
+
guardBuild(new URL('..', import.meta.url), 'geonosis-doctor')
|
|
194
9
|
}
|
|
195
10
|
|
|
196
11
|
await import('../dist/doctor-cli.js')
|
|
@@ -253,153 +253,58 @@ var checkBaseline = ({ ref, root }) => {
|
|
|
253
253
|
};
|
|
254
254
|
|
|
255
255
|
// src/deployed.ts
|
|
256
|
-
import { existsSync as existsSync2
|
|
256
|
+
import { existsSync as existsSync2 } from "fs";
|
|
257
257
|
import { join as join4 } from "path";
|
|
258
|
-
|
|
259
|
-
var
|
|
258
|
+
import { DEPLOYED_FILE, NOTHING_SAYS, runDeployed } from "@geonosis/release";
|
|
259
|
+
var finding2 = findingMaker("deployed");
|
|
260
260
|
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
261
|
-
var
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
let out = "";
|
|
266
|
-
for (let index = 0; index < source.length; index += 1) {
|
|
267
|
-
const char = source[index] ?? "";
|
|
268
|
-
if (char === '"') {
|
|
269
|
-
const start = index;
|
|
270
|
-
index += 1;
|
|
271
|
-
for (; index < source.length; index += 1) {
|
|
272
|
-
if (source[index] === "\\") {
|
|
273
|
-
index += 1;
|
|
274
|
-
continue;
|
|
275
|
-
}
|
|
276
|
-
if (source[index] === '"') break;
|
|
277
|
-
}
|
|
278
|
-
out += source.slice(start, index + 1);
|
|
279
|
-
continue;
|
|
280
|
-
}
|
|
281
|
-
if (char === "/" && source[index + 1] === "/") {
|
|
282
|
-
const end = source.indexOf("\n", index);
|
|
283
|
-
index = end === -1 ? source.length : end - 1;
|
|
284
|
-
continue;
|
|
285
|
-
}
|
|
286
|
-
if (char === "/" && source[index + 1] === "*") {
|
|
287
|
-
const end = source.indexOf("*/", index + 2);
|
|
288
|
-
index = end === -1 ? source.length : end + 1;
|
|
289
|
-
continue;
|
|
290
|
-
}
|
|
291
|
-
out += char;
|
|
292
|
-
}
|
|
293
|
-
return JSON.parse(out.replaceAll(/,(\s*[\]}])/g, "$1"));
|
|
294
|
-
};
|
|
295
|
-
var BINDING_LISTS = [
|
|
296
|
-
"ai",
|
|
297
|
-
"analytics_engine_datasets",
|
|
298
|
-
"browser",
|
|
299
|
-
"d1_databases",
|
|
300
|
-
"dispatch_namespaces",
|
|
301
|
-
"durable_objects",
|
|
302
|
-
"hyperdrive",
|
|
303
|
-
"kv_namespaces",
|
|
304
|
-
"mtls_certificates",
|
|
305
|
-
"queues",
|
|
306
|
-
"r2_buckets",
|
|
307
|
-
"send_email",
|
|
308
|
-
"services",
|
|
309
|
-
"vectorize",
|
|
310
|
-
"version_metadata",
|
|
311
|
-
"workflows"
|
|
312
|
-
];
|
|
313
|
-
var bindingsOf = (found) => {
|
|
314
|
-
if (Array.isArray(found)) return found.flatMap(bindingsOf);
|
|
315
|
-
if (!isRecord(found)) return [];
|
|
316
|
-
const named2 = found["binding"] ?? found["name"];
|
|
317
|
-
return [
|
|
318
|
-
...typeof named2 === "string" ? [named2] : [],
|
|
319
|
-
...Object.entries(found).filter(([key]) => key === "bindings" || key === "producers" || key === "consumers").flatMap(([, one]) => bindingsOf(one))
|
|
320
|
-
];
|
|
321
|
-
};
|
|
322
|
-
var declaredIn = (config) => {
|
|
323
|
-
const triggers = config["triggers"];
|
|
324
|
-
const one = config["route"];
|
|
325
|
-
const listed = [
|
|
326
|
-
...Array.isArray(config["routes"]) ? config["routes"] : [],
|
|
327
|
-
...typeof one === "string" ? [one] : []
|
|
328
|
-
];
|
|
329
|
-
return {
|
|
330
|
-
bindings: BINDING_LISTS.flatMap((key) => bindingsOf(config[key])),
|
|
331
|
-
crons: isRecord(triggers) ? listOf(triggers["crons"]) : [],
|
|
332
|
-
routes: listed.map((route) => isRecord(route) ? route["pattern"] : route).filter((pattern) => typeof pattern === "string")
|
|
333
|
-
};
|
|
334
|
-
};
|
|
335
|
-
var differences = (kind, declared, deployed) => {
|
|
336
|
-
const missing = declared.filter((name) => !deployed.includes(name));
|
|
337
|
-
const extra = deployed.filter((name) => !declared.includes(name));
|
|
338
|
-
return [
|
|
339
|
-
...missing.length === 0 ? [] : [`declared but not deployed ${kind}: ${missing.join(", ")}`],
|
|
340
|
-
...extra.length === 0 ? [] : [`deployed but not declared ${kind}: ${extra.join(", ")}`]
|
|
341
|
-
];
|
|
342
|
-
};
|
|
261
|
+
var declaresNothing = (release) => !isRecord(release) || ["wrangler", "secrets"].every((key) => {
|
|
262
|
+
const listed = release[key];
|
|
263
|
+
return !Array.isArray(listed) || listed.length === 0;
|
|
264
|
+
});
|
|
343
265
|
var checkDeployed = ({ root }) => {
|
|
344
266
|
const read = readGeonosisFile(root);
|
|
345
267
|
if (read.kind === "absent") return [];
|
|
346
268
|
if (read.kind === "unreadable") return [unreadableGeonosis("deployed", read.error)];
|
|
347
|
-
|
|
348
|
-
const configs = listOf(release["wrangler"]);
|
|
349
|
-
const secrets = listOf(release["secrets"]);
|
|
350
|
-
if (configs.length === 0 && secrets.length === 0) {
|
|
269
|
+
if (declaresNothing(read.config["release"])) {
|
|
351
270
|
return [
|
|
352
271
|
finding2(
|
|
353
|
-
"SKIP",
|
|
354
272
|
"geonosis.json",
|
|
273
|
+
"SKIP",
|
|
355
274
|
"release.wrangler and release.secrets are both empty \u2014 this repo has declared nothing a deployment is supposed to carry"
|
|
356
275
|
)
|
|
357
276
|
];
|
|
358
277
|
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
for (const relative of configs) {
|
|
362
|
-
let parsed;
|
|
363
|
-
try {
|
|
364
|
-
parsed = parseJsonc(readFileSync3(join4(root, relative), "utf8"));
|
|
365
|
-
} catch (error) {
|
|
366
|
-
return [finding2("FAIL", relative, `could not be read: ${error.message}`)];
|
|
367
|
-
}
|
|
368
|
-
if (!isRecord(parsed)) return [finding2("FAIL", relative, "is not a wrangler configuration")];
|
|
369
|
-
const environments = parsed["env"];
|
|
370
|
-
const block = env !== void 0 && isRecord(environments) && isRecord(environments[env]) ? environments[env] : parsed;
|
|
371
|
-
const one = declaredIn(block);
|
|
372
|
-
declared.bindings.push(...one.bindings);
|
|
373
|
-
declared.crons.push(...one.crons);
|
|
374
|
-
declared.routes.push(...one.routes);
|
|
375
|
-
}
|
|
376
|
-
const path = join4(root, DEPLOYED_FILE);
|
|
377
|
-
if (!existsSync2(path)) return [finding2("SKIP", DEPLOYED_FILE, NOT_WRITTEN)];
|
|
378
|
-
let reported;
|
|
278
|
+
if (!existsSync2(join4(root, DEPLOYED_FILE))) return [finding2(DEPLOYED_FILE, "SKIP", NOTHING_SAYS)];
|
|
279
|
+
let report;
|
|
379
280
|
try {
|
|
380
|
-
|
|
281
|
+
report = runDeployed({ root });
|
|
381
282
|
} catch (error) {
|
|
382
|
-
return [finding2("FAIL",
|
|
383
|
-
}
|
|
384
|
-
if (
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
283
|
+
return [finding2(DEPLOYED_FILE, "FAIL", error instanceof Error ? error.message : String(error))];
|
|
284
|
+
}
|
|
285
|
+
if (report.ok) {
|
|
286
|
+
return [
|
|
287
|
+
finding2(
|
|
288
|
+
DEPLOYED_FILE,
|
|
289
|
+
"OK",
|
|
290
|
+
`what the tree declares is what the pipeline reported${report.at === void 0 ? "" : ` at ${report.at}`}`
|
|
291
|
+
)
|
|
292
|
+
];
|
|
293
|
+
}
|
|
392
294
|
return [
|
|
393
|
-
|
|
394
|
-
"OK",
|
|
295
|
+
finding2(
|
|
395
296
|
DEPLOYED_FILE,
|
|
396
|
-
|
|
397
|
-
|
|
297
|
+
"FAIL",
|
|
298
|
+
report.drift.flatMap(({ extra, kind, missing }) => [
|
|
299
|
+
...missing.length === 0 ? [] : [`declared but not deployed ${kind}: ${missing.join(", ")}`],
|
|
300
|
+
...extra.length === 0 ? [] : [`deployed but not declared ${kind}: ${extra.join(", ")}`]
|
|
301
|
+
]).join("; ")
|
|
302
|
+
)
|
|
398
303
|
];
|
|
399
304
|
};
|
|
400
305
|
|
|
401
306
|
// src/resolve.ts
|
|
402
|
-
import { existsSync as existsSync3, readFileSync as
|
|
307
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, realpathSync as realpathSync2 } from "fs";
|
|
403
308
|
import { createRequire } from "module";
|
|
404
309
|
import { dirname, join as join5 } from "path";
|
|
405
310
|
import { pathToFileURL } from "url";
|
|
@@ -460,7 +365,7 @@ var packageDirOf = (entry, name) => {
|
|
|
460
365
|
const manifest = join5(dir, "package.json");
|
|
461
366
|
if (existsSync3(manifest)) {
|
|
462
367
|
try {
|
|
463
|
-
const parsed = JSON.parse(
|
|
368
|
+
const parsed = JSON.parse(readFileSync3(manifest, "utf8"));
|
|
464
369
|
if (parsed.name === name) return dir;
|
|
465
370
|
} catch {
|
|
466
371
|
}
|
|
@@ -544,17 +449,17 @@ var real2 = (path) => {
|
|
|
544
449
|
var relativeToRoot = (root, path) => relativePath(real2(root), real2(path));
|
|
545
450
|
|
|
546
451
|
// src/group.ts
|
|
547
|
-
import { readFileSync as
|
|
452
|
+
import { readFileSync as readFileSync6 } from "fs";
|
|
548
453
|
import { join as join8 } from "path";
|
|
549
454
|
|
|
550
455
|
// src/callers.ts
|
|
551
|
-
import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as
|
|
456
|
+
import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync4 } from "fs";
|
|
552
457
|
import { join as join6, resolve } from "path";
|
|
553
458
|
var WORKFLOWS = ".github/workflows";
|
|
554
459
|
var A_WORKFLOW = (name) => name.endsWith(".yml") || name.endsWith(".yaml");
|
|
555
460
|
var readOr = (path) => {
|
|
556
461
|
try {
|
|
557
|
-
return
|
|
462
|
+
return readFileSync4(path, "utf8");
|
|
558
463
|
} catch {
|
|
559
464
|
return "";
|
|
560
465
|
}
|
|
@@ -617,7 +522,7 @@ var scriptFilesNamedBy = (root, lines) => {
|
|
|
617
522
|
};
|
|
618
523
|
|
|
619
524
|
// src/hooks.ts
|
|
620
|
-
import { existsSync as existsSync5, readdirSync as readdirSync3, readFileSync as
|
|
525
|
+
import { existsSync as existsSync5, readdirSync as readdirSync3, readFileSync as readFileSync5 } from "fs";
|
|
621
526
|
import { join as join7 } from "path";
|
|
622
527
|
var HOOK_FILES = ["lefthook.yml", "lefthook.yaml", ".lefthook.yml", ".lefthook.yaml"];
|
|
623
528
|
var HOOK_DIRS = [".husky", ".githooks"];
|
|
@@ -627,7 +532,7 @@ var hookLines = (root) => {
|
|
|
627
532
|
const found = [];
|
|
628
533
|
const read = (path) => {
|
|
629
534
|
const at = relativePath(root, path);
|
|
630
|
-
for (const line of
|
|
535
|
+
for (const line of readFileSync5(path, "utf8").split("\n")) {
|
|
631
536
|
if (line.trim() !== "") found.push({ at, line });
|
|
632
537
|
}
|
|
633
538
|
};
|
|
@@ -708,7 +613,7 @@ var versionOf = (dir, name, root) => {
|
|
|
708
613
|
}
|
|
709
614
|
try {
|
|
710
615
|
const manifest = JSON.parse(
|
|
711
|
-
|
|
616
|
+
readFileSync6(join8(packageDirOf(entry, name), "package.json"), "utf8")
|
|
712
617
|
);
|
|
713
618
|
return typeof manifest.version === "string" ? { kind: "read", version: manifest.version } : { kind: "unreadable", why: "its package.json declares no version" };
|
|
714
619
|
} catch (error) {
|
|
@@ -740,7 +645,7 @@ var DOORS = ["@geonosis/cli", "geonosis"];
|
|
|
740
645
|
var manifestOf = (root, name) => {
|
|
741
646
|
try {
|
|
742
647
|
return JSON.parse(
|
|
743
|
-
|
|
648
|
+
readFileSync6(join8(packageDirOf(resolveFrom(root, name, root), name), "package.json"), "utf8")
|
|
744
649
|
);
|
|
745
650
|
} catch {
|
|
746
651
|
return void 0;
|
|
@@ -750,7 +655,7 @@ var binNamesOf = (manifest, name) => {
|
|
|
750
655
|
const declared = manifest?.bin;
|
|
751
656
|
return typeof declared === "string" ? [name.replace(/^@[^/]+\//, "")] : Object.keys(declared ?? {});
|
|
752
657
|
};
|
|
753
|
-
var
|
|
658
|
+
var declaredIn = (manifest) => [...dependencyNamesOf(manifest)];
|
|
754
659
|
var calls = (lines, bin) => new RegExp(`(?:^|[\\s;&|(/'"\`])${bin}(?:[\\s;&|)'"\`,\\]]|$)`).test(lines);
|
|
755
660
|
var workspaceCaller = (workspaces, bins) => {
|
|
756
661
|
for (const workspace of workspaces) {
|
|
@@ -764,7 +669,7 @@ var workspaceCaller = (workspaces, bins) => {
|
|
|
764
669
|
var rootDeclarations = (root, workspaces) => {
|
|
765
670
|
const here = workspaces.find((one) => one.relative === "");
|
|
766
671
|
if (here === void 0) return [];
|
|
767
|
-
const declared =
|
|
672
|
+
const declared = declaredIn(here.manifest);
|
|
768
673
|
const door = DOORS.find((name) => declared.includes(name));
|
|
769
674
|
if (door === void 0) return [];
|
|
770
675
|
const owned = new Set(
|
|
@@ -893,7 +798,7 @@ var checkGroup = ({
|
|
|
893
798
|
};
|
|
894
799
|
|
|
895
800
|
// src/drift.ts
|
|
896
|
-
import { closeSync, existsSync as existsSync6, openSync, readdirSync as readdirSync4, readFileSync as
|
|
801
|
+
import { closeSync, existsSync as existsSync6, openSync, readdirSync as readdirSync4, readFileSync as readFileSync7, readSync } from "fs";
|
|
897
802
|
import { homedir } from "os";
|
|
898
803
|
import { join as join9, sep as sep3 } from "path";
|
|
899
804
|
|
|
@@ -976,7 +881,7 @@ var ci = (root) => {
|
|
|
976
881
|
}
|
|
977
882
|
return filesUnder(dir, (name) => name.endsWith(".yml") || name.endsWith(".yaml")).map((path) => {
|
|
978
883
|
const at = relativePath(root, path);
|
|
979
|
-
return SWITCHED_OFF.test(
|
|
884
|
+
return SWITCHED_OFF.test(readFileSync7(path, "utf8")) ? finding4(
|
|
980
885
|
at,
|
|
981
886
|
"FAIL",
|
|
982
887
|
"a job or step here is switched off by a condition that can never be true \u2014 every gate downstream of it reports green having run nothing"
|
|
@@ -1170,7 +1075,7 @@ var law = (root, config) => {
|
|
|
1170
1075
|
if (!existsSync6(path)) {
|
|
1171
1076
|
return [finding4(file, "SKIP", "there is no law file here to measure")];
|
|
1172
1077
|
}
|
|
1173
|
-
const source =
|
|
1078
|
+
const source = readFileSync7(path, "utf8");
|
|
1174
1079
|
const lines = source.split("\n").length - (source.endsWith("\n") ? 1 : 0);
|
|
1175
1080
|
if (typeof declared.maxLines !== "number") {
|
|
1176
1081
|
return [
|
|
@@ -1194,7 +1099,7 @@ var KIT_PLUGIN = "geonosis";
|
|
|
1194
1099
|
var enablesKit = (path) => {
|
|
1195
1100
|
if (!existsSync6(path)) return false;
|
|
1196
1101
|
try {
|
|
1197
|
-
const parsed = JSON.parse(
|
|
1102
|
+
const parsed = JSON.parse(readFileSync7(path, "utf8"));
|
|
1198
1103
|
const enabled = parsed.enabledPlugins;
|
|
1199
1104
|
if (typeof enabled !== "object" || enabled === null) return false;
|
|
1200
1105
|
return Object.entries(enabled).some(
|
|
@@ -1230,7 +1135,7 @@ var ALLOW_BUILDS_KEY = "allowBuilds";
|
|
|
1230
1135
|
var allowedBuilds = (root) => {
|
|
1231
1136
|
const path = join9(root, WORKSPACE_YAML);
|
|
1232
1137
|
if (!existsSync6(path)) return void 0;
|
|
1233
|
-
const lines =
|
|
1138
|
+
const lines = readFileSync7(path, "utf8").split("\n");
|
|
1234
1139
|
const at = lines.findIndex((line) => new RegExp(`^${ALLOW_BUILDS_KEY}\\s*:`).test(line));
|
|
1235
1140
|
if (at < 0) return void 0;
|
|
1236
1141
|
const names = [];
|
|
@@ -1387,7 +1292,7 @@ var composedHere = (root, workspaces) => {
|
|
|
1387
1292
|
for (const path of filesUnder(root, (name) => SOURCE_FILE.test(name))) {
|
|
1388
1293
|
let body;
|
|
1389
1294
|
try {
|
|
1390
|
-
body =
|
|
1295
|
+
body = readFileSync7(path, "utf8");
|
|
1391
1296
|
} catch {
|
|
1392
1297
|
continue;
|
|
1393
1298
|
}
|
|
@@ -1422,7 +1327,7 @@ var doorBringing = (root, name, declared) => DOORS.filter((door) => declared.has
|
|
|
1422
1327
|
var dependenciesOf = (root, name) => {
|
|
1423
1328
|
try {
|
|
1424
1329
|
return JSON.parse(
|
|
1425
|
-
|
|
1330
|
+
readFileSync7(
|
|
1426
1331
|
join9(packageDirOf(resolveFrom(root, name, root), name), "package.json"),
|
|
1427
1332
|
"utf8"
|
|
1428
1333
|
)
|
|
@@ -1567,7 +1472,7 @@ var pluginDirs = (root) => {
|
|
|
1567
1472
|
const governs = layerOver(layers, root, relative);
|
|
1568
1473
|
if (governs === void 0) continue;
|
|
1569
1474
|
const key = governs.registry.join(", ");
|
|
1570
|
-
const registry = source.get(key) ?? governs.registry.map((half) =>
|
|
1475
|
+
const registry = source.get(key) ?? governs.registry.map((half) => readFileSync7(join9(root, half), "utf8")).join("\n");
|
|
1571
1476
|
source.set(key, registry);
|
|
1572
1477
|
const hasManifest = governs.manifests.some((name) => existsSync6(join9(at, entry.name, name)));
|
|
1573
1478
|
if (!hasManifest || registry.includes(entry.name)) continue;
|
|
@@ -1597,7 +1502,7 @@ var pnpmMajorOf = (manifest) => {
|
|
|
1597
1502
|
var pnpmBlock = (root) => {
|
|
1598
1503
|
let manifest;
|
|
1599
1504
|
try {
|
|
1600
|
-
manifest = JSON.parse(
|
|
1505
|
+
manifest = JSON.parse(readFileSync7(join9(root, "package.json"), "utf8"));
|
|
1601
1506
|
} catch {
|
|
1602
1507
|
return [];
|
|
1603
1508
|
}
|
|
@@ -1635,7 +1540,7 @@ var HOIST_REPAIR = "rm -rf node_modules/.modules.yaml node_modules/.pnpm-workspa
|
|
|
1635
1540
|
var hoistPatterns = (root) => {
|
|
1636
1541
|
const path = join9(root, WORKSPACE_YAML);
|
|
1637
1542
|
if (!existsSync6(path)) return void 0;
|
|
1638
|
-
const lines =
|
|
1543
|
+
const lines = readFileSync7(path, "utf8").split("\n");
|
|
1639
1544
|
const at = lines.findIndex((line) => new RegExp(`^${HOIST_KEY}\\s*:`).test(line));
|
|
1640
1545
|
if (at < 0) return void 0;
|
|
1641
1546
|
const patterns = [];
|
|
@@ -1759,7 +1664,7 @@ import {
|
|
|
1759
1664
|
existsSync as existsSync7,
|
|
1760
1665
|
mkdirSync,
|
|
1761
1666
|
mkdtempSync,
|
|
1762
|
-
readFileSync as
|
|
1667
|
+
readFileSync as readFileSync8,
|
|
1763
1668
|
rmSync,
|
|
1764
1669
|
writeFileSync
|
|
1765
1670
|
} from "fs";
|
|
@@ -1938,7 +1843,7 @@ var throughProbes = ({
|
|
|
1938
1843
|
refused.set(rule, String(error.message));
|
|
1939
1844
|
}
|
|
1940
1845
|
}
|
|
1941
|
-
const probeConfig = JSON.parse(
|
|
1846
|
+
const probeConfig = JSON.parse(readFileSync8(config.path, "utf8"));
|
|
1942
1847
|
const configDir = dirname2(config.path);
|
|
1943
1848
|
probeConfig.jsPlugins = (probeConfig.jsPlugins ?? []).map(
|
|
1944
1849
|
(spec) => spec.startsWith(".") || spec.startsWith("/") ? resolve2(configDir, spec) : packageDirOf(resolveFrom(configDir, spec, root), spec)
|
|
@@ -2098,15 +2003,16 @@ var checkExercised = async ({
|
|
|
2098
2003
|
};
|
|
2099
2004
|
|
|
2100
2005
|
// src/envelope.ts
|
|
2101
|
-
import { existsSync as existsSync8, readdirSync as readdirSync5, readFileSync as
|
|
2006
|
+
import { existsSync as existsSync8, readdirSync as readdirSync5, readFileSync as readFileSync9 } from "fs";
|
|
2102
2007
|
import { join as join11 } from "path";
|
|
2103
|
-
|
|
2008
|
+
import { ENVELOPES_DIR } from "@geonosis/ratchet";
|
|
2009
|
+
import { ENVELOPES_DIR as ENVELOPES_DIR2 } from "@geonosis/ratchet";
|
|
2104
2010
|
var THIS_TOOL = "doctor";
|
|
2105
2011
|
var OWN = `${THIS_TOOL}.json`;
|
|
2106
2012
|
var NO_ENVELOPES = "no .geonosis/envelopes/*.json \u2014 the tools write one per run, so an absent envelope is a run nobody has made here yet, and it is not a balanced one (this run\u2019s own doctor.json is not one of them: it is written after these checks, and balanced at write time)";
|
|
2107
2013
|
var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2108
|
-
var
|
|
2109
|
-
var finding6 = (verdict, subject, message) =>
|
|
2014
|
+
var make = findingMaker("envelope");
|
|
2015
|
+
var finding6 = (verdict, subject, message) => make(subject, verdict, message);
|
|
2110
2016
|
var lengthOf = (value) => Array.isArray(value) ? value.length : void 0;
|
|
2111
2017
|
var NEXT = "Next: re-run the tool that wrote it and read the numbers it prints \u2014 a run that has lost count of its own inputs is a bug in that tool, not in this tree";
|
|
2112
2018
|
var judge = (subject, name, parsed) => {
|
|
@@ -2164,7 +2070,7 @@ var checkEnvelopes = ({ root }) => {
|
|
|
2164
2070
|
const subject = `${ENVELOPES_DIR}/${name}`;
|
|
2165
2071
|
let parsed;
|
|
2166
2072
|
try {
|
|
2167
|
-
parsed = JSON.parse(
|
|
2073
|
+
parsed = JSON.parse(readFileSync9(join11(dir, name), "utf8"));
|
|
2168
2074
|
} catch (error) {
|
|
2169
2075
|
return finding6("FAIL", subject, `is not readable JSON: ${error.message}. ${NEXT}`);
|
|
2170
2076
|
}
|
|
@@ -2173,7 +2079,7 @@ var checkEnvelopes = ({ root }) => {
|
|
|
2173
2079
|
};
|
|
2174
2080
|
|
|
2175
2081
|
// src/exams.ts
|
|
2176
|
-
import { existsSync as existsSync9, readFileSync as
|
|
2082
|
+
import { existsSync as existsSync9, readFileSync as readFileSync10 } from "fs";
|
|
2177
2083
|
import { dirname as dirname3, join as join12 } from "path";
|
|
2178
2084
|
var SCOPE = "@geonosis/";
|
|
2179
2085
|
var EXAM = /(?:^|[^$\w])([$\w]*Conformance)\b/g;
|
|
@@ -2200,7 +2106,7 @@ var declaredBy = (workspaces) => {
|
|
|
2200
2106
|
var declarationsOf = (dir) => {
|
|
2201
2107
|
const at = join12(dir, "package.json");
|
|
2202
2108
|
if (!existsSync9(at)) return "";
|
|
2203
|
-
const manifest = JSON.parse(
|
|
2109
|
+
const manifest = JSON.parse(readFileSync10(at, "utf8"));
|
|
2204
2110
|
const entries = [];
|
|
2205
2111
|
const walk2 = (value) => {
|
|
2206
2112
|
if (typeof value === "string" && value.endsWith(".d.ts")) entries.push(value);
|
|
@@ -2211,7 +2117,7 @@ var declarationsOf = (dir) => {
|
|
|
2211
2117
|
walk2({ exports: manifest["exports"], types: manifest["types"] });
|
|
2212
2118
|
return [...new Set(entries)].map((entry) => {
|
|
2213
2119
|
try {
|
|
2214
|
-
return
|
|
2120
|
+
return readFileSync10(join12(dir, entry), "utf8");
|
|
2215
2121
|
} catch {
|
|
2216
2122
|
return "";
|
|
2217
2123
|
}
|
|
@@ -2237,7 +2143,7 @@ var checkExams = ({
|
|
|
2237
2143
|
root,
|
|
2238
2144
|
workspaces
|
|
2239
2145
|
}) => {
|
|
2240
|
-
const tests = testFilesUnder(root).map((path) => ({ path, source:
|
|
2146
|
+
const tests = testFilesUnder(root).map((path) => ({ path, source: readFileSync10(path, "utf8") }));
|
|
2241
2147
|
const found = [];
|
|
2242
2148
|
for (const { from, name } of declaredBy(workspaces)) {
|
|
2243
2149
|
const exams = examsShippedBy(from, name, root);
|
|
@@ -2263,7 +2169,7 @@ var checkExams = ({
|
|
|
2263
2169
|
};
|
|
2264
2170
|
|
|
2265
2171
|
// src/claude-plugin.ts
|
|
2266
|
-
import { existsSync as existsSync10, readFileSync as
|
|
2172
|
+
import { existsSync as existsSync10, readFileSync as readFileSync11, realpathSync as realpathSync3 } from "fs";
|
|
2267
2173
|
import { homedir as homedir2 } from "os";
|
|
2268
2174
|
import { join as join13, resolve as resolve3 } from "path";
|
|
2269
2175
|
var INSTALLED_PLUGINS = ".claude/plugins/installed_plugins.json";
|
|
@@ -2274,7 +2180,7 @@ var installedPlugins = (home, name) => {
|
|
|
2274
2180
|
if (!existsSync10(at)) return [];
|
|
2275
2181
|
let record;
|
|
2276
2182
|
try {
|
|
2277
|
-
record = JSON.parse(
|
|
2183
|
+
record = JSON.parse(readFileSync11(at, "utf8"));
|
|
2278
2184
|
} catch {
|
|
2279
2185
|
return [];
|
|
2280
2186
|
}
|
|
@@ -2380,7 +2286,7 @@ var checkClaudePlugin = ({
|
|
|
2380
2286
|
};
|
|
2381
2287
|
|
|
2382
2288
|
// src/loaded.ts
|
|
2383
|
-
import { readFileSync as
|
|
2289
|
+
import { readFileSync as readFileSync12 } from "fs";
|
|
2384
2290
|
import { join as join14, sep as sep4 } from "path";
|
|
2385
2291
|
var SCOPE2 = "@geonosis/";
|
|
2386
2292
|
var NOT_THE_SCOPE_DIRECTORY = "Deleting only the scope directory inside that node_modules is a repair no pnpm install undoes \u2014 the tree is then missing the package and every install stays green.";
|
|
@@ -2529,7 +2435,7 @@ var shipperOf = (root, bin) => {
|
|
|
2529
2435
|
const name = bin === "geonosis" ? "@geonosis/cli" : `@geonosis/${bin.replace(/^geonosis-/, "")}`;
|
|
2530
2436
|
try {
|
|
2531
2437
|
const manifest = JSON.parse(
|
|
2532
|
-
|
|
2438
|
+
readFileSync12(join14(packageDirOf(resolveFrom(root, name, root), name), "package.json"), "utf8")
|
|
2533
2439
|
);
|
|
2534
2440
|
const declared = manifest.bin;
|
|
2535
2441
|
return typeof declared === "object" && !Object.hasOwn(declared, bin) ? "" : name;
|
|
@@ -2637,13 +2543,13 @@ var trainVersion = async (root, specifiers) => {
|
|
|
2637
2543
|
};
|
|
2638
2544
|
|
|
2639
2545
|
// src/observability.ts
|
|
2640
|
-
import { readFileSync as
|
|
2546
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
2641
2547
|
import { join as join15 } from "path";
|
|
2642
2548
|
var REACHES_NOTHING = /* @__PURE__ */ new Set(["console", "memory", "noop", "none", "null", "swallowing"]);
|
|
2643
2549
|
var DEFAULT_MAX_AGE_SECONDS = 3600;
|
|
2644
2550
|
var HEAD_TIMEOUT_MS = 3e3;
|
|
2645
|
-
var
|
|
2646
|
-
var finding9 = (verdict, subject, message) =>
|
|
2551
|
+
var make2 = findingMaker("observability");
|
|
2552
|
+
var finding9 = (verdict, subject, message) => make2(subject, verdict, message);
|
|
2647
2553
|
var readGeonosis = (root) => {
|
|
2648
2554
|
const read = readGeonosisFile(root);
|
|
2649
2555
|
if (read.kind === "unreadable") return { error: read.error, present: false };
|
|
@@ -2721,7 +2627,7 @@ var ageFinding = (config, root, now) => {
|
|
|
2721
2627
|
const maxAgeSeconds = typeof config.maxAgeSeconds === "number" && config.maxAgeSeconds > 0 ? config.maxAgeSeconds : DEFAULT_MAX_AGE_SECONDS;
|
|
2722
2628
|
let record;
|
|
2723
2629
|
try {
|
|
2724
|
-
record = JSON.parse(
|
|
2630
|
+
record = JSON.parse(readFileSync13(join15(root, file), "utf8"));
|
|
2725
2631
|
} catch (error) {
|
|
2726
2632
|
return finding9(
|
|
2727
2633
|
"FAIL",
|
|
@@ -2865,7 +2771,7 @@ var checkRequiredOptions = async ({
|
|
|
2865
2771
|
};
|
|
2866
2772
|
|
|
2867
2773
|
// src/runner.ts
|
|
2868
|
-
import { readFileSync as
|
|
2774
|
+
import { readFileSync as readFileSync14 } from "fs";
|
|
2869
2775
|
import { resolve as resolve4 } from "path";
|
|
2870
2776
|
var TEST_FAILURES = "testFailures";
|
|
2871
2777
|
var RUNS_A_RUNNER = /(?:^|[\s;&|(])(?:npx\s+|bunx\s+|pnpm\s+(?:exec\s+)?)?(?:vitest|bun\s+test)(?:\s|$)/;
|
|
@@ -2875,7 +2781,7 @@ var wrapperNamedBy = (script, dir) => {
|
|
|
2875
2781
|
for (const token of script.split(/[\s'"]+/)) {
|
|
2876
2782
|
if (!A_SCRIPT_FILE.test(token)) continue;
|
|
2877
2783
|
try {
|
|
2878
|
-
return { source:
|
|
2784
|
+
return { source: readFileSync14(resolve4(dir, token), "utf8"), where: token };
|
|
2879
2785
|
} catch {
|
|
2880
2786
|
continue;
|
|
2881
2787
|
}
|
|
@@ -3145,7 +3051,7 @@ var checkPathGrants = ({
|
|
|
3145
3051
|
};
|
|
3146
3052
|
|
|
3147
3053
|
// src/rails.ts
|
|
3148
|
-
import { existsSync as existsSync12, readFileSync as
|
|
3054
|
+
import { existsSync as existsSync12, readFileSync as readFileSync15 } from "fs";
|
|
3149
3055
|
import { join as join18 } from "path";
|
|
3150
3056
|
var PROJECT = ".claude/settings.json";
|
|
3151
3057
|
var LOCAL = ".claude/settings.local.json";
|
|
@@ -3169,7 +3075,7 @@ var sourceAt = (path, label) => {
|
|
|
3169
3075
|
if (!existsSync12(path)) return { network: void 0, path: label, unreadable: false };
|
|
3170
3076
|
try {
|
|
3171
3077
|
return {
|
|
3172
|
-
network: networkIn(JSON.parse(
|
|
3078
|
+
network: networkIn(JSON.parse(readFileSync15(path, "utf8"))),
|
|
3173
3079
|
path: label,
|
|
3174
3080
|
unreadable: false
|
|
3175
3081
|
};
|
|
@@ -3198,7 +3104,7 @@ var deniedEgress = (root) => {
|
|
|
3198
3104
|
}
|
|
3199
3105
|
let parsed;
|
|
3200
3106
|
try {
|
|
3201
|
-
parsed = JSON.parse(
|
|
3107
|
+
parsed = JSON.parse(readFileSync15(at, "utf8"));
|
|
3202
3108
|
} catch (error) {
|
|
3203
3109
|
return [
|
|
3204
3110
|
finding12(
|
|
@@ -3533,7 +3439,6 @@ export {
|
|
|
3533
3439
|
defaultRef,
|
|
3534
3440
|
checkBaseline,
|
|
3535
3441
|
DEPLOYED_FILE,
|
|
3536
|
-
NOT_WRITTEN,
|
|
3537
3442
|
checkDeployed,
|
|
3538
3443
|
resolveFrom,
|
|
3539
3444
|
packageDirOf,
|
|
@@ -3551,9 +3456,9 @@ export {
|
|
|
3551
3456
|
missingOptionOf,
|
|
3552
3457
|
enabledRulesOf,
|
|
3553
3458
|
checkExercised,
|
|
3554
|
-
ENVELOPES_DIR,
|
|
3555
3459
|
NO_ENVELOPES,
|
|
3556
3460
|
checkEnvelopes,
|
|
3461
|
+
ENVELOPES_DIR2 as ENVELOPES_DIR,
|
|
3557
3462
|
checkExams,
|
|
3558
3463
|
INSTALLED_PLUGINS,
|
|
3559
3464
|
PLUGIN_NAME,
|
package/dist/doctor-cli.js
CHANGED
|
@@ -4,82 +4,15 @@ import {
|
|
|
4
4
|
formatDoctor,
|
|
5
5
|
formatJson,
|
|
6
6
|
runDoctor
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-IJXJ2EF4.js";
|
|
8
8
|
|
|
9
9
|
// src/doctor-cli.ts
|
|
10
10
|
import { fstatSync, statSync } from "fs";
|
|
11
|
-
import { resolve
|
|
11
|
+
import { resolve } from "path";
|
|
12
12
|
|
|
13
13
|
// src/envelope-write.ts
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import { fileURLToPath } from "url";
|
|
17
|
-
var envelopePath = (root, tool) => join(root, ENVELOPES_DIR, `${tool}.json`);
|
|
18
|
-
var UnbalancedEnvelope = class extends Error {
|
|
19
|
-
constructor(message) {
|
|
20
|
-
super(message);
|
|
21
|
-
this.name = "UnbalancedEnvelope";
|
|
22
|
-
}
|
|
23
|
-
};
|
|
24
|
-
var isCount = (value) => Number.isSafeInteger(value) && value >= 0;
|
|
25
|
-
var unbalancedMessage = (envelope, next) => `${envelope.tool}: considered ${envelope.considered} but accounts for ${envelope.read + envelope.refused.length + envelope.excused.length} \u2014 ${envelope.read} read + ${envelope.refused.length} refused + ${envelope.excused.length} excused. A run that has lost count of its own inputs cannot say what it measured, so no verdict was rendered and no envelope was written. Next: ${next}`;
|
|
26
|
-
var FORBIDDEN_ROOT = "GEONOSIS_ENVELOPES_FORBIDDEN_ROOT";
|
|
27
|
-
var refuseForbiddenRoot = (root) => {
|
|
28
|
-
const forbidden = process.env[FORBIDDEN_ROOT];
|
|
29
|
-
if (forbidden === void 0 || resolve(forbidden) !== resolve(root)) return;
|
|
30
|
-
throw new UnbalancedEnvelope(
|
|
31
|
-
`${root} is off limits to envelope writers in this process (${FORBIDDEN_ROOT}) \u2014 a run that writes one into a shared root races every other run reading it, and leaves a file the next one takes for real. Point this at a scratch root of its own: tooling/scratch-dir.ts.`
|
|
32
|
-
);
|
|
33
|
-
};
|
|
34
|
-
var writeEnvelope = ({
|
|
35
|
-
envelope,
|
|
36
|
-
next,
|
|
37
|
-
root
|
|
38
|
-
}) => {
|
|
39
|
-
if (envelope.tool.trim() === "") {
|
|
40
|
-
throw new UnbalancedEnvelope(
|
|
41
|
-
`an envelope with no tool name cannot be filed or reported against. Next: ${next}`
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
if (envelope.version.trim() === "") {
|
|
45
|
-
throw new UnbalancedEnvelope(
|
|
46
|
-
`${envelope.tool}: an envelope that cannot name the build that wrote it dates nothing, and a stale one reads exactly like a fresh one. Next: ${next}`
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
if (!isCount(envelope.considered) || !isCount(envelope.read)) {
|
|
50
|
-
throw new UnbalancedEnvelope(
|
|
51
|
-
`${envelope.tool}: considered ${envelope.considered} and read ${envelope.read} \u2014 a census is a whole number of things, and arithmetic over anything else balances by accident. Next: ${next}`
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
if (envelope.considered !== envelope.read + envelope.refused.length + envelope.excused.length) {
|
|
55
|
-
throw new UnbalancedEnvelope(unbalancedMessage(envelope, next));
|
|
56
|
-
}
|
|
57
|
-
refuseForbiddenRoot(root);
|
|
58
|
-
const at = envelopePath(root, envelope.tool);
|
|
59
|
-
mkdirSync(dirname(at), { recursive: true });
|
|
60
|
-
writeFileSync(at, `${JSON.stringify(envelope, void 0, 2)}
|
|
61
|
-
`);
|
|
62
|
-
return at;
|
|
63
|
-
};
|
|
64
|
-
var UNKNOWN = "unknown";
|
|
65
|
-
var versionIn = (dir) => {
|
|
66
|
-
try {
|
|
67
|
-
const manifest = JSON.parse(readFileSync(join(dir, "package.json"), "utf8"));
|
|
68
|
-
return typeof manifest.version === "string" ? manifest.version : void 0;
|
|
69
|
-
} catch {
|
|
70
|
-
return void 0;
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
var versionOf = (moduleUrl) => {
|
|
74
|
-
let dir = dirname(fileURLToPath(moduleUrl));
|
|
75
|
-
for (; ; ) {
|
|
76
|
-
const found = versionIn(dir);
|
|
77
|
-
if (found !== void 0) return found;
|
|
78
|
-
const up = dirname(dir);
|
|
79
|
-
if (up === dir) return UNKNOWN;
|
|
80
|
-
dir = up;
|
|
81
|
-
}
|
|
82
|
-
};
|
|
14
|
+
import { versionOf } from "@geonosis/ratchet";
|
|
15
|
+
import { envelopePath, UnbalancedEnvelope, versionOf as versionOf2, writeEnvelope } from "@geonosis/ratchet";
|
|
83
16
|
var DOCTOR_TOOL = "doctor";
|
|
84
17
|
var DOCTOR_NEXT = "geonosis-doctor --json and count the checks it printed \u2014 this run considered every question the tool has, and a check nobody asked for is excused by name rather than left out of the denominator";
|
|
85
18
|
var doctorEnvelope = ({
|
|
@@ -247,8 +180,8 @@ var parseDoctorArgs = (argv, cwd) => {
|
|
|
247
180
|
...baseline === void 0 ? {} : { baseline },
|
|
248
181
|
json,
|
|
249
182
|
...only === void 0 || only.length === 0 ? {} : { only },
|
|
250
|
-
...read["--oxlint"] === void 0 ? {} : { oxlint:
|
|
251
|
-
root:
|
|
183
|
+
...read["--oxlint"] === void 0 ? {} : { oxlint: resolve(cwd, read["--oxlint"]) },
|
|
184
|
+
root: resolve(cwd, read["--root"] ?? cwd),
|
|
252
185
|
strict
|
|
253
186
|
};
|
|
254
187
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
export { DEPLOYED_FILE } from '@geonosis/release';
|
|
2
|
+
export { ENVELOPES_DIR } from '@geonosis/ratchet';
|
|
3
|
+
|
|
1
4
|
type Overrides = {
|
|
2
5
|
files: readonly string[];
|
|
3
6
|
rules: Record<string, unknown>;
|
|
@@ -135,20 +138,6 @@ declare const checkBaseline: ({ ref, root }: {
|
|
|
135
138
|
root: string;
|
|
136
139
|
}) => Finding[];
|
|
137
140
|
|
|
138
|
-
/**
|
|
139
|
-
* The `loaded` lie one layer out: what a pipeline SAID it deployed, against what the tree declares.
|
|
140
|
-
*
|
|
141
|
-
* `wrangler versions upload` applies no triggers, so a cron or a route edited in the config is
|
|
142
|
-
* silently ignored in production for ever, with a green pipeline; `wrangler secret put` creates and
|
|
143
|
-
* deploys a version outside the gate entirely. Nothing in either consumer measured any of it.
|
|
144
|
-
*
|
|
145
|
-
* This reads the FILE the consumer's pipeline writes after promote — `.geonosis/deployed.json` —
|
|
146
|
-
* and imports nothing of `@geonosis/release`, the same discipline `observability` follows: a check
|
|
147
|
-
* that needed the package it checks cannot run in the tree where that package is missing. The
|
|
148
|
-
* comparison is duplicated on purpose; it is set differences over four lists.
|
|
149
|
-
*/
|
|
150
|
-
declare const DEPLOYED_FILE = ".geonosis/deployed.json";
|
|
151
|
-
declare const NOT_WRITTEN = "no .geonosis/deployed.json \u2014 it is written by the pipeline after promote, and its absence is not a pass";
|
|
152
141
|
declare const checkDeployed: ({ root }: {
|
|
153
142
|
root: string;
|
|
154
143
|
}) => Finding[];
|
|
@@ -200,22 +189,6 @@ declare const checkDrift: ({ readers, root, userSettings, workspaces }: {
|
|
|
200
189
|
workspaces: Workspace[];
|
|
201
190
|
}) => Finding[];
|
|
202
191
|
|
|
203
|
-
/**
|
|
204
|
-
* The one line that catches a denominator bug: for every envelope a tool wrote,
|
|
205
|
-
* `considered === read + refused + excused`.
|
|
206
|
-
*
|
|
207
|
-
* Four of them landed in one day, all green — a migrations run that reported on three of four
|
|
208
|
-
* files, a plan check that printed `PASS — 0 plan(s)` over a directory of twenty-one, a parity run
|
|
209
|
-
* over a tree the second config had ignored, a validator that walked a list it had already
|
|
210
|
-
* filtered. Every one published a numerator and no denominator, so nothing could be wrong.
|
|
211
|
-
*
|
|
212
|
-
* This READS the files and imports nothing of the tools that wrote them — the same discipline
|
|
213
|
-
* `deployed` and `observability` follow. A check that needed the package it checks cannot run in
|
|
214
|
-
* the tree where that package is missing, which is the first case it exists to find; and this one
|
|
215
|
-
* has to work over a consumer's `.geonosis/` with no kit installed at all. The arithmetic is
|
|
216
|
-
* duplicated here on purpose: it is one comparison over three numbers, and it is the check.
|
|
217
|
-
*/
|
|
218
|
-
declare const ENVELOPES_DIR = ".geonosis/envelopes";
|
|
219
192
|
declare const NO_ENVELOPES = "no .geonosis/envelopes/*.json \u2014 the tools write one per run, so an absent envelope is a run nobody has made here yet, and it is not a balanced one (this run\u2019s own doctor.json is not one of them: it is written after these checks, and balanced at write time)";
|
|
220
193
|
declare const checkEnvelopes: ({ root }: {
|
|
221
194
|
root: string;
|
|
@@ -519,4 +492,4 @@ declare const checkRunner: ({ ratchet, root, workspaces }: {
|
|
|
519
492
|
workspaces: Workspace[];
|
|
520
493
|
}) => Finding[];
|
|
521
494
|
|
|
522
|
-
export { APPARATUS_KEY, CHECKS, COMPOSITION_ROOT, CONFIG_FILE, type CheckName,
|
|
495
|
+
export { APPARATUS_KEY, CHECKS, COMPOSITION_ROOT, CONFIG_FILE, type CheckName, type DiscoveredConfig, DoctorError, type DoctorOptions, type DoctorReport, FIXED_GROUP, FLOOR_PACKAGES, type Finding, GEONOSIS_FILE, type HeldTogether, INSTALLED_PLUGINS, type InstalledPlugin, KIT_GROUP, type LastEventRecord, MANIFEST_FILE, type Manifest, NO_ENVELOPES, type ObservabilityConfig, PLUGIN_NAME, RATCHET_FILE, READERS, type RatchetConfig, SCOPE, type Verdict, type Workspace, checkBaseline, checkClaudePlugin, checkDeployed, checkDrift, checkEnvelopes, checkExams, checkExercised, checkGroup, checkLoaded, checkObservability, checkRequiredOptions, checkRunner, corpusOfPlugin, declaredApparatusOf, declaredFor, declaredGroupsOf, defaultRef, discoverConfigs, discoverWorkspaces, enabledRulesOf, evidenceHolding, formatDoctor, formatJson, installedPluginVersions, installedPlugins, loadedAt, missingOptionOf, packageDirOf, pluginVersionOf, readConfig, readRatchet, relativePath, relativeToRoot, repoCorpusOf, resolveFrom, runDoctor, satisfies };
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
INSTALLED_PLUGINS,
|
|
13
13
|
KIT_GROUP,
|
|
14
14
|
MANIFEST_FILE,
|
|
15
|
-
NOT_WRITTEN,
|
|
16
15
|
NO_ENVELOPES,
|
|
17
16
|
PLUGIN_NAME,
|
|
18
17
|
RATCHET_FILE,
|
|
@@ -55,7 +54,7 @@ import {
|
|
|
55
54
|
resolveFrom,
|
|
56
55
|
runDoctor,
|
|
57
56
|
satisfies
|
|
58
|
-
} from "./chunk-
|
|
57
|
+
} from "./chunk-IJXJ2EF4.js";
|
|
59
58
|
export {
|
|
60
59
|
APPARATUS_KEY,
|
|
61
60
|
CHECKS,
|
|
@@ -70,7 +69,6 @@ export {
|
|
|
70
69
|
INSTALLED_PLUGINS,
|
|
71
70
|
KIT_GROUP,
|
|
72
71
|
MANIFEST_FILE,
|
|
73
|
-
NOT_WRITTEN,
|
|
74
72
|
NO_ENVELOPES,
|
|
75
73
|
PLUGIN_NAME,
|
|
76
74
|
RATCHET_FILE,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geonosis/doctor",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.13.0",
|
|
4
4
|
"types": "./dist/index.d.ts",
|
|
5
5
|
"description": "The adoption doctor — declared ≠ loaded, enabled ≠ exercised, a baseline that grew, a runner whose exit code is the only verdict.",
|
|
6
6
|
"keywords": [
|
|
@@ -36,10 +36,9 @@
|
|
|
36
36
|
"dist"
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@geonosis/lint-parity": "2.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
"@geonosis/ratchet": "2.12.0"
|
|
39
|
+
"@geonosis/lint-parity": "2.13.0",
|
|
40
|
+
"@geonosis/ratchet": "2.13.0",
|
|
41
|
+
"@geonosis/release": "2.13.0"
|
|
43
42
|
},
|
|
44
43
|
"peerDependencies": {
|
|
45
44
|
"oxlint": ">=1.77"
|