@sdd-method/sdd-cli 1.29.2 → 1.30.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/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/snapshot/advance.d.ts +39 -0
- package/dist/lib/snapshot/advance.d.ts.map +1 -0
- package/dist/lib/snapshot/advance.js +253 -0
- package/dist/lib/snapshot/advance.js.map +1 -0
- package/dist/lib/snapshot/catalogue-diff.d.ts +21 -0
- package/dist/lib/snapshot/catalogue-diff.d.ts.map +1 -0
- package/dist/lib/snapshot/catalogue-diff.js +133 -0
- package/dist/lib/snapshot/catalogue-diff.js.map +1 -0
- package/dist/lib/snapshot/diff.d.ts +73 -0
- package/dist/lib/snapshot/diff.d.ts.map +1 -0
- package/dist/lib/snapshot/diff.js +177 -0
- package/dist/lib/snapshot/diff.js.map +1 -0
- package/dist/lib/snapshot/fold.d.ts +14 -0
- package/dist/lib/snapshot/fold.d.ts.map +1 -0
- package/dist/lib/snapshot/fold.js +106 -0
- package/dist/lib/snapshot/fold.js.map +1 -0
- package/dist/lib/snapshot/land.d.ts +21 -0
- package/dist/lib/snapshot/land.d.ts.map +1 -0
- package/dist/lib/snapshot/land.js +160 -0
- package/dist/lib/snapshot/land.js.map +1 -0
- package/dist/lib/snapshot/manifest.d.ts +59 -0
- package/dist/lib/snapshot/manifest.d.ts.map +1 -0
- package/dist/lib/snapshot/manifest.js +152 -0
- package/dist/lib/snapshot/manifest.js.map +1 -0
- package/dist/lib/snapshot/targets.d.ts +26 -0
- package/dist/lib/snapshot/targets.d.ts.map +1 -0
- package/dist/lib/snapshot/targets.js +121 -0
- package/dist/lib/snapshot/targets.js.map +1 -0
- package/dist/lib/snapshot/verify.d.ts +18 -0
- package/dist/lib/snapshot/verify.d.ts.map +1 -0
- package/dist/lib/snapshot/verify.js +82 -0
- package/dist/lib/snapshot/verify.js.map +1 -0
- package/dist/verbs/catalogue.d.ts.map +1 -1
- package/dist/verbs/catalogue.js +13 -0
- package/dist/verbs/catalogue.js.map +1 -1
- package/dist/verbs/snapshot.d.ts +3 -0
- package/dist/verbs/snapshot.d.ts.map +1 -0
- package/dist/verbs/snapshot.js +119 -0
- package/dist/verbs/snapshot.js.map +1 -0
- package/package.json +2 -2
- package/scripts/build-verify-package.mjs +23 -2
- package/scripts/verify-acceptance.sh +39 -2
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
// Implementation-set snapshot manifests (`<area>/<baseline-id>/snapshot.yaml`)
|
|
2
|
+
// and the file-level primitives the `snapshot` verbs share.
|
|
3
|
+
//
|
|
4
|
+
// The shape is the method's, not this module's: reverse/artifact-conventions.md
|
|
5
|
+
// "Implementation-Set Snapshots". Where the conventions REQUIRE a companion
|
|
6
|
+
// field (`carried_reason` beside `carried_from`, `reverification` beside
|
|
7
|
+
// `reverified_at`), readManifest refuses a manifest without it — a carried member
|
|
8
|
+
// with no reason cannot be told apart from an unrecorded removal.
|
|
9
|
+
import { createHash } from "node:crypto";
|
|
10
|
+
import { readdir, readFile, stat } from "node:fs/promises";
|
|
11
|
+
import { join, relative, sep } from "node:path";
|
|
12
|
+
import { parse } from "yaml";
|
|
13
|
+
export class SnapshotError extends Error {
|
|
14
|
+
}
|
|
15
|
+
const DATE = /^\d{4}-\d{2}-\d{2}$/;
|
|
16
|
+
export function isIsoDate(s) {
|
|
17
|
+
if (typeof s !== "string" || !DATE.test(s))
|
|
18
|
+
return false;
|
|
19
|
+
const d = new Date(`${s}T00:00:00Z`);
|
|
20
|
+
return !Number.isNaN(d.getTime()) && d.toISOString().slice(0, 10) === s;
|
|
21
|
+
}
|
|
22
|
+
/** `name-branch@rev` → { stem: "name-branch", revision: "rev" }. */
|
|
23
|
+
export function splitFolder(folder) {
|
|
24
|
+
const at = folder.lastIndexOf("@");
|
|
25
|
+
if (at <= 0 || at === folder.length - 1)
|
|
26
|
+
return null;
|
|
27
|
+
return { stem: folder.slice(0, at), revision: folder.slice(at + 1) };
|
|
28
|
+
}
|
|
29
|
+
/** Member folders (`*@*` directories) directly under a baseline directory. */
|
|
30
|
+
export async function memberFolders(baselineDir) {
|
|
31
|
+
const out = [];
|
|
32
|
+
for (const e of await readdir(baselineDir, { withFileTypes: true })) {
|
|
33
|
+
if (e.isDirectory() && splitFolder(e.name) !== null)
|
|
34
|
+
out.push(e.name);
|
|
35
|
+
}
|
|
36
|
+
return out.sort();
|
|
37
|
+
}
|
|
38
|
+
/** Dated baseline ids under an area, ascending. */
|
|
39
|
+
export async function baselineIds(areaRoot) {
|
|
40
|
+
const out = [];
|
|
41
|
+
for (const e of await readdir(areaRoot, { withFileTypes: true })) {
|
|
42
|
+
if (e.isDirectory() && isIsoDate(e.name))
|
|
43
|
+
out.push(e.name);
|
|
44
|
+
}
|
|
45
|
+
return out.sort();
|
|
46
|
+
}
|
|
47
|
+
export async function exists(p) {
|
|
48
|
+
try {
|
|
49
|
+
await stat(p);
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
export function manifestPath(areaRoot, id) {
|
|
57
|
+
return join(areaRoot, id, "snapshot.yaml");
|
|
58
|
+
}
|
|
59
|
+
export async function readManifest(areaRoot, id) {
|
|
60
|
+
const p = manifestPath(areaRoot, id);
|
|
61
|
+
if (!(await exists(p)))
|
|
62
|
+
throw new SnapshotError(`no manifest at ${p}`);
|
|
63
|
+
const raw = parse(await readFile(p, "utf8"));
|
|
64
|
+
if (raw === null || typeof raw !== "object")
|
|
65
|
+
throw new SnapshotError(`${p}: not a mapping`);
|
|
66
|
+
const problems = [];
|
|
67
|
+
if (raw.snapshot_id !== id)
|
|
68
|
+
problems.push(`snapshot_id '${String(raw.snapshot_id)}' does not match its folder '${id}'`);
|
|
69
|
+
if (!isIsoDate(raw.observed_at))
|
|
70
|
+
problems.push("observed_at must be YYYY-MM-DD");
|
|
71
|
+
if (typeof raw.implementation !== "string" || raw.implementation === "")
|
|
72
|
+
problems.push("implementation missing");
|
|
73
|
+
if (raw.prior_snapshot !== null && raw.prior_snapshot !== undefined && !isIsoDate(raw.prior_snapshot)) {
|
|
74
|
+
problems.push("prior_snapshot must be a baseline id or null");
|
|
75
|
+
}
|
|
76
|
+
if (!Array.isArray(raw.members))
|
|
77
|
+
problems.push("members must be a list");
|
|
78
|
+
// A hand-written revision of digits only (`50834756022`) parses as a NUMBER, and
|
|
79
|
+
// a strict comparison with the folder's string suffix then calls an unchanged
|
|
80
|
+
// member changed. Identity fields are strings, whatever YAML made of them.
|
|
81
|
+
for (const m of raw.members ?? []) {
|
|
82
|
+
if (m && typeof m === "object") {
|
|
83
|
+
for (const f of ["canonical_repo_key", "exported_folder_name", "source_revision"]) {
|
|
84
|
+
if (typeof m[f] === "number")
|
|
85
|
+
m[f] = String(m[f]);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
for (const r of raw.members_removed ?? []) {
|
|
90
|
+
if (r && typeof r.last_revision === "number")
|
|
91
|
+
r.last_revision = String(r.last_revision);
|
|
92
|
+
}
|
|
93
|
+
for (const [i, m] of (raw.members ?? []).entries()) {
|
|
94
|
+
const at = `members[${i}] (${m?.canonical_repo_key ?? "?"})`;
|
|
95
|
+
if (!m?.canonical_repo_key || !m.exported_folder_name || !m.source_revision) {
|
|
96
|
+
problems.push(`${at}: canonical_repo_key, exported_folder_name and source_revision are required`);
|
|
97
|
+
}
|
|
98
|
+
if (m?.carried_from !== undefined && !m.carried_reason)
|
|
99
|
+
problems.push(`${at}: carried_from requires carried_reason`);
|
|
100
|
+
if (m?.reverified_at !== undefined && !m.reverification)
|
|
101
|
+
problems.push(`${at}: reverified_at requires reverification`);
|
|
102
|
+
}
|
|
103
|
+
for (const [i, r] of (raw.members_removed ?? []).entries()) {
|
|
104
|
+
if (!r?.canonical_repo_key || !r.last_revision || !r.reason) {
|
|
105
|
+
problems.push(`members_removed[${i}]: canonical_repo_key, last_revision and reason are required`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (problems.length > 0)
|
|
109
|
+
throw new SnapshotError(`${p}:\n - ${problems.join("\n - ")}`);
|
|
110
|
+
return raw;
|
|
111
|
+
}
|
|
112
|
+
/** Where a member's bytes live: its own baseline, or the one it was carried from. */
|
|
113
|
+
export function memberBytesDir(areaRoot, manifest, m) {
|
|
114
|
+
return join(areaRoot, m.carried_from ?? manifest.snapshot_id, m.exported_folder_name);
|
|
115
|
+
}
|
|
116
|
+
/** relative path → sha256 for every file under a directory. */
|
|
117
|
+
export async function hashTree(root) {
|
|
118
|
+
const out = new Map();
|
|
119
|
+
async function walk(dir) {
|
|
120
|
+
for (const e of await readdir(dir, { withFileTypes: true })) {
|
|
121
|
+
const full = join(dir, e.name);
|
|
122
|
+
if (e.isDirectory()) {
|
|
123
|
+
if (e.name === ".git")
|
|
124
|
+
continue;
|
|
125
|
+
await walk(full);
|
|
126
|
+
}
|
|
127
|
+
else if (e.isFile()) {
|
|
128
|
+
const h = createHash("sha256").update(await readFile(full)).digest("hex");
|
|
129
|
+
out.set(relative(root, full).split(sep).join("/"), h);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
await walk(root);
|
|
134
|
+
return out;
|
|
135
|
+
}
|
|
136
|
+
export function diffTrees(before, after) {
|
|
137
|
+
const added = [];
|
|
138
|
+
const removed = [];
|
|
139
|
+
const modified = [];
|
|
140
|
+
for (const [p, h] of after) {
|
|
141
|
+
const b = before.get(p);
|
|
142
|
+
if (b === undefined)
|
|
143
|
+
added.push(p);
|
|
144
|
+
else if (b !== h)
|
|
145
|
+
modified.push(p);
|
|
146
|
+
}
|
|
147
|
+
for (const p of before.keys())
|
|
148
|
+
if (!after.has(p))
|
|
149
|
+
removed.push(p);
|
|
150
|
+
return { added: added.sort(), removed: removed.sort(), modified: modified.sort() };
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../../src/lib/snapshot/manifest.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,4DAA4D;AAC5D,EAAE;AACF,gFAAgF;AAChF,4EAA4E;AAC5E,yEAAyE;AACzE,kFAAkF;AAClF,kEAAkE;AAElE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EAAE,KAAK,EAAE,MAAM,MAAM,CAAC;AAuC7B,MAAM,OAAO,aAAc,SAAQ,KAAK;CAAG;AAE3C,MAAM,IAAI,GAAG,qBAAqB,CAAC;AAEnC,MAAM,UAAU,SAAS,CAAC,CAAU;IAClC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IACzD,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IACrC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC;AAC1E,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,WAAW,CAAC,MAAc;IACxC,MAAM,EAAE,GAAG,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACnC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACrD,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC;AACvE,CAAC;AAED,8EAA8E;AAC9E,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,WAAmB;IACrD,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,MAAM,CAAC,IAAI,MAAM,OAAO,CAAC,WAAW,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QACpE,IAAI,CAAC,CAAC,WAAW,EAAE,IAAI,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,IAAI;YAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACxE,CAAC;IACD,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;AACpB,CAAC;AAED,mDAAmD;AACnD,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,QAAgB;IAChD,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,MAAM,CAAC,IAAI,MAAM,OAAO,CAAC,QAAQ,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QACjE,IAAI,CAAC,CAAC,WAAW,EAAE,IAAI,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;YAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAC7D,CAAC;IACD,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;AACpB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,CAAS;IACpC,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC;QACd,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,QAAgB,EAAE,EAAU;IACvD,OAAO,IAAI,CAAC,QAAQ,EAAE,EAAE,EAAE,eAAe,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,QAAgB,EAAE,EAAU;IAC7D,MAAM,CAAC,GAAG,YAAY,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IACrC,IAAI,CAAC,CAAC,MAAM,MAAM,CAAC,CAAC,CAAC,CAAC;QAAE,MAAM,IAAI,aAAa,CAAC,kBAAkB,CAAC,EAAE,CAAC,CAAC;IACvE,MAAM,GAAG,GAAG,KAAK,CAAC,MAAM,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC,CAAqC,CAAC;IACjF,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,MAAM,IAAI,aAAa,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IAC5F,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,IAAI,GAAG,CAAC,WAAW,KAAK,EAAE;QAAE,QAAQ,CAAC,IAAI,CAAC,gBAAgB,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,gCAAgC,EAAE,GAAG,CAAC,CAAC;IACxH,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,WAAW,CAAC;QAAE,QAAQ,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAC;IACjF,IAAI,OAAO,GAAG,CAAC,cAAc,KAAK,QAAQ,IAAI,GAAG,CAAC,cAAc,KAAK,EAAE;QAAE,QAAQ,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;IACjH,IAAI,GAAG,CAAC,cAAc,KAAK,IAAI,IAAI,GAAG,CAAC,cAAc,KAAK,SAAS,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,CAAC;QACtG,QAAQ,CAAC,IAAI,CAAC,8CAA8C,CAAC,CAAC;IAChE,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC;QAAE,QAAQ,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;IACzE,iFAAiF;IACjF,8EAA8E;IAC9E,2EAA2E;IAC3E,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;QAClC,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;YAC/B,KAAK,MAAM,CAAC,IAAI,CAAC,oBAAoB,EAAE,sBAAsB,EAAE,iBAAiB,CAAU,EAAE,CAAC;gBAC3F,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,QAAQ;oBAAG,CAAwC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC5F,CAAC;QACH,CAAC;IACH,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,eAAe,IAAI,EAAE,EAAE,CAAC;QAC1C,IAAI,CAAC,IAAI,OAAO,CAAC,CAAC,aAAa,KAAK,QAAQ;YAAG,CAAwC,CAAC,aAAa,GAAG,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC;IAClI,CAAC;IACD,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;QACnD,MAAM,EAAE,GAAG,WAAW,CAAC,MAAM,CAAC,EAAE,kBAAkB,IAAI,GAAG,GAAG,CAAC;QAC7D,IAAI,CAAC,CAAC,EAAE,kBAAkB,IAAI,CAAC,CAAC,CAAC,oBAAoB,IAAI,CAAC,CAAC,CAAC,eAAe,EAAE,CAAC;YAC5E,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,6EAA6E,CAAC,CAAC;QACpG,CAAC;QACD,IAAI,CAAC,EAAE,YAAY,KAAK,SAAS,IAAI,CAAC,CAAC,CAAC,cAAc;YAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,wCAAwC,CAAC,CAAC;QACrH,IAAI,CAAC,EAAE,aAAa,KAAK,SAAS,IAAI,CAAC,CAAC,CAAC,cAAc;YAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,yCAAyC,CAAC,CAAC;IACzH,CAAC;IACD,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,eAAe,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;QAC3D,IAAI,CAAC,CAAC,EAAE,kBAAkB,IAAI,CAAC,CAAC,CAAC,aAAa,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;YAC5D,QAAQ,CAAC,IAAI,CAAC,mBAAmB,CAAC,8DAA8D,CAAC,CAAC;QACpG,CAAC;IACH,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;QAAE,MAAM,IAAI,aAAa,CAAC,GAAG,CAAC,UAAU,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC1F,OAAO,GAAuB,CAAC;AACjC,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,cAAc,CAAC,QAAgB,EAAE,QAA0B,EAAE,CAAiB;IAC5F,OAAO,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,YAAY,IAAI,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC,oBAAoB,CAAC,CAAC;AACxF,CAAC;AAED,+DAA+D;AAC/D,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,IAAY;IACzC,MAAM,GAAG,GAAG,IAAI,GAAG,EAAkB,CAAC;IACtC,KAAK,UAAU,IAAI,CAAC,GAAW;QAC7B,KAAK,MAAM,CAAC,IAAI,MAAM,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;YAC5D,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;YAC/B,IAAI,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;gBACpB,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM;oBAAE,SAAS;gBAChC,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;YACnB,CAAC;iBAAM,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;gBACtB,MAAM,CAAC,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBAC1E,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;YACxD,CAAC;QACH,CAAC;IACH,CAAC;IACD,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;IACjB,OAAO,GAAG,CAAC;AACb,CAAC;AAQD,MAAM,UAAU,SAAS,CAAC,MAA2B,EAAE,KAA0B;IAC/E,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,KAAK,EAAE,CAAC;QAC3B,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACxB,IAAI,CAAC,KAAK,SAAS;YAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;aAC9B,IAAI,CAAC,KAAK,CAAC;YAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACrC,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,EAAE;QAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;YAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClE,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC;AACrF,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type DeltaFinding } from "./diff.js";
|
|
2
|
+
type Writer = (s: string) => void;
|
|
3
|
+
export interface TargetsInputs {
|
|
4
|
+
sddPath: string;
|
|
5
|
+
areaRoot: string;
|
|
6
|
+
id: string;
|
|
7
|
+
}
|
|
8
|
+
export interface Target {
|
|
9
|
+
artefact: string;
|
|
10
|
+
grain: "file" | "repo";
|
|
11
|
+
cited: string;
|
|
12
|
+
}
|
|
13
|
+
/** Every text artefact of the SDD, excluding the method's own copies and generated catalogues. */
|
|
14
|
+
export declare function sddArtefacts(sddPath: string): Promise<Map<string, string>>;
|
|
15
|
+
/**
|
|
16
|
+
* `shared` holds paths that more than one member changed (`helm/values.yaml`,
|
|
17
|
+
* `src/main/resources/application.properties`): a citation of one of those names
|
|
18
|
+
* nobody in particular, so it is file grain only on a line that also names this
|
|
19
|
+
* member. Without that, a citation of another member's copy reads as precise.
|
|
20
|
+
*/
|
|
21
|
+
export declare function routeFinding(f: Pick<DeltaFinding, "member" | "files">, artefacts: ReadonlyMap<string, string>, shared?: ReadonlySet<string>): Target[];
|
|
22
|
+
export declare function runSnapshotTargets(inputs: TargetsInputs, write: Writer): Promise<{
|
|
23
|
+
exitCode: number;
|
|
24
|
+
}>;
|
|
25
|
+
export {};
|
|
26
|
+
//# sourceMappingURL=targets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"targets.d.ts","sourceRoot":"","sources":["../../../src/lib/snapshot/targets.ts"],"names":[],"mappings":"AAeA,OAAO,EAAa,KAAK,YAAY,EAAE,MAAM,WAAW,CAAC;AAGzD,KAAK,MAAM,GAAG,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;AAElC,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,MAAM;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;CACf;AAMD,kGAAkG;AAClG,wBAAsB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAgBhF;AAMD;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,CAAC,EAAE,IAAI,CAAC,YAAY,EAAE,QAAQ,GAAG,OAAO,CAAC,EACzC,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,EACtC,MAAM,GAAE,WAAW,CAAC,MAAM,CAAa,GACtC,MAAM,EAAE,CAkBV;AAED,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAoD5G"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// `sdd-cli snapshot targets` — route each delta.yaml finding to the SDD
|
|
2
|
+
// artefacts it grounds, recording the GRAIN each route was found at:
|
|
3
|
+
//
|
|
4
|
+
// file — the artefact cites a changed file's path (feature-grounding style
|
|
5
|
+
// evidence citing `src/…`): the route is as precise as the citation;
|
|
6
|
+
// repo — the artefact only names the member's canonical_repo_key: the route
|
|
7
|
+
// reaches everything that repo grounds, and must not read as precise.
|
|
8
|
+
//
|
|
9
|
+
// Findings whose targets are already set are left alone (routing refined by
|
|
10
|
+
// hand is judgement). Findings nothing cites are listed as unroutable, never
|
|
11
|
+
// dropped. Writes back into delta.yaml preserving everything else in it.
|
|
12
|
+
import { readdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
13
|
+
import { join, relative, sep } from "node:path";
|
|
14
|
+
import { parseDocument } from "yaml";
|
|
15
|
+
import { deltaPath } from "./diff.js";
|
|
16
|
+
import { SnapshotError, exists } from "./manifest.js";
|
|
17
|
+
const TEXT = /\.(md|ya?ml|json|txt|adoc|mermaid|puml|csv)$/i;
|
|
18
|
+
const SKIP_DIRS = new Set([".git", "node_modules", ".sdd", "catalogues"]);
|
|
19
|
+
const MAX_BYTES = 4 * 1024 * 1024;
|
|
20
|
+
/** Every text artefact of the SDD, excluding the method's own copies and generated catalogues. */
|
|
21
|
+
export async function sddArtefacts(sddPath) {
|
|
22
|
+
const out = new Map();
|
|
23
|
+
async function walk(dir) {
|
|
24
|
+
for (const e of await readdir(dir, { withFileTypes: true })) {
|
|
25
|
+
const full = join(dir, e.name);
|
|
26
|
+
const rel = relative(sddPath, full).split(sep).join("/");
|
|
27
|
+
if (e.isDirectory()) {
|
|
28
|
+
if (SKIP_DIRS.has(e.name) || rel === "docs/method")
|
|
29
|
+
continue;
|
|
30
|
+
await walk(full);
|
|
31
|
+
}
|
|
32
|
+
else if (e.isFile() && TEXT.test(e.name) && (await stat(full)).size <= MAX_BYTES) {
|
|
33
|
+
out.set(rel, await readFile(full, "utf8"));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
await walk(sddPath);
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
// A path needs two segments to be a citation rather than a coincidence:
|
|
41
|
+
// `pom.xml` alone appears in every Java SDD.
|
|
42
|
+
const citable = (p) => p.includes("/");
|
|
43
|
+
/**
|
|
44
|
+
* `shared` holds paths that more than one member changed (`helm/values.yaml`,
|
|
45
|
+
* `src/main/resources/application.properties`): a citation of one of those names
|
|
46
|
+
* nobody in particular, so it is file grain only on a line that also names this
|
|
47
|
+
* member. Without that, a citation of another member's copy reads as precise.
|
|
48
|
+
*/
|
|
49
|
+
export function routeFinding(f, artefacts, shared = new Set()) {
|
|
50
|
+
const targets = new Map();
|
|
51
|
+
const paths = [...f.files.added, ...f.files.removed, ...f.files.modified].filter(citable);
|
|
52
|
+
// The key alone, or in export-folder form (`<key>-master@<rev>`): a hyphen may
|
|
53
|
+
// follow it only as a branch suffix, so `tcu` never matches inside `tcu-collector`.
|
|
54
|
+
const keyRe = new RegExp(`(^|[^A-Za-z0-9_-])${f.member.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(-(master|main|develop|trunk)@|[^A-Za-z0-9_-]|$)`);
|
|
55
|
+
const cites = (text, p) => shared.has(p) ? text.split("\n").some((line) => line.includes(p) && keyRe.test(line)) : text.includes(p);
|
|
56
|
+
for (const [rel, text] of artefacts) {
|
|
57
|
+
const hit = paths.find((p) => cites(text, p));
|
|
58
|
+
if (hit)
|
|
59
|
+
targets.set(rel, { artefact: rel, grain: "file", cited: hit });
|
|
60
|
+
}
|
|
61
|
+
for (const [rel, text] of artefacts) {
|
|
62
|
+
if (!targets.has(rel) && keyRe.test(text))
|
|
63
|
+
targets.set(rel, { artefact: rel, grain: "repo", cited: f.member });
|
|
64
|
+
}
|
|
65
|
+
return [...targets.values()].sort((a, b) => (a.grain === b.grain ? a.artefact.localeCompare(b.artefact) : a.grain === "file" ? -1 : 1));
|
|
66
|
+
}
|
|
67
|
+
export async function runSnapshotTargets(inputs, write) {
|
|
68
|
+
const dp = deltaPath(inputs.areaRoot, inputs.id);
|
|
69
|
+
if (!(await exists(dp))) {
|
|
70
|
+
write(`REFUSED: no ${dp} — run snapshot diff first\n`);
|
|
71
|
+
return { exitCode: 1 };
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
const doc = parseDocument(await readFile(dp, "utf8"));
|
|
75
|
+
const findings = doc.toJS().findings ?? [];
|
|
76
|
+
const artefacts = await sddArtefacts(inputs.sddPath);
|
|
77
|
+
const owners = new Map();
|
|
78
|
+
for (const f of findings) {
|
|
79
|
+
for (const p of [...(f.files?.added ?? []), ...(f.files?.removed ?? []), ...(f.files?.modified ?? [])]) {
|
|
80
|
+
if (!owners.has(p))
|
|
81
|
+
owners.set(p, new Set());
|
|
82
|
+
owners.get(p).add(f.member);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const shared = new Set([...owners].filter(([, m]) => m.size > 1).map(([p]) => p));
|
|
86
|
+
let routed = 0;
|
|
87
|
+
let kept = 0;
|
|
88
|
+
const unroutable = [];
|
|
89
|
+
const grains = { file: 0, repo: 0 };
|
|
90
|
+
for (const [i, f] of findings.entries()) {
|
|
91
|
+
if (Array.isArray(f.targets) && f.targets.length > 0) {
|
|
92
|
+
kept++;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
const t = routeFinding(f, artefacts, shared);
|
|
96
|
+
if (t.length === 0) {
|
|
97
|
+
unroutable.push(f.id);
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
for (const x of t)
|
|
101
|
+
grains[x.grain]++;
|
|
102
|
+
doc.setIn(["findings", i, "targets"], doc.createNode(t));
|
|
103
|
+
routed++;
|
|
104
|
+
}
|
|
105
|
+
await writeFile(dp, doc.toString({ lineWidth: 0 }));
|
|
106
|
+
write(`${routed} finding(s) routed (${grains.file} file-grain, ${grains.repo} repo-grain target(s)); ${kept} already targeted, left alone; ` +
|
|
107
|
+
`${unroutable.length} unroutable${unroutable.length ? `: ${unroutable.join(", ")}` : ""}\n`);
|
|
108
|
+
if (grains.repo > 0) {
|
|
109
|
+
write("note: a repo-grain target reaches everything that repo grounds — narrow it by hand before re-reversing\n");
|
|
110
|
+
}
|
|
111
|
+
return { exitCode: 0 };
|
|
112
|
+
}
|
|
113
|
+
catch (e) {
|
|
114
|
+
if (e instanceof SnapshotError) {
|
|
115
|
+
write(`REFUSED: ${e.message}\n`);
|
|
116
|
+
return { exitCode: 1 };
|
|
117
|
+
}
|
|
118
|
+
throw e;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=targets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"targets.js","sourceRoot":"","sources":["../../../src/lib/snapshot/targets.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
type Writer = (s: string) => void;
|
|
2
|
+
export interface Waiver {
|
|
3
|
+
who: string;
|
|
4
|
+
why: string;
|
|
5
|
+
date: string;
|
|
6
|
+
}
|
|
7
|
+
export interface VerifyInputs {
|
|
8
|
+
areaRoot: string;
|
|
9
|
+
id: string;
|
|
10
|
+
confirmed?: ReadonlyMap<string, string>;
|
|
11
|
+
waiver?: Partial<Waiver>;
|
|
12
|
+
today?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function runSnapshotVerify(inputs: VerifyInputs, write: Writer): Promise<{
|
|
15
|
+
exitCode: number;
|
|
16
|
+
}>;
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=verify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../../src/lib/snapshot/verify.ts"],"names":[],"mappings":"AAoBA,KAAK,MAAM,GAAG,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;AAElC,MAAM,WAAW,MAAM;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,MAAM,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAoD1G"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// `sdd-cli snapshot verify` — T0 provenance before the diff is trusted
|
|
2
|
+
// (delta-reverse.md §3).
|
|
3
|
+
//
|
|
4
|
+
// Two legs. LOCAL, checked here: each member folder exists, its `@revision`
|
|
5
|
+
// suffix matches source_revision, and where the export kept its `.git`, HEAD is
|
|
6
|
+
// that revision. CANONICAL — that the revision is a real commit on the canonical
|
|
7
|
+
// repo — cannot be proven from an export, so it is supplied: `--confirmed
|
|
8
|
+
// key=<how it was checked>` per member, or one recorded `--waiver` (who, why,
|
|
9
|
+
// date). A member with neither is "cannot assert", which FAILS: could-not-check
|
|
10
|
+
// is never silently checked. The result is written beside the baseline as
|
|
11
|
+
// provenance.yaml, so the waiver travels with the baseline it excuses.
|
|
12
|
+
import { execFile as execFileCb } from "node:child_process";
|
|
13
|
+
import { writeFile } from "node:fs/promises";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import { promisify } from "node:util";
|
|
16
|
+
import { stringify } from "yaml";
|
|
17
|
+
import { SnapshotError, exists, isIsoDate, memberBytesDir, readManifest, splitFolder } from "./manifest.js";
|
|
18
|
+
const execFile = promisify(execFileCb);
|
|
19
|
+
export async function runSnapshotVerify(inputs, write) {
|
|
20
|
+
try {
|
|
21
|
+
const m = await readManifest(inputs.areaRoot, inputs.id);
|
|
22
|
+
const w = inputs.waiver;
|
|
23
|
+
if (w && (!w.who || !w.why || !isIsoDate(w.date))) {
|
|
24
|
+
throw new SnapshotError("--waiver needs who=…,why=…,date=YYYY-MM-DD — an unattributed waiver is a skip with a name on it");
|
|
25
|
+
}
|
|
26
|
+
const rows = [];
|
|
27
|
+
const failures = [];
|
|
28
|
+
for (const mem of m.members) {
|
|
29
|
+
const dir = memberBytesDir(inputs.areaRoot, m, mem);
|
|
30
|
+
let local = "ok";
|
|
31
|
+
if (!(await exists(dir)))
|
|
32
|
+
local = `FAIL: ${dir} does not exist`;
|
|
33
|
+
else if (splitFolder(mem.exported_folder_name)?.revision !== mem.source_revision) {
|
|
34
|
+
local = `FAIL: folder revision does not match source_revision ${mem.source_revision}`;
|
|
35
|
+
}
|
|
36
|
+
else if (await exists(join(dir, ".git"))) {
|
|
37
|
+
const head = (await execFile("git", ["-C", dir, "rev-parse", "HEAD"])).stdout.trim();
|
|
38
|
+
if (!head.startsWith(mem.source_revision))
|
|
39
|
+
local = `FAIL: .git HEAD ${head} is not ${mem.source_revision}`;
|
|
40
|
+
else
|
|
41
|
+
local = "ok (git HEAD matches)";
|
|
42
|
+
}
|
|
43
|
+
const how = inputs.confirmed?.get(mem.canonical_repo_key);
|
|
44
|
+
const canonical = how ? `confirmed: ${how}` : w ? "waived" : "CANNOT ASSERT";
|
|
45
|
+
if (local.startsWith("FAIL"))
|
|
46
|
+
failures.push(`${mem.canonical_repo_key}: ${local}`);
|
|
47
|
+
if (canonical === "CANNOT ASSERT")
|
|
48
|
+
failures.push(`${mem.canonical_repo_key}: canonical provenance not confirmed and no waiver`);
|
|
49
|
+
rows.push({ canonical_repo_key: mem.canonical_repo_key, local, canonical });
|
|
50
|
+
}
|
|
51
|
+
for (const k of inputs.confirmed?.keys() ?? []) {
|
|
52
|
+
if (!m.members.some((x) => x.canonical_repo_key === k))
|
|
53
|
+
throw new SnapshotError(`--confirmed ${k}: not a member of ${inputs.id}`);
|
|
54
|
+
}
|
|
55
|
+
const report = {
|
|
56
|
+
baseline: inputs.id,
|
|
57
|
+
verified_at: inputs.today ?? new Date().toISOString().slice(0, 10),
|
|
58
|
+
result: failures.length === 0 ? (w ? "pass-with-waiver" : "pass") : "fail",
|
|
59
|
+
...(w ? { waiver: w } : {}),
|
|
60
|
+
members: rows,
|
|
61
|
+
};
|
|
62
|
+
await writeFile(join(inputs.areaRoot, inputs.id, "provenance.yaml"), stringify(report, { lineWidth: 0 }));
|
|
63
|
+
for (const r of rows)
|
|
64
|
+
write(`${r.canonical_repo_key}: local ${r.local}; canonical ${r.canonical}\n`);
|
|
65
|
+
if (w)
|
|
66
|
+
write(`WAIVER in force — ${w.who}, ${w.date}: ${w.why}\n`);
|
|
67
|
+
if (failures.length > 0) {
|
|
68
|
+
write(`FAIL: ${failures.length} problem(s); written to provenance.yaml\n`);
|
|
69
|
+
return { exitCode: 1 };
|
|
70
|
+
}
|
|
71
|
+
write(`${report.result}: ${rows.length} member(s)\n`);
|
|
72
|
+
return { exitCode: 0 };
|
|
73
|
+
}
|
|
74
|
+
catch (e) {
|
|
75
|
+
if (e instanceof SnapshotError) {
|
|
76
|
+
write(`REFUSED: ${e.message}\n`);
|
|
77
|
+
return { exitCode: 1 };
|
|
78
|
+
}
|
|
79
|
+
throw e;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=verify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../../../src/lib/snapshot/verify.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catalogue.d.ts","sourceRoot":"","sources":["../../src/verbs/catalogue.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"catalogue.d.ts","sourceRoot":"","sources":["../../src/verbs/catalogue.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAkCzC,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAgKxD"}
|
package/dist/verbs/catalogue.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
1
2
|
import { registerCatalogueBaselineCheck, registerCatalogueImpact, registerCatalogueValidate, registerCatalogueVersion, } from "./catalogue-verify.js";
|
|
2
3
|
import { runCatalogueBuild, runCatalogueAggregate, runCatalogueMigrationMap, } from "../lib/catalogue/index.js";
|
|
3
4
|
import { runCatalogueGraph } from "../lib/catalogue/graph-run.js";
|
|
5
|
+
import { runCatalogueDiff } from "../lib/snapshot/catalogue-diff.js";
|
|
4
6
|
export function registerCatalogue(program) {
|
|
5
7
|
const catalogue = program
|
|
6
8
|
.command("catalogue")
|
|
@@ -40,6 +42,17 @@ export function registerCatalogue(program) {
|
|
|
40
42
|
process.exit(result.exitCode);
|
|
41
43
|
});
|
|
42
44
|
registerCatalogueValidate(catalogue);
|
|
45
|
+
catalogue
|
|
46
|
+
.command("diff")
|
|
47
|
+
.description("Build the catalogues at a git ref and at the working tree in ONE process (so one CLI), and attribute the change at file grain: every SDD source file changed since the ref must be a target of some delta.yaml finding. Exit 1 on any unattributed change. Writes nothing into the SDD.")
|
|
48
|
+
.requiredOption("--from <ref>", "the git ref of the SDD before the snapshot update")
|
|
49
|
+
.requiredOption("--attribute <delta.yaml>", "the snapshot's delta.yaml, whose findings' targets attribute the change")
|
|
50
|
+
.option("--sdd-path <path>", "Path to the SDD repo root (defaults to cwd)")
|
|
51
|
+
.action(async (opts) => {
|
|
52
|
+
const r = await runCatalogueDiff({ sddPath: resolve(opts.sddPath ?? process.cwd()), from: opts.from, attribute: resolve(opts.attribute) }, (s) => process.stdout.write(s));
|
|
53
|
+
if (r.exitCode !== 0)
|
|
54
|
+
process.exit(r.exitCode);
|
|
55
|
+
});
|
|
43
56
|
catalogue
|
|
44
57
|
.command("graph")
|
|
45
58
|
.description("Emit the graph projection (typed node/edge JSON Lines) derived from built catalogues (M-ADR 0205 §4.6) — regenerated every run, digest-stamped, never committed")
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catalogue.js","sourceRoot":"","sources":["../../src/verbs/catalogue.ts"],"names":[],"mappings":"AACA,OAAO,EACL,8BAA8B,EAC9B,uBAAuB,EACvB,yBAAyB,EACzB,wBAAwB,GACzB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,wBAAwB,GACzB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"catalogue.js","sourceRoot":"","sources":["../../src/verbs/catalogue.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot.d.ts","sourceRoot":"","sources":["../../src/verbs/snapshot.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAsCzC,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA+HvD"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { runSnapshotAdvance } from "../lib/snapshot/advance.js";
|
|
3
|
+
import { runSnapshotDiff } from "../lib/snapshot/diff.js";
|
|
4
|
+
import { runSnapshotFold } from "../lib/snapshot/fold.js";
|
|
5
|
+
import { runSnapshotLand } from "../lib/snapshot/land.js";
|
|
6
|
+
import { runSnapshotTargets } from "../lib/snapshot/targets.js";
|
|
7
|
+
import { runSnapshotVerify } from "../lib/snapshot/verify.js";
|
|
8
|
+
const out = (s) => process.stdout.write(s);
|
|
9
|
+
const exit = ({ exitCode }) => {
|
|
10
|
+
if (exitCode !== 0)
|
|
11
|
+
process.exit(exitCode);
|
|
12
|
+
};
|
|
13
|
+
/** Repeatable `key=value` option → Map. The value may itself contain `=`. */
|
|
14
|
+
function collectPairs(value, prior) {
|
|
15
|
+
const i = value.indexOf("=");
|
|
16
|
+
if (i <= 0)
|
|
17
|
+
throw new Error(`expected key=value, got '${value}'`);
|
|
18
|
+
prior.set(value.slice(0, i), value.slice(i + 1));
|
|
19
|
+
return prior;
|
|
20
|
+
}
|
|
21
|
+
function collectList(value, prior) {
|
|
22
|
+
prior.push(value);
|
|
23
|
+
return prior;
|
|
24
|
+
}
|
|
25
|
+
/** `a=1,b=2` → object (for records like the credential scan and a waiver). */
|
|
26
|
+
function record(value) {
|
|
27
|
+
if (value === undefined)
|
|
28
|
+
return undefined;
|
|
29
|
+
const o = {};
|
|
30
|
+
for (const part of value.split(",")) {
|
|
31
|
+
const i = part.indexOf("=");
|
|
32
|
+
if (i > 0)
|
|
33
|
+
o[part.slice(0, i).trim()] = part.slice(i + 1).trim();
|
|
34
|
+
}
|
|
35
|
+
return o;
|
|
36
|
+
}
|
|
37
|
+
export function registerSnapshot(program) {
|
|
38
|
+
const snapshot = program
|
|
39
|
+
.command("snapshot")
|
|
40
|
+
.description("Implementation-set snapshots: the deterministic steps of delta-reverse (M-ADR 0180 §2.7, M-ADR 0181 §2.5), run by the reverse-snapshot-delta skill");
|
|
41
|
+
snapshot
|
|
42
|
+
.command("fold <area>")
|
|
43
|
+
.description("Move a flat <repo>@<rev>/ area into its first dated baseline, write its manifest, and list every reference to the old paths")
|
|
44
|
+
.option("--id <date>", "the date the flat exports were OBSERVED (YYYY-MM-DD); no default")
|
|
45
|
+
.option("--implementation <id>", "the durable system identity")
|
|
46
|
+
.option("--key <folder=key>", "canonical_repo_key for a folder (repeatable)", collectPairs, new Map())
|
|
47
|
+
.option("--scan <root>", "a root to search for references to the old paths — scan the estate, not only the SDD (repeatable)", collectList, [])
|
|
48
|
+
.action(async (area, o) => exit(await runSnapshotFold({
|
|
49
|
+
areaRoot: resolve(area),
|
|
50
|
+
...(o.id !== undefined ? { id: o.id } : {}),
|
|
51
|
+
...(o.implementation !== undefined ? { implementation: o.implementation } : {}),
|
|
52
|
+
keys: o.key,
|
|
53
|
+
scanRoots: o.scan.map((r) => resolve(r)),
|
|
54
|
+
}, out)));
|
|
55
|
+
snapshot
|
|
56
|
+
.command("land <area>")
|
|
57
|
+
.description("Write a new baseline's manifest: every prior member re-observed, carried (with a reason) or removed (with a reason); refuses without a recorded credential scan")
|
|
58
|
+
.option("--id <date>", "the date the snapshot was OBSERVED (YYYY-MM-DD); no default, so the processing date cannot slip in")
|
|
59
|
+
.option("--prior <date>", "the baseline this supersedes (default: the latest earlier one)")
|
|
60
|
+
.option("--implementation <id>", "the durable system identity (default: the prior baseline's)")
|
|
61
|
+
.option("--member-date <key=date>", "a member observed on another day than the set (repeatable)", collectPairs, new Map())
|
|
62
|
+
.option("--carried <key=reason>", "a prior member not re-exported, and why (repeatable)", collectPairs, new Map())
|
|
63
|
+
.option("--removed <key=reason>", "a prior member that left the set, and why (repeatable)", collectPairs, new Map())
|
|
64
|
+
.option("--key <folder=key>", "canonical_repo_key for a new member's folder (repeatable)", collectPairs, new Map())
|
|
65
|
+
.option("--credential-scan <record>", "tool=…,version=…,date=YYYY-MM-DD,result=clean")
|
|
66
|
+
.option("--notes <text>", "coherence caveats for the manifest")
|
|
67
|
+
.action(async (area, o) => {
|
|
68
|
+
const scan = record(o.credentialScan);
|
|
69
|
+
exit(await runSnapshotLand({
|
|
70
|
+
areaRoot: resolve(area),
|
|
71
|
+
...(o.id !== undefined ? { id: o.id } : {}),
|
|
72
|
+
...(o.prior !== undefined ? { prior: o.prior } : {}),
|
|
73
|
+
...(o.implementation !== undefined ? { implementation: o.implementation } : {}),
|
|
74
|
+
memberDates: o.memberDate,
|
|
75
|
+
carried: o.carried,
|
|
76
|
+
removed: o.removed,
|
|
77
|
+
keys: o.key,
|
|
78
|
+
...(scan ? { credentialScan: scan } : {}),
|
|
79
|
+
...(o.notes !== undefined ? { notes: o.notes } : {}),
|
|
80
|
+
}, out));
|
|
81
|
+
});
|
|
82
|
+
snapshot
|
|
83
|
+
.command("verify <area>")
|
|
84
|
+
.description("Check a baseline's provenance: local folder/revision consistency here, canonical-repo confirmation supplied per member or waived on the record; cannot-assert FAILS")
|
|
85
|
+
.requiredOption("--id <date>", "the baseline to verify (normally T0)")
|
|
86
|
+
.option("--confirmed <key=how>", "how a member's revision was confirmed on its canonical repo (repeatable)", collectPairs, new Map())
|
|
87
|
+
.option("--waiver <record>", "who=…,why=…,date=YYYY-MM-DD — recorded in provenance.yaml and printed on every run")
|
|
88
|
+
.action(async (area, o) => {
|
|
89
|
+
const w = record(o.waiver);
|
|
90
|
+
exit(await runSnapshotVerify({ areaRoot: resolve(area), id: o.id, confirmed: o.confirmed, ...(w ? { waiver: w } : {}) }, out));
|
|
91
|
+
});
|
|
92
|
+
snapshot
|
|
93
|
+
.command("diff <area>")
|
|
94
|
+
.description("Classify members from the two manifests and diff changed members by evidence class into delta.yaml (status: proposed); refuses once any finding carries judgement")
|
|
95
|
+
.requiredOption("--to <date>", "the new baseline (T1)")
|
|
96
|
+
.option("--from <date>", "the prior baseline (default: T1's prior_snapshot)")
|
|
97
|
+
.action(async (area, o) => exit(await runSnapshotDiff({ areaRoot: resolve(area), to: o.to, ...(o.from !== undefined ? { from: o.from } : {}) }, out)));
|
|
98
|
+
snapshot
|
|
99
|
+
.command("targets <area>")
|
|
100
|
+
.description("Route each delta.yaml finding to the SDD artefacts that cite it, recording the grain (file | repo) of each route")
|
|
101
|
+
.requiredOption("--id <date>", "the baseline whose delta.yaml to route")
|
|
102
|
+
.option("--sdd-path <path>", "the SDD repo root (defaults to cwd)")
|
|
103
|
+
.action(async (area, o) => exit(await runSnapshotTargets({ areaRoot: resolve(area), id: o.id, sddPath: resolve(o.sddPath ?? process.cwd()) }, out)));
|
|
104
|
+
snapshot
|
|
105
|
+
.command("advance <area>")
|
|
106
|
+
.description("Advance each member's evidence pin to the baseline and append one history entry; refuses before delta.yaml, without impact, on legacy history or an unknown reverse_status")
|
|
107
|
+
.requiredOption("--to <date>", "the baseline to advance to (T1)")
|
|
108
|
+
.option("--sdd-path <path>", "the SDD repo root (defaults to cwd)")
|
|
109
|
+
.option("--member <key>", "limit to these members (repeatable)", collectList, [])
|
|
110
|
+
.option("--migrate-history", "convert legacy entries: histories to history[] first (phase: scan rows only)")
|
|
111
|
+
.action(async (area, o) => exit(await runSnapshotAdvance({
|
|
112
|
+
areaRoot: resolve(area),
|
|
113
|
+
to: o.to,
|
|
114
|
+
sddPath: resolve(o.sddPath ?? process.cwd()),
|
|
115
|
+
...(o.member.length > 0 ? { members: o.member } : {}),
|
|
116
|
+
migrateHistory: o.migrateHistory === true,
|
|
117
|
+
}, out)));
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=snapshot.js.map
|