@geonosis/doctor 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +73 -7
- package/dist/{chunk-CG2LN4VW.js → chunk-7BRGHYQR.js} +637 -86
- package/dist/doctor-cli.js +82 -35
- package/dist/index.d.ts +17 -11
- package/dist/index.js +1 -1
- package/package.json +2 -2
package/dist/doctor-cli.js
CHANGED
|
@@ -1,63 +1,71 @@
|
|
|
1
1
|
import {
|
|
2
|
+
CHECKS,
|
|
2
3
|
formatDoctor,
|
|
3
4
|
formatJson,
|
|
4
5
|
runDoctor
|
|
5
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-7BRGHYQR.js";
|
|
6
7
|
|
|
7
8
|
// src/doctor-cli.ts
|
|
8
9
|
import { resolve } from "path";
|
|
9
|
-
var
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
var ABOUT = {
|
|
11
|
+
baseline: `The ratchet's numbers at HEAD against another ref. A ratchet lowers its own baseline
|
|
12
|
+
when a number shrinks, so nothing inside one checkout can see a branch raise one back
|
|
13
|
+
up. OFF unless asked for: it is the only check that runs git, and this tool is run
|
|
14
|
+
over repos other people are working in.`,
|
|
15
|
+
deployed: `What the pipeline REPORTED deploying, in .geonosis/deployed.json, against what the
|
|
16
|
+
tree declares in the wrangler configs geonosis.json names and release.secrets.
|
|
17
|
+
"versions upload" applies no triggers, so a cron edited in a config is silently
|
|
18
|
+
ignored in production for ever with a green pipeline; a secret can be put outside the
|
|
19
|
+
gate entirely. SKIPs with the sentence when the file is absent \u2014 its absence is not a
|
|
20
|
+
pass \u2014 and reads the file rather than importing @geonosis/release.`,
|
|
21
|
+
drift: `The gates that were set up and are no longer running: a CI job switched off by a
|
|
22
|
+
condition that can never be true, a test file under a workspace with no test script, a
|
|
23
|
+
script handing bun/node/tsx a file that is not on disk, a workspace bin nothing links,
|
|
24
|
+
an integration directory the registry never names, a geonosis bin a git hook calls
|
|
25
|
+
through pnpm/npx/bunx and therefore cannot start, a law over the ceiling its own
|
|
26
|
+
geonosis.json declared, nothing enabling the plugin in either scope, and a
|
|
27
|
+
geonosis.json block no manifest declares a package for (or the reverse).`,
|
|
28
|
+
exercised: `Every rule a config enables, against the corpus the loaded plugin ships. A rule at
|
|
29
|
+
"error" that can never fire is indistinguishable from a clean tree.`,
|
|
30
|
+
loaded: `The plugin oxlint would LOAD from each config's directory, against the version that
|
|
15
31
|
config's workspace DECLARES. oxlint resolves a jsPlugins specifier from the CONFIG
|
|
16
32
|
FILE's directory, so a nested copy left behind by a per-workspace install runs while
|
|
17
33
|
every manifest and the lockfile say otherwise \u2014 one consumer measured a whole bug
|
|
18
34
|
report against 0.3.0 on a repo pinned to 0.4.0. Every copy is found by RESOLUTION,
|
|
19
|
-
never by find: on pnpm the store keeps every version ever installed
|
|
20
|
-
|
|
21
|
-
exercised Every rule a config enables, against the corpus the loaded plugin ships. A rule at
|
|
22
|
-
"error" that can never fire is indistinguishable from a clean tree.
|
|
23
|
-
|
|
24
|
-
baseline The ratchet's numbers at HEAD against another ref. A ratchet lowers its own baseline
|
|
25
|
-
when a number shrinks, so nothing inside one checkout can see a branch raise one back
|
|
26
|
-
up. OFF unless asked for: it is the only check that runs git, and this tool is run
|
|
27
|
-
over repos other people are working in.
|
|
28
|
-
|
|
29
|
-
runner Workspaces whose test script's exit code is the only verdict. A pool exited 0 over
|
|
30
|
-
suites it had just reported as failing, for weeks, in a consumer.
|
|
31
|
-
|
|
32
|
-
observability
|
|
33
|
-
The exporter named by the "observability" block in geonosis.json: is a sink
|
|
35
|
+
never by find: on pnpm the store keeps every version ever installed.`,
|
|
36
|
+
observability: `The exporter named by the "observability" block in geonosis.json: is a sink
|
|
34
37
|
configured, is its endpoint reachable (a HEAD with a short timeout), and did an event
|
|
35
38
|
arrive inside maxAgeSeconds \u2014 read from the lastEventFile the sink writes, never from
|
|
36
39
|
this tool importing the library it is checking. A deploy job reported success having
|
|
37
40
|
deployed nothing and a /health answered ok over a dead database on the same
|
|
38
|
-
afternoon; both left a green build and a silent project
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
afternoon; both left a green build and a silent project.`,
|
|
42
|
+
runner: `Workspaces whose test script's exit code is the only verdict. A pool exited 0 over
|
|
43
|
+
suites it had just reported as failing, for weeks, in a consumer. A script with no
|
|
44
|
+
test file under it is a SKIP \u2014 there is no result for an exit code to be wrong about.`
|
|
45
|
+
};
|
|
46
|
+
var USAGE = `geonosis-doctor [--root <dir>] [--only <check,\u2026>] [--json] [--strict] [--baseline-against [<ref>]] [--oxlint <path>]
|
|
44
47
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
${CHECKS.length} questions a version bump is not finished until something has asked. Most are ways
|
|
49
|
+
enforcement has reported green while measuring nothing; drift asks whether the gate is still there
|
|
50
|
+
at all, and the last two ask whether what was reported is what happened.
|
|
51
|
+
|
|
52
|
+
With no arguments it runs every one of them over the working directory. A tree holding no
|
|
53
|
+
package.json and no .oxlintrc.json is REFUSED with exit 2 \u2014 there is nothing there to examine, and a
|
|
54
|
+
page of SKIPs is not a pass.
|
|
55
|
+
|
|
56
|
+
${CHECKS.map((check) => ` ${check.padEnd(11)} ${ABOUT[check]}`).join("\n\n")}
|
|
51
57
|
|
|
52
58
|
exercised reads the corpus the loaded plugin ships, and \u2014 when geonosis.json names one under
|
|
53
59
|
doctor.corpus \u2014 the corpus THIS repo ships for its own options. Three rules cannot be answered any
|
|
54
60
|
other way: layer-walls fires on a repo's own layer names, no-brand-names on its brand, and
|
|
55
61
|
plugin-route-namespaced on its package root, while the shipped corpus says acme and layers/core.
|
|
56
62
|
|
|
63
|
+
--print-config-shape every file this reads, and the keys it reads out of each
|
|
64
|
+
|
|
57
65
|
Exits 1 when any line is a FAIL or an UNJUDGED \u2014 a question this could not ask is not a pass \u2014 and
|
|
58
66
|
2 when the run could not be made at all. --strict promotes every WARN to a FAIL. --json prints the
|
|
59
67
|
whole report for a CI step to read.`;
|
|
60
|
-
var VALUED = /* @__PURE__ */ new Set(["--oxlint", "--root"]);
|
|
68
|
+
var VALUED = /* @__PURE__ */ new Set(["--only", "--oxlint", "--root"]);
|
|
61
69
|
var parseDoctorArgs = (argv, cwd) => {
|
|
62
70
|
const read = {};
|
|
63
71
|
let baseline;
|
|
@@ -85,18 +93,57 @@ var parseDoctorArgs = (argv, cwd) => {
|
|
|
85
93
|
read[flag] = value;
|
|
86
94
|
index += 1;
|
|
87
95
|
}
|
|
96
|
+
const only = read["--only"]?.split(",").map((one) => one.trim()).filter((one) => one !== "");
|
|
97
|
+
const unknown = (only ?? []).filter((one) => !CHECKS.includes(one));
|
|
98
|
+
if (unknown.length > 0) {
|
|
99
|
+
throw new Error(`no check called ${unknown.join(", ")} \u2014 the checks are ${CHECKS.join(", ")}`);
|
|
100
|
+
}
|
|
88
101
|
return {
|
|
89
102
|
...baseline === void 0 ? {} : { baseline },
|
|
90
103
|
json,
|
|
104
|
+
...only === void 0 || only.length === 0 ? {} : { only },
|
|
91
105
|
...read["--oxlint"] === void 0 ? {} : { oxlint: resolve(cwd, read["--oxlint"]) },
|
|
92
106
|
root: resolve(cwd, read["--root"] ?? cwd),
|
|
93
107
|
strict
|
|
94
108
|
};
|
|
95
109
|
};
|
|
110
|
+
var CONFIG_SHAPE = `geonosis-doctor reads geonosis.json, geonosis.ratchet.json, .oxlintrc.json,
|
|
111
|
+
pnpm-workspace.yaml and .claude/settings.json
|
|
112
|
+
|
|
113
|
+
geonosis.json \u2192 "law"
|
|
114
|
+
file string? the law file to measure (default "CLAUDE.md")
|
|
115
|
+
maxLines number? the ceiling. Undeclared means UNJUDGED, never a default it fires on
|
|
116
|
+
geonosis.json \u2192 "observability"
|
|
117
|
+
sink string where this repo sends its errors
|
|
118
|
+
endpoint string the URL that must be reachable
|
|
119
|
+
lastEventFile string the file the sink writes on arrival
|
|
120
|
+
probe string? a command that answers instead of lastEventFile
|
|
121
|
+
maxAgeSeconds number how old the last event may be
|
|
122
|
+
geonosis.json \u2192 "release"
|
|
123
|
+
wrangler string[] the wrangler configs a deployment carries
|
|
124
|
+
wranglerEnv string? the named environment block to read
|
|
125
|
+
secrets string[] the secret names it carries
|
|
126
|
+
.oxlintrc.json
|
|
127
|
+
jsPlugins string[] the plugin specifiers oxlint would load
|
|
128
|
+
rules object the base rules, and the options each was given
|
|
129
|
+
overrides object[] files + rules; an entry REPLACES a rule's options for the files it
|
|
130
|
+
claims, last match winning \u2014 the same resolution oxlint does
|
|
131
|
+
geonosis.ratchet.json
|
|
132
|
+
the ratchet's own \u2014 run \`geonosis-ratchet --print-config-shape\`
|
|
133
|
+
pnpm-workspace.yaml
|
|
134
|
+
publicHoistPattern string[] the packages this repo claims are linked at the root, read as
|
|
135
|
+
text; a "*" is the only wildcard and it spans "/"
|
|
136
|
+
.claude/settings.json
|
|
137
|
+
enabledPlugins object what installs the kit's plugin in THIS repo`;
|
|
96
138
|
var main = async () => {
|
|
97
139
|
const argv = process.argv.slice(2);
|
|
98
140
|
if (argv.includes("--help") || argv.includes("-h")) {
|
|
99
141
|
process.stdout.write(`${USAGE}
|
|
142
|
+
`);
|
|
143
|
+
return 0;
|
|
144
|
+
}
|
|
145
|
+
if (argv.includes("--print-config-shape")) {
|
|
146
|
+
process.stdout.write(`${CONFIG_SHAPE}
|
|
100
147
|
`);
|
|
101
148
|
return 0;
|
|
102
149
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
type Overrides = {
|
|
2
|
+
files: readonly string[];
|
|
3
|
+
rules: Record<string, unknown>;
|
|
4
|
+
}[];
|
|
5
|
+
|
|
1
6
|
/**
|
|
2
7
|
* The questions a bump is not finished until something has asked, in the order a run asks them.
|
|
3
8
|
* The first four are ways enforcement has reported green while measuring nothing; `drift` asks
|
|
@@ -29,6 +34,7 @@ declare class DoctorError extends Error {
|
|
|
29
34
|
constructor(message: string);
|
|
30
35
|
}
|
|
31
36
|
type Manifest = {
|
|
37
|
+
bin?: Record<string, string> | string;
|
|
32
38
|
dependencies?: Record<string, string>;
|
|
33
39
|
devDependencies?: Record<string, string>;
|
|
34
40
|
name?: string;
|
|
@@ -47,6 +53,8 @@ type DiscoveredConfig = {
|
|
|
47
53
|
/** Why this config could not be read. A config nobody can parse is a finding, never a skip. */
|
|
48
54
|
error?: string;
|
|
49
55
|
jsPlugins: string[];
|
|
56
|
+
/** Layers that REPLACE a rule's options for the files they claim — never merged into the base. */
|
|
57
|
+
overrides: Overrides;
|
|
50
58
|
path: string;
|
|
51
59
|
relative: string;
|
|
52
60
|
rules: Record<string, unknown>;
|
|
@@ -65,6 +73,8 @@ type DoctorOptions = {
|
|
|
65
73
|
ref?: string;
|
|
66
74
|
};
|
|
67
75
|
oxlint?: string;
|
|
76
|
+
/** Run these checks and no other — one question, asked fast, belongs in a sub-second gate. */
|
|
77
|
+
only?: CheckName[];
|
|
68
78
|
root: string;
|
|
69
79
|
strict?: boolean;
|
|
70
80
|
};
|
|
@@ -124,7 +134,7 @@ declare const discoverConfigs: (root: string) => DiscoveredConfig[];
|
|
|
124
134
|
declare const discoverWorkspaces: (root: string) => Workspace[];
|
|
125
135
|
declare const readRatchet: (root: string) => RatchetConfig | undefined;
|
|
126
136
|
|
|
127
|
-
declare const runDoctor: ({ baseline, oxlint, root, strict, }: DoctorOptions) => Promise<DoctorReport>;
|
|
137
|
+
declare const runDoctor: ({ baseline, only, oxlint, root, strict, }: DoctorOptions) => Promise<DoctorReport>;
|
|
128
138
|
|
|
129
139
|
/** Which kit package reads which block of `geonosis.json`. */
|
|
130
140
|
declare const READERS: Record<string, string>;
|
|
@@ -136,7 +146,7 @@ declare const READERS: Record<string, string>;
|
|
|
136
146
|
* block nothing reads. The other four checks ask whether a gate measures what it names; this one
|
|
137
147
|
* asks whether it is still there at all.
|
|
138
148
|
*/
|
|
139
|
-
declare const checkDrift: ({ readers, root, workspaces, }: {
|
|
149
|
+
declare const checkDrift: ({ readers, root, userSettings, workspaces, }: {
|
|
140
150
|
/**
|
|
141
151
|
* Which package reads which block. It is a parameter so a test can name one that can NEVER be
|
|
142
152
|
* installed: a fixture that turns on whether some real package happens to be present on the base
|
|
@@ -145,6 +155,11 @@ declare const checkDrift: ({ readers, root, workspaces, }: {
|
|
|
145
155
|
*/
|
|
146
156
|
readers?: Record<string, string>;
|
|
147
157
|
root: string;
|
|
158
|
+
/**
|
|
159
|
+
* The machine-wide settings, a parameter so a test never asks about the home directory it happens
|
|
160
|
+
* to run in — the same reason `readers` is one.
|
|
161
|
+
*/
|
|
162
|
+
userSettings?: string;
|
|
148
163
|
workspaces: Workspace[];
|
|
149
164
|
}) => Finding[];
|
|
150
165
|
|
|
@@ -242,15 +257,6 @@ declare const repoCorpusOf: (root: string) => string | undefined;
|
|
|
242
257
|
declare const formatDoctor: ({ counts, findings, ok, root }: DoctorReport) => string;
|
|
243
258
|
declare const formatJson: (report: DoctorReport) => string;
|
|
244
259
|
|
|
245
|
-
/**
|
|
246
|
-
* What oxlint would load, asked the only way that answers on every package manager.
|
|
247
|
-
*
|
|
248
|
-
* oxlint resolves a `jsPlugins` specifier from the CONFIG FILE's directory, not the working
|
|
249
|
-
* directory, so the question is asked from a directory and never from the process's cwd. And it is
|
|
250
|
-
* asked of the RESOLVER: counting copies with `find` is a bun/npm check only — on pnpm the entries
|
|
251
|
-
* under `node_modules/@scope/` are symlinks into `.pnpm/`, which keeps every version ever
|
|
252
|
-
* installed, so the same count answers 7 on a correct tree and 0 with the store excluded.
|
|
253
|
-
*/
|
|
254
260
|
declare const resolveFrom: (dir: string, specifier: string) => string;
|
|
255
261
|
/** The package a resolved entry point belongs to: up until a manifest claims the name. */
|
|
256
262
|
declare const packageDirOf: (entry: string, name: string) => string;
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geonosis/doctor",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.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": [
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"dist"
|
|
36
36
|
],
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@geonosis/lint-parity": "1.
|
|
38
|
+
"@geonosis/lint-parity": "1.3.0"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"oxlint": ">=1.77"
|