@mmnto/cli 1.103.0 → 1.104.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/dist/commands/doctor-parity-fetch.d.ts +84 -0
- package/dist/commands/doctor-parity-fetch.d.ts.map +1 -0
- package/dist/commands/doctor-parity-fetch.js +367 -0
- package/dist/commands/doctor-parity-fetch.js.map +1 -0
- package/dist/commands/doctor-parity-fetch.test.d.ts +13 -0
- package/dist/commands/doctor-parity-fetch.test.d.ts.map +1 -0
- package/dist/commands/doctor-parity-fetch.test.js +269 -0
- package/dist/commands/doctor-parity-fetch.test.js.map +1 -0
- package/dist/commands/doctor-parity.d.ts.map +1 -1
- package/dist/commands/doctor-parity.js +57 -1
- package/dist/commands/doctor-parity.js.map +1 -1
- package/dist/commands/eject.d.ts.map +1 -1
- package/dist/commands/eject.js +3 -0
- package/dist/commands/eject.js.map +1 -1
- package/dist/commands/init-templates.d.ts +15 -0
- package/dist/commands/init-templates.d.ts.map +1 -1
- package/dist/commands/init-templates.js +183 -2
- package/dist/commands/init-templates.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +30 -2
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/init.test.js +264 -1
- package/dist/commands/init.test.js.map +1 -1
- package/dist/commands/install-hooks-exit-contract.test.js +107 -3
- package/dist/commands/install-hooks-exit-contract.test.js.map +1 -1
- package/dist/commands/install-hooks.d.ts +39 -0
- package/dist/commands/install-hooks.d.ts.map +1 -1
- package/dist/commands/install-hooks.js +152 -0
- package/dist/commands/install-hooks.js.map +1 -1
- package/dist/commands/pr.d.ts +130 -0
- package/dist/commands/pr.d.ts.map +1 -0
- package/dist/commands/pr.js +427 -0
- package/dist/commands/pr.js.map +1 -0
- package/dist/commands/pr.test.d.ts +2 -0
- package/dist/commands/pr.test.d.ts.map +1 -0
- package/dist/commands/pr.test.js +494 -0
- package/dist/commands/pr.test.js.map +1 -0
- package/dist/index.js +34 -0
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Network-read-only fetch edge for the Prop 296 §14 posture probes
|
|
3
|
+
* (mmnto-ai/totem-strategy#962). The CLI EDGE owns the network — core's
|
|
4
|
+
* `parity-detect.ts` keeps its module-wide never-networks + synchronous-pure
|
|
5
|
+
* invariant, so this module resolves the per-repo, per-surface SNAPSHOTS the
|
|
6
|
+
* pure detector then verdicts against.
|
|
7
|
+
*
|
|
8
|
+
* §14 hard edges honored here:
|
|
9
|
+
* 1. Read-only, ever — every request is an authenticated `gh api` GET; nothing
|
|
10
|
+
* mutates ([Tenet 13]).
|
|
11
|
+
* 2. Auth is the hard edge, rendered honestly — a missing/under-privileged
|
|
12
|
+
* token, an unreachable host, or a repo-scoped CI token that cannot see a
|
|
13
|
+
* sibling repo degrades to a per-SURFACE cannot-verify outcome
|
|
14
|
+
* (`auth`/`not-found`/`error`), never a drift verdict and never a
|
|
15
|
+
* manifest-wide outage. `no-transport` (gh absent / offline) is the distinct
|
|
16
|
+
* honest-absent (§14 clause 4) signal.
|
|
17
|
+
* 3. Per-repo verdict lines — the roster resolves one snapshot per repo; the
|
|
18
|
+
* current repo (derived from the LOCAL git remote — no network) is always in
|
|
19
|
+
* the roster, cross-repo reads are opt-in via `orient.parityProbeRepos`.
|
|
20
|
+
* 4. Offline degradation — gh unavailable ⇒ every surface `no-transport` ⇒
|
|
21
|
+
* every line renders as the honest-absent stub. NO retries.
|
|
22
|
+
*
|
|
23
|
+
* Transport is behind an INJECTABLE seam ({@link GhFetch}) so tests feed canned
|
|
24
|
+
* JSON and NEVER spawn `gh`. The default spawns `gh api` via `safeExec` (arg
|
|
25
|
+
* arrays, no `shell: true`, bounded timeout) — the git-subprocess pattern the
|
|
26
|
+
* core detectors already use.
|
|
27
|
+
*/
|
|
28
|
+
import type { NetworkPostureRow, NetworkProbeRepoSnapshot, NetworkSurfaceOutcome } from '@mmnto/totem';
|
|
29
|
+
/** Resolve a contract id to its network-posture row kind, or undefined when unregistered. */
|
|
30
|
+
export declare function networkPostureRowFor(contractId: string): NetworkPostureRow | undefined;
|
|
31
|
+
/** One fetched surface plus its outcome — what {@link GhFetch} returns. */
|
|
32
|
+
export interface GhFetchResult {
|
|
33
|
+
outcome: NetworkSurfaceOutcome;
|
|
34
|
+
/** Parsed JSON body when `outcome === 'ok'`. */
|
|
35
|
+
data?: unknown;
|
|
36
|
+
/** Optional render detail (e.g. `HTTP 403`). */
|
|
37
|
+
detail?: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Injectable transport seam: issue one read-only GitHub API GET against
|
|
41
|
+
* `apiPath` (an absolute `/repos/...` path) from `cwd`. Tests inject a canned
|
|
42
|
+
* implementation; production omits it and the default spawns `gh api`.
|
|
43
|
+
*/
|
|
44
|
+
export type GhFetch = (apiPath: string, cwd: string) => GhFetchResult;
|
|
45
|
+
/** Injectable local-remote reader (default `git remote get-url origin`). */
|
|
46
|
+
export type ReadRemote = (cwd: string) => string | undefined;
|
|
47
|
+
/** One network-posture row present in the manifest (id-derived row kind + its consumers scope). */
|
|
48
|
+
export interface NetworkRowSpec {
|
|
49
|
+
row: NetworkPostureRow;
|
|
50
|
+
consumers?: string[];
|
|
51
|
+
}
|
|
52
|
+
/** Options for {@link resolveNetworkSnapshots}. */
|
|
53
|
+
export interface ResolveNetworkSnapshotsOptions {
|
|
54
|
+
/** The network-posture rows present in the manifest (drives which surfaces to fetch). */
|
|
55
|
+
rows: NetworkRowSpec[];
|
|
56
|
+
/** Current repo's cohort id (for `consumers` scoping of the current-repo slug). */
|
|
57
|
+
repoId?: string;
|
|
58
|
+
/** The git root the local remote is read from + `gh` runs in. */
|
|
59
|
+
gitRoot: string;
|
|
60
|
+
/** Optional cross-repo read set (`orient.parityProbeRepos`), each an `owner/repo` slug. */
|
|
61
|
+
probeRepos?: string[];
|
|
62
|
+
/** Injectable transport (default spawns `gh api`). */
|
|
63
|
+
ghFetch?: GhFetch;
|
|
64
|
+
/** Injectable local-remote reader (default reads `git remote get-url origin`). */
|
|
65
|
+
readRemote?: ReadRemote;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Resolve the per-repo, per-surface snapshots the network-posture detector
|
|
69
|
+
* verdicts against. Fetches ONCE per (repo, surface) — the union of surfaces the
|
|
70
|
+
* in-scope rows need for each repo — with NO caching and NO retries. Never
|
|
71
|
+
* throws: a transport/auth failure becomes a per-surface outcome, never a crash.
|
|
72
|
+
*
|
|
73
|
+
* The function is async to keep the network step BEFORE the synchronous detector
|
|
74
|
+
* dispatch (the default transport is a synchronous `gh api` spawn; the async
|
|
75
|
+
* boundary future-proofs an async transport).
|
|
76
|
+
*/
|
|
77
|
+
export declare function resolveNetworkSnapshots(options: ResolveNetworkSnapshotsOptions): Promise<NetworkProbeRepoSnapshot[]>;
|
|
78
|
+
/**
|
|
79
|
+
* Extract `owner/repo` from an ssh (`git@host:owner/repo.git`) or https
|
|
80
|
+
* (`https://host/owner/repo.git`) remote URL, tolerating a trailing `.git` and
|
|
81
|
+
* slashes. Returns undefined when no `owner/repo` pair resolves.
|
|
82
|
+
*/
|
|
83
|
+
export declare function slugFromRemoteUrl(remoteUrl: string | undefined): string | undefined;
|
|
84
|
+
//# sourceMappingURL=doctor-parity-fetch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor-parity-fetch.d.ts","sourceRoot":"","sources":["../../src/commands/doctor-parity-fetch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EACV,iBAAiB,EACjB,wBAAwB,EAExB,qBAAqB,EAEtB,MAAM,cAAc,CAAC;AAkBtB,6FAA6F;AAC7F,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAEtF;AAED,2EAA2E;AAC3E,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,qBAAqB,CAAC;IAC/B,gDAAgD;IAChD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,gDAAgD;IAChD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,aAAa,CAAC;AAEtE,4EAA4E;AAC5E,MAAM,MAAM,UAAU,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;AAE7D,mGAAmG;AACnG,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,iBAAiB,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,mDAAmD;AACnD,MAAM,WAAW,8BAA8B;IAC7C,yFAAyF;IACzF,IAAI,EAAE,cAAc,EAAE,CAAC;IACvB,mFAAmF;IACnF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iEAAiE;IACjE,OAAO,EAAE,MAAM,CAAC;IAChB,2FAA2F;IAC3F,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,sDAAsD;IACtD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,kFAAkF;IAClF,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB;AA6BD;;;;;;;;;GASG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,8BAA8B,GACtC,OAAO,CAAC,wBAAwB,EAAE,CAAC,CA+BrC;AA+CD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAcnF"}
|
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Network-read-only fetch edge for the Prop 296 §14 posture probes
|
|
3
|
+
* (mmnto-ai/totem-strategy#962). The CLI EDGE owns the network — core's
|
|
4
|
+
* `parity-detect.ts` keeps its module-wide never-networks + synchronous-pure
|
|
5
|
+
* invariant, so this module resolves the per-repo, per-surface SNAPSHOTS the
|
|
6
|
+
* pure detector then verdicts against.
|
|
7
|
+
*
|
|
8
|
+
* §14 hard edges honored here:
|
|
9
|
+
* 1. Read-only, ever — every request is an authenticated `gh api` GET; nothing
|
|
10
|
+
* mutates ([Tenet 13]).
|
|
11
|
+
* 2. Auth is the hard edge, rendered honestly — a missing/under-privileged
|
|
12
|
+
* token, an unreachable host, or a repo-scoped CI token that cannot see a
|
|
13
|
+
* sibling repo degrades to a per-SURFACE cannot-verify outcome
|
|
14
|
+
* (`auth`/`not-found`/`error`), never a drift verdict and never a
|
|
15
|
+
* manifest-wide outage. `no-transport` (gh absent / offline) is the distinct
|
|
16
|
+
* honest-absent (§14 clause 4) signal.
|
|
17
|
+
* 3. Per-repo verdict lines — the roster resolves one snapshot per repo; the
|
|
18
|
+
* current repo (derived from the LOCAL git remote — no network) is always in
|
|
19
|
+
* the roster, cross-repo reads are opt-in via `orient.parityProbeRepos`.
|
|
20
|
+
* 4. Offline degradation — gh unavailable ⇒ every surface `no-transport` ⇒
|
|
21
|
+
* every line renders as the honest-absent stub. NO retries.
|
|
22
|
+
*
|
|
23
|
+
* Transport is behind an INJECTABLE seam ({@link GhFetch}) so tests feed canned
|
|
24
|
+
* JSON and NEVER spawn `gh`. The default spawns `gh api` via `safeExec` (arg
|
|
25
|
+
* arrays, no `shell: true`, bounded timeout) — the git-subprocess pattern the
|
|
26
|
+
* core detectors already use.
|
|
27
|
+
*/
|
|
28
|
+
/** Bounded per-request `gh api` timeout (mirrors orient's GH adapters). */
|
|
29
|
+
const GH_TIMEOUT_MS = 15_000;
|
|
30
|
+
/**
|
|
31
|
+
* The registry mapping a capability-probe contract id to its network-posture
|
|
32
|
+
* row kind (mirrors `capabilityProbesFor` with a `default: undefined` → the
|
|
33
|
+
* existing honest-skip stub). An unregistered id keeps the current
|
|
34
|
+
* capability-probe path untouched. The identity mapping is the routing seam:
|
|
35
|
+
* routing keys PRIMARILY on this id registry, not on the `probe-class` metadata.
|
|
36
|
+
*/
|
|
37
|
+
const NETWORK_POSTURE_ROW_IDS = {
|
|
38
|
+
'repo-merge-posture': 'repo-merge-posture',
|
|
39
|
+
'repo-required-checks-posture': 'repo-required-checks-posture',
|
|
40
|
+
'repo-branch-protection-posture': 'repo-branch-protection-posture',
|
|
41
|
+
};
|
|
42
|
+
/** Resolve a contract id to its network-posture row kind, or undefined when unregistered. */
|
|
43
|
+
export function networkPostureRowFor(contractId) {
|
|
44
|
+
return NETWORK_POSTURE_ROW_IDS[contractId];
|
|
45
|
+
}
|
|
46
|
+
/** Per-row surface requirements. */
|
|
47
|
+
function surfaceNeedsFor(row) {
|
|
48
|
+
switch (row) {
|
|
49
|
+
case 'repo-merge-posture':
|
|
50
|
+
return { repoSettings: true, rulesets: false, branchProtection: false };
|
|
51
|
+
case 'repo-required-checks-posture':
|
|
52
|
+
return { repoSettings: false, rulesets: true, branchProtection: false };
|
|
53
|
+
case 'repo-branch-protection-posture':
|
|
54
|
+
return { repoSettings: false, rulesets: true, branchProtection: true };
|
|
55
|
+
default:
|
|
56
|
+
return { repoSettings: false, rulesets: false, branchProtection: false };
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Resolve the per-repo, per-surface snapshots the network-posture detector
|
|
61
|
+
* verdicts against. Fetches ONCE per (repo, surface) — the union of surfaces the
|
|
62
|
+
* in-scope rows need for each repo — with NO caching and NO retries. Never
|
|
63
|
+
* throws: a transport/auth failure becomes a per-surface outcome, never a crash.
|
|
64
|
+
*
|
|
65
|
+
* The function is async to keep the network step BEFORE the synchronous detector
|
|
66
|
+
* dispatch (the default transport is a synchronous `gh api` spawn; the async
|
|
67
|
+
* boundary future-proofs an async transport).
|
|
68
|
+
*/
|
|
69
|
+
export async function resolveNetworkSnapshots(options) {
|
|
70
|
+
// Import `safeExec` once for the default transport/remote seams (idempotent,
|
|
71
|
+
// cached; the dynamic import matches the doctor's other lazy `@mmnto/totem`
|
|
72
|
+
// loads). When both seams are injected — the test path — the real spawn is
|
|
73
|
+
// never invoked, so no `gh`/`git` subprocess runs.
|
|
74
|
+
const { safeExec } = await import('@mmnto/totem');
|
|
75
|
+
const ghFetch = options.ghFetch ?? makeDefaultGhFetch(safeExec);
|
|
76
|
+
const readRemote = options.readRemote ?? makeDefaultReadRemote(safeExec);
|
|
77
|
+
const roster = resolveRoster(options, readRemote);
|
|
78
|
+
const snapshots = [];
|
|
79
|
+
for (const entry of roster) {
|
|
80
|
+
// Per-repo needed surfaces = union over the rows in scope for THIS repo
|
|
81
|
+
// (a `consumers: [totem]` row contributes its surfaces only to totem).
|
|
82
|
+
const need = { repoSettings: false, rulesets: false, branchProtection: false };
|
|
83
|
+
for (const spec of options.rows) {
|
|
84
|
+
if (spec.consumers !== undefined && !spec.consumers.includes(entry.repoId))
|
|
85
|
+
continue;
|
|
86
|
+
const rowNeed = surfaceNeedsFor(spec.row);
|
|
87
|
+
need.repoSettings ||= rowNeed.repoSettings;
|
|
88
|
+
need.rulesets ||= rowNeed.rulesets;
|
|
89
|
+
need.branchProtection ||= rowNeed.branchProtection;
|
|
90
|
+
}
|
|
91
|
+
if (!need.repoSettings && !need.rulesets && !need.branchProtection)
|
|
92
|
+
continue;
|
|
93
|
+
snapshots.push({
|
|
94
|
+
repoSlug: entry.repoSlug,
|
|
95
|
+
repoId: entry.repoId,
|
|
96
|
+
surfaces: fetchSurfaces(entry.repoSlug, need, ghFetch, options.gitRoot),
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
return snapshots;
|
|
100
|
+
}
|
|
101
|
+
/** Build the roster: the current repo (local-remote-derived) plus any opt-in cross-repo slugs. */
|
|
102
|
+
function resolveRoster(options, readRemote) {
|
|
103
|
+
const entries = [];
|
|
104
|
+
const seen = new Set();
|
|
105
|
+
const currentSlug = deriveCurrentSlug(options, readRemote);
|
|
106
|
+
if (currentSlug !== undefined) {
|
|
107
|
+
entries.push({ repoSlug: currentSlug, repoId: options.repoId ?? repoSegment(currentSlug) });
|
|
108
|
+
seen.add(currentSlug);
|
|
109
|
+
}
|
|
110
|
+
for (const raw of options.probeRepos ?? []) {
|
|
111
|
+
const slug = raw.trim();
|
|
112
|
+
if (slug.length === 0 || seen.has(slug))
|
|
113
|
+
continue;
|
|
114
|
+
// A cross-repo entry's cohort id is the repo segment of its own slug.
|
|
115
|
+
entries.push({ repoSlug: slug, repoId: repoSegment(slug) });
|
|
116
|
+
seen.add(slug);
|
|
117
|
+
}
|
|
118
|
+
return entries;
|
|
119
|
+
}
|
|
120
|
+
/** Derive the current repo's `owner/repo` slug from the LOCAL git remote (no network). */
|
|
121
|
+
function deriveCurrentSlug(options, readRemote) {
|
|
122
|
+
let url;
|
|
123
|
+
try {
|
|
124
|
+
url = readRemote(options.gitRoot);
|
|
125
|
+
// totem-context: a missing remote / non-git dir / absent git binary is a routine fall-through (the current repo is simply not probed), not a sensor failure.
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
url = undefined;
|
|
129
|
+
}
|
|
130
|
+
return slugFromRemoteUrl(url);
|
|
131
|
+
}
|
|
132
|
+
/** The repo segment of an `owner/repo` slug (the cohort id). */
|
|
133
|
+
function repoSegment(slug) {
|
|
134
|
+
const parts = slug.split('/');
|
|
135
|
+
return parts[parts.length - 1] ?? slug;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Extract `owner/repo` from an ssh (`git@host:owner/repo.git`) or https
|
|
139
|
+
* (`https://host/owner/repo.git`) remote URL, tolerating a trailing `.git` and
|
|
140
|
+
* slashes. Returns undefined when no `owner/repo` pair resolves.
|
|
141
|
+
*/
|
|
142
|
+
export function slugFromRemoteUrl(remoteUrl) {
|
|
143
|
+
if (typeof remoteUrl !== 'string' || remoteUrl.trim().length === 0)
|
|
144
|
+
return undefined;
|
|
145
|
+
const trimmed = remoteUrl
|
|
146
|
+
.trim()
|
|
147
|
+
.replace(/\.git$/i, '')
|
|
148
|
+
.replace(/\/+$/, '');
|
|
149
|
+
const match = /[/:]([^/:]+)\/([^/]+)$/.exec(trimmed);
|
|
150
|
+
if (match === null)
|
|
151
|
+
return undefined;
|
|
152
|
+
const owner = match[1];
|
|
153
|
+
const repo = match[2];
|
|
154
|
+
if (owner === undefined || repo === undefined || owner.length === 0 || repo.length === 0) {
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
return `${owner}/${repo}`;
|
|
158
|
+
}
|
|
159
|
+
/** Fetch the union of needed surfaces for one repo. */
|
|
160
|
+
function fetchSurfaces(slug, need, ghFetch, cwd) {
|
|
161
|
+
const surfaces = {};
|
|
162
|
+
// Repo settings double as the source of `default_branch` for classic branch
|
|
163
|
+
// protection, so fetch it whenever either surface is needed.
|
|
164
|
+
let repoResult;
|
|
165
|
+
if (need.repoSettings || need.branchProtection) {
|
|
166
|
+
repoResult = ghFetch(`/repos/${slug}`, cwd);
|
|
167
|
+
}
|
|
168
|
+
if (need.repoSettings && repoResult !== undefined) {
|
|
169
|
+
surfaces.repoSettings = toSnapshot(repoResult);
|
|
170
|
+
}
|
|
171
|
+
if (need.rulesets) {
|
|
172
|
+
surfaces.rulesets = fetchRulesetsSurface(slug, ghFetch, cwd);
|
|
173
|
+
}
|
|
174
|
+
if (need.branchProtection) {
|
|
175
|
+
surfaces.branchProtection = fetchBranchProtectionSurface(slug, repoResult, ghFetch, cwd);
|
|
176
|
+
}
|
|
177
|
+
return surfaces;
|
|
178
|
+
}
|
|
179
|
+
/** Narrow a {@link GhFetchResult} to a {@link NetworkSurfaceSnapshot}. */
|
|
180
|
+
function toSnapshot(result) {
|
|
181
|
+
return {
|
|
182
|
+
outcome: result.outcome,
|
|
183
|
+
...(result.data !== undefined ? { data: result.data } : {}),
|
|
184
|
+
...(result.detail !== undefined ? { detail: result.detail } : {}),
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* The rulesets surface: list the repo's rulesets, then fetch each one's DETAIL
|
|
189
|
+
* (the list omits conditions/rules/bypass_actors). The detail array is the
|
|
190
|
+
* surface `data`. A list-level failure propagates its outcome; a per-detail 404
|
|
191
|
+
* (ruleset deleted mid-read) is skipped; any other per-detail failure propagates
|
|
192
|
+
* (conservative — a partial read cannot certify the union).
|
|
193
|
+
*/
|
|
194
|
+
function fetchRulesetsSurface(slug, ghFetch, cwd) {
|
|
195
|
+
// per_page=100 (the API max). Deliberately NOT `--paginate`: paged gh output
|
|
196
|
+
// concatenates JSON documents (or needs --slurp, a newer-gh dependency); the
|
|
197
|
+
// boundary sentinel below keeps the never-silently-undercount invariant
|
|
198
|
+
// instead (CR round 1) — a repo at the cap degrades to cannot-verify, never
|
|
199
|
+
// to a partial union.
|
|
200
|
+
const list = ghFetch(`/repos/${slug}/rulesets?includes_parents=true&per_page=100`, cwd);
|
|
201
|
+
if (list.outcome !== 'ok')
|
|
202
|
+
return toSnapshot(list);
|
|
203
|
+
// A non-array 200 payload must degrade to cannot-verify — coercing it to []
|
|
204
|
+
// would let the detector certify "no rulesets" from garbage (greptile P1).
|
|
205
|
+
if (!Array.isArray(list.data)) {
|
|
206
|
+
return { outcome: 'error', detail: 'unparseable rulesets list response' };
|
|
207
|
+
}
|
|
208
|
+
const summaries = list.data;
|
|
209
|
+
if (summaries.length >= 100) {
|
|
210
|
+
return {
|
|
211
|
+
outcome: 'error',
|
|
212
|
+
detail: 'rulesets list at the pagination boundary (100) — possible truncation, cannot certify the union',
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
const details = [];
|
|
216
|
+
for (const summary of summaries) {
|
|
217
|
+
const id = rulesetId(summary);
|
|
218
|
+
if (id === undefined) {
|
|
219
|
+
// An id-less entry cannot be detail-fetched — a partial read cannot
|
|
220
|
+
// certify the union (same posture as the per-detail failure below).
|
|
221
|
+
return {
|
|
222
|
+
outcome: 'error',
|
|
223
|
+
detail: 'ruleset list entry without a usable id — cannot enumerate the union',
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
const detail = ghFetch(`/repos/${slug}/rulesets/${id}`, cwd);
|
|
227
|
+
if (detail.outcome === 'ok') {
|
|
228
|
+
details.push(detail.data);
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
if (detail.outcome === 'not-found')
|
|
232
|
+
continue; // deleted mid-read — omit
|
|
233
|
+
// A non-404 per-detail failure means we cannot fully enumerate the union.
|
|
234
|
+
return {
|
|
235
|
+
outcome: detail.outcome,
|
|
236
|
+
...(detail.detail !== undefined ? { detail: detail.detail } : {}),
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
return { outcome: 'ok', data: details };
|
|
240
|
+
}
|
|
241
|
+
/** Extract a ruleset id from a list-summary object. */
|
|
242
|
+
function rulesetId(summary) {
|
|
243
|
+
if (typeof summary !== 'object' || summary === null)
|
|
244
|
+
return undefined;
|
|
245
|
+
const id = summary.id;
|
|
246
|
+
return typeof id === 'number' || typeof id === 'string' ? id : undefined;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* The classic-branch-protection surface: `GET …/branches/{default_branch}/protection`.
|
|
250
|
+
* The default branch comes from the repo-settings read; when that read failed (or
|
|
251
|
+
* omitted `default_branch`), the branch is unaddressable → propagate the
|
|
252
|
+
* repo-settings failure (or an auth-class `unknown` for a field-shy 200).
|
|
253
|
+
*/
|
|
254
|
+
function fetchBranchProtectionSurface(slug, repoResult, ghFetch, cwd) {
|
|
255
|
+
if (repoResult === undefined) {
|
|
256
|
+
return { outcome: 'error', detail: 'repo settings unavailable — default branch unresolved' };
|
|
257
|
+
}
|
|
258
|
+
if (repoResult.outcome !== 'ok')
|
|
259
|
+
return toSnapshot(repoResult);
|
|
260
|
+
const defaultBranch = defaultBranchOf(repoResult.data);
|
|
261
|
+
if (defaultBranch === undefined) {
|
|
262
|
+
return {
|
|
263
|
+
outcome: 'auth',
|
|
264
|
+
detail: 'repo 200 without default_branch — cannot address branch protection (auth-class)',
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
const encoded = defaultBranch
|
|
268
|
+
.split('/')
|
|
269
|
+
.map((seg) => encodeURIComponent(seg))
|
|
270
|
+
.join('/');
|
|
271
|
+
return toSnapshot(ghFetch(`/repos/${slug}/branches/${encoded}/protection`, cwd));
|
|
272
|
+
}
|
|
273
|
+
/** Read `default_branch` from a repo-settings payload, or undefined when absent/mis-typed. */
|
|
274
|
+
function defaultBranchOf(data) {
|
|
275
|
+
if (typeof data !== 'object' || data === null)
|
|
276
|
+
return undefined;
|
|
277
|
+
const branch = data.default_branch;
|
|
278
|
+
return typeof branch === 'string' && branch.length > 0 ? branch : undefined;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Build the default {@link GhFetch}: spawn `gh api <path>` via `safeExec` (arg
|
|
282
|
+
* array, no shell, bounded timeout) and classify the outcome. `safeExec` is
|
|
283
|
+
* injected (imported once in {@link resolveNetworkSnapshots}) so this module
|
|
284
|
+
* stays ESM-clean and test-injectable.
|
|
285
|
+
*/
|
|
286
|
+
function makeDefaultGhFetch(safeExec) {
|
|
287
|
+
return (apiPath, cwd) => {
|
|
288
|
+
let raw;
|
|
289
|
+
try {
|
|
290
|
+
raw = safeExec('gh', ['api', apiPath, '-H', 'Accept: application/vnd.github+json'], {
|
|
291
|
+
cwd,
|
|
292
|
+
timeout: GH_TIMEOUT_MS,
|
|
293
|
+
env: { ...process.env, GH_PROMPT_DISABLED: '1' },
|
|
294
|
+
});
|
|
295
|
+
// totem-context: a gh failure (no token, 4xx/5xx, offline, gh absent) is classified into a per-surface outcome (§14 clause 2/4), never rethrown — the sensor must degrade honestly, not crash.
|
|
296
|
+
}
|
|
297
|
+
catch (err) {
|
|
298
|
+
return classifyGhError(err);
|
|
299
|
+
}
|
|
300
|
+
try {
|
|
301
|
+
return { outcome: 'ok', data: JSON.parse(raw) };
|
|
302
|
+
// totem-context: an unparseable 200 body is a transient `error` outcome (→ unknown), not a throw.
|
|
303
|
+
}
|
|
304
|
+
catch {
|
|
305
|
+
return { outcome: 'error', detail: 'unparseable gh api response' };
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Classify a `gh api` failure into a network-read-only outcome (§14 clause 2/4):
|
|
311
|
+
* - spawn failure (gh absent) → `no-transport` (offline honest-absent).
|
|
312
|
+
* - 401 / bad credentials / not-authenticated → `auth`.
|
|
313
|
+
* - 403 → `auth` unless it is a rate-limit (→ `error`, transient).
|
|
314
|
+
* - 404 → `not-found`.
|
|
315
|
+
* - 5xx / timeout / DNS → `error`.
|
|
316
|
+
* - anything else → `error` (transient), never a drift verdict.
|
|
317
|
+
*/
|
|
318
|
+
function classifyGhError(err) {
|
|
319
|
+
const e = (err ?? {});
|
|
320
|
+
const stderr = `${e.stderr ?? ''} ${e.message ?? ''}`.toLowerCase();
|
|
321
|
+
if (isSpawnFailure(e)) {
|
|
322
|
+
return { outcome: 'no-transport', detail: 'gh not found — offline (§14 clause 4)' };
|
|
323
|
+
}
|
|
324
|
+
if (/http 401|bad credentials|requires authentication|gh auth login|not logged in/.test(stderr)) {
|
|
325
|
+
return { outcome: 'auth', detail: 'HTTP 401 / not authenticated' };
|
|
326
|
+
}
|
|
327
|
+
if (/http 403/.test(stderr)) {
|
|
328
|
+
return /rate limit/.test(stderr)
|
|
329
|
+
? { outcome: 'error', detail: 'HTTP 403 rate limited' }
|
|
330
|
+
: { outcome: 'auth', detail: 'HTTP 403 — under-privileged token' };
|
|
331
|
+
}
|
|
332
|
+
if (/http 404|not found/.test(stderr)) {
|
|
333
|
+
return { outcome: 'not-found', detail: 'HTTP 404' };
|
|
334
|
+
}
|
|
335
|
+
if (/http 5\d\d|timeout|timed out|could not resolve host|network is unreachable|econnreset|dial tcp/.test(stderr)) {
|
|
336
|
+
return { outcome: 'error', detail: 'transient / unreachable host' };
|
|
337
|
+
}
|
|
338
|
+
// A gh with no token at all often reports an auth hint without an HTTP code.
|
|
339
|
+
if (/authentication|gh auth|no token|token/.test(stderr)) {
|
|
340
|
+
return { outcome: 'auth', detail: 'not authenticated' };
|
|
341
|
+
}
|
|
342
|
+
return { outcome: 'error', detail: 'gh api failed' };
|
|
343
|
+
}
|
|
344
|
+
/** True when the throw is a spawn-level failure (gh binary missing), not an HTTP error. */
|
|
345
|
+
function isSpawnFailure(e) {
|
|
346
|
+
const causeCode = typeof e.cause === 'object' && e.cause !== null
|
|
347
|
+
? e.cause.code
|
|
348
|
+
: undefined;
|
|
349
|
+
if (causeCode === 'ENOENT')
|
|
350
|
+
return true;
|
|
351
|
+
const msg = `${e.message ?? ''}`;
|
|
352
|
+
// safeExec renders a spawn-level failure as `…: spawn failed` (no HTTP status).
|
|
353
|
+
return /enoent|spawn failed/i.test(msg) && !/http \d{3}/i.test(msg);
|
|
354
|
+
}
|
|
355
|
+
/** Build the default local-remote reader: `git remote get-url origin` (swallowed to undefined on failure). */
|
|
356
|
+
function makeDefaultReadRemote(safeExec) {
|
|
357
|
+
return (cwd) => {
|
|
358
|
+
try {
|
|
359
|
+
return safeExec('git', ['remote', 'get-url', 'origin'], { cwd, timeout: GH_TIMEOUT_MS });
|
|
360
|
+
// totem-context: a missing remote / non-git dir / absent git binary is a routine fall-through (the current repo is simply not probed), not a sensor failure.
|
|
361
|
+
}
|
|
362
|
+
catch {
|
|
363
|
+
return undefined;
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
//# sourceMappingURL=doctor-parity-fetch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor-parity-fetch.js","sourceRoot":"","sources":["../../src/commands/doctor-parity-fetch.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the §14 network-read-only fetch edge (`doctor-parity-fetch.ts`,
|
|
3
|
+
* strategy#962).
|
|
4
|
+
*
|
|
5
|
+
* Every test injects the transport ({@link GhFetch}) AND the local-remote reader
|
|
6
|
+
* ({@link ReadRemote}) seams, so NO `gh`/`git` subprocess ever spawns and there
|
|
7
|
+
* is zero live network. The suite covers: slug derivation, the roster (current +
|
|
8
|
+
* opt-in cross-repo, dedup), per-repo surface selection under consumers scoping,
|
|
9
|
+
* the rulesets list→detail assembly, branch-protection default-branch resolution,
|
|
10
|
+
* and the no-remote / no-transport degradations.
|
|
11
|
+
*/
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=doctor-parity-fetch.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor-parity-fetch.test.d.ts","sourceRoot":"","sources":["../../src/commands/doctor-parity-fetch.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG"}
|