@sdd-method/author-cli 0.16.1 → 0.16.2
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.
|
@@ -6,7 +6,11 @@
|
|
|
6
6
|
// - a member observed after the set date;
|
|
7
7
|
// - a prior member absent from the export and not declared carried (with a
|
|
8
8
|
// reason) or removed (with a reason) — never left to be inferred;
|
|
9
|
-
// - a member re-exported at the same revision whose bytes differ
|
|
9
|
+
// - a member re-exported at the same revision whose bytes differ, unless the only
|
|
10
|
+
// difference is files under a path declared --not-source (with a reason) that the
|
|
11
|
+
// prior export holds and the new one does not: analysis a tool wrote into the old
|
|
12
|
+
// export, not the repository. The prior baseline is never edited, the excused files
|
|
13
|
+
// and the reason are recorded, and a declaration that excuses nothing is refused;
|
|
10
14
|
// - no recorded credential scan, or one with findings but no disposition —
|
|
11
15
|
// a snapshot owner often cannot remediate upstream code, so findings land
|
|
12
16
|
// with the disposition on the record, never as a false `clean`;
|
|
@@ -68,6 +72,17 @@ export async function land(inputs, write) {
|
|
|
68
72
|
const s = splitFolder(m.exported_folder_name);
|
|
69
73
|
return s ? [[s.stem, m.canonical_repo_key]] : [];
|
|
70
74
|
}));
|
|
75
|
+
const notSource = new Map();
|
|
76
|
+
for (const [raw, reason] of inputs.notSource ?? []) {
|
|
77
|
+
const p = raw.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/+$/, "");
|
|
78
|
+
if (p === "" || p.startsWith("/") || p.split("/").includes(".."))
|
|
79
|
+
throw new SnapshotError(`--not-source '${raw}' must be a relative path inside a member folder`);
|
|
80
|
+
if (!reason.trim())
|
|
81
|
+
throw new SnapshotError(`--not-source ${raw} needs a reason: --not-source <path>=<why it is not source>`);
|
|
82
|
+
notSource.set(p, reason.trim());
|
|
83
|
+
}
|
|
84
|
+
const declaredPath = (f) => [...notSource.keys()].find((p) => f === p || f.startsWith(`${p}/`));
|
|
85
|
+
const excusedBy = new Map();
|
|
71
86
|
const known = inputs.sddPath ? await knownRepositoryIds(inputs.sddPath) : new Set();
|
|
72
87
|
const keyProblems = [];
|
|
73
88
|
const members = [];
|
|
@@ -105,18 +120,35 @@ export async function land(inputs, write) {
|
|
|
105
120
|
const before = await hashTree(memberBytesDir(areaRoot, prior, p));
|
|
106
121
|
const after = await hashTree(join(dir, folder));
|
|
107
122
|
const d = diffTrees(before, after);
|
|
108
|
-
|
|
123
|
+
// Only a file the prior export holds and the new one lacks, under a declared path, is excused.
|
|
124
|
+
const excused = d.removed.filter((f) => declaredPath(f) !== undefined);
|
|
125
|
+
const changed = d.added.length + d.modified.length + d.removed.length - excused.length;
|
|
109
126
|
if (changed > 0) {
|
|
110
|
-
throw new SnapshotError(`${key} was re-exported at the same revision ${revision} as ${prior.snapshot_id}, but ${changed} file(s) differ — one of the two exports is not what its name says`
|
|
127
|
+
throw new SnapshotError(`${key} was re-exported at the same revision ${revision} as ${prior.snapshot_id}, but ${changed} file(s) differ — one of the two exports is not what its name says` +
|
|
128
|
+
(notSource.size > 0 ? ` (--not-source excuses only files the prior export holds and this one does not, under a declared path)` : ""));
|
|
111
129
|
}
|
|
112
130
|
entry.reverified_at = md ?? id;
|
|
113
131
|
entry.reverification = `re-exported at the same revision as ${prior.snapshot_id}; ${after.size} file(s) byte-identical`;
|
|
132
|
+
if (excused.length > 0) {
|
|
133
|
+
const byPath = new Map();
|
|
134
|
+
for (const f of excused) {
|
|
135
|
+
const p = declaredPath(f);
|
|
136
|
+
byPath.set(p, (byPath.get(p) ?? 0) + 1);
|
|
137
|
+
excusedBy.set(p, (excusedBy.get(p) ?? 0) + 1);
|
|
138
|
+
}
|
|
139
|
+
entry.reverification += `; ${[...byPath].map(([p, n]) => `${n} file(s) under ${p}/ in ${prior.snapshot_id} only, not source: ${notSource.get(p)}`).join("; ")}`;
|
|
140
|
+
write(`note: ${key}: ${excused.length} file(s) under a --not-source path excused (prior export only)\n`);
|
|
141
|
+
}
|
|
114
142
|
}
|
|
115
143
|
members.push(entry);
|
|
116
144
|
}
|
|
117
145
|
if (keyProblems.length > 0) {
|
|
118
146
|
throw new SnapshotError(`${keyProblems.length} derived key(s) are not repository ids:\n - ${keyProblems.join("\n - ")}`);
|
|
119
147
|
}
|
|
148
|
+
const unused = [...notSource.keys()].filter((p) => !excusedBy.has(p));
|
|
149
|
+
if (unused.length > 0) {
|
|
150
|
+
throw new SnapshotError(`--not-source ${unused.join(", ")} excused nothing: no same-revision member's prior export holds files there that this one lacks; drop the declaration`);
|
|
151
|
+
}
|
|
120
152
|
const removed = [];
|
|
121
153
|
const unaccounted = [];
|
|
122
154
|
for (const p of prior?.members ?? []) {
|
package/dist/verbs/snapshot.js
CHANGED
|
@@ -72,6 +72,7 @@ export function registerSnapshot(program) {
|
|
|
72
72
|
.option("--role-hint <key=role>", "role_hint for a member; continuing members otherwise keep the prior baseline's (repeatable)", collectPairs, new Map())
|
|
73
73
|
.option("--credential-scan <record>", "tool=…,version=…,date=YYYY-MM-DD,result=clean — or result=<findings>,disposition=<what, and why it stands>")
|
|
74
74
|
.option("--notes <text>", "coherence caveats for the manifest")
|
|
75
|
+
.option("--not-source <path=reason>", "a path inside member folders that is not source (a folder a tool wrote into the prior export), and why: a same-revision re-export may lack it, recorded on the member; nothing else is excused (repeatable)", collectPairs, new Map())
|
|
75
76
|
.action(async (area, o) => {
|
|
76
77
|
const scan = record(o.credentialScan);
|
|
77
78
|
exit(await runSnapshotLand({
|
|
@@ -85,6 +86,7 @@ export function registerSnapshot(program) {
|
|
|
85
86
|
removed: o.removed,
|
|
86
87
|
keys: o.key,
|
|
87
88
|
roleHints: o.roleHint,
|
|
89
|
+
notSource: o.notSource,
|
|
88
90
|
...(scan ? { credentialScan: scan } : {}),
|
|
89
91
|
...(o.notes !== undefined ? { notes: o.notes } : {}),
|
|
90
92
|
}, out));
|
package/dist/verify-version.json
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sdd-method/author-cli",
|
|
3
|
-
"version": "0.16.
|
|
4
|
-
"sddCliVersion": "5.3.
|
|
3
|
+
"version": "0.16.2",
|
|
4
|
+
"sddCliVersion": "5.3.2",
|
|
5
5
|
"description": "Re-author one SDD's content — scaffold, plan, contracts, conformance, api-surface, snapshots, briefings, catalogue check — plus every read-only verify verb. Ships only the code those verbs reach; it cannot sync the baseline or rebuild the catalogues. Maintenance is in @sdd-method/maintain-cli, administration in @sdd-method/sdd-cli.",
|
|
6
6
|
"license": "UNLICENSED",
|
|
7
7
|
"type": "module",
|