octwin-cli 0.8.4 → 0.8.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +39 -0
- package/dist/index.js +518 -489
- package/dist/lib/outcome.js +79 -0
- package/package.json +1 -1
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Outcome` — what one `octwin validate` check ACTUALLY did, as a value.
|
|
3
|
+
*
|
|
4
|
+
* ## Why this exists
|
|
5
|
+
*
|
|
6
|
+
* `cmdValidate` used to hand-write its own `✓` per check, inside the `if` that had the
|
|
7
|
+
* check's data, with a `skipped[]` array and a `skipReasons` Map maintained alongside. It
|
|
8
|
+
* was correct because someone was careful, and the next check added was one `if` away from
|
|
9
|
+
* lying again. It had lied before, twice, in the same shape:
|
|
10
|
+
*
|
|
11
|
+
* - the KB-dependent block printed ONE `✓` above six checks whose own `✓`s lived inside
|
|
12
|
+
* their `if`s, so a run with no pulled KB read as "one check, passed". An entire backlog
|
|
13
|
+
* batch reached production that way;
|
|
14
|
+
* - `yamlDocs()` parsed inside `try { … } catch { return [] }`, so a file that failed to
|
|
15
|
+
* parse was silently DROPPED and every later check skipped it — `validate` printed
|
|
16
|
+
* all-green over a pack the platform's importer then refused with the same message, from
|
|
17
|
+
* the same `yaml` version this CLI already had.
|
|
18
|
+
*
|
|
19
|
+
* **An unparseable file is not "no findings", it is "no idea", and those must never print the
|
|
20
|
+
* same.** So a check no longer prints; it RETURNS one of three things, and a single printer
|
|
21
|
+
* renders all of them. A check that returns nothing cannot reach the `passed` branch, which
|
|
22
|
+
* makes the false-`✓` unrepresentable rather than merely discouraged.
|
|
23
|
+
*
|
|
24
|
+
* ## Why `not-run` carries the lookup rather than a message
|
|
25
|
+
*
|
|
26
|
+
* `describeKbLookup` phrases it at print time, so the wording lives in the module that owns
|
|
27
|
+
* the three KB states and cannot drift into a doubled "SKIPPED — SKIPPED —". It also lets the
|
|
28
|
+
* printer GROUP by reason: when the KB is absent every check skips for the identical reason,
|
|
29
|
+
* and six copies of one sentence is how a reader learns to scroll past the ⚠ block — the same
|
|
30
|
+
* failure as not printing it at all.
|
|
31
|
+
*/
|
|
32
|
+
import { describeKbLookup } from './kb-path.js';
|
|
33
|
+
/**
|
|
34
|
+
* Run every check in order and print exactly what each returned.
|
|
35
|
+
*
|
|
36
|
+
* Returns the labels that could not run, so the caller can decide what a partial pass means
|
|
37
|
+
* (`--require-kb` fails on any; an interactive run says which, last, where it is read).
|
|
38
|
+
*
|
|
39
|
+
* The first `findings` outcome terminates via `die`, matching the previous behaviour: an
|
|
40
|
+
* author fixes one class at a time, and printing six screens of unrelated findings buries the
|
|
41
|
+
* first. Order therefore matters and is the caller's to choose.
|
|
42
|
+
*/
|
|
43
|
+
export function reportChecks(checks, io) {
|
|
44
|
+
const notRun = [];
|
|
45
|
+
for (const check of checks) {
|
|
46
|
+
const outcome = check.run();
|
|
47
|
+
switch (outcome.kind) {
|
|
48
|
+
case 'passed':
|
|
49
|
+
io.log(`✓ ${outcome.line}`);
|
|
50
|
+
break;
|
|
51
|
+
case 'findings': {
|
|
52
|
+
const n = outcome.lines.length;
|
|
53
|
+
io.err(`✗ ${n} ${outcome.noun}${n === 1 ? '' : 's'}:`);
|
|
54
|
+
for (const line of outcome.lines)
|
|
55
|
+
io.err(` ✗ ${line}`);
|
|
56
|
+
io.die(outcome.hint);
|
|
57
|
+
}
|
|
58
|
+
// eslint-disable-next-line no-fallthrough -- `die` returns never; the case above cannot exit.
|
|
59
|
+
case 'not-run':
|
|
60
|
+
notRun.push({ label: check.label, lookup: outcome.lookup });
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// One ⚠ per distinct reason, naming every check it cost. Grouped by the lookup's IDENTITY.
|
|
65
|
+
const byReason = new Map();
|
|
66
|
+
for (const { label, lookup } of notRun) {
|
|
67
|
+
const key = lookup.state === 'ok'
|
|
68
|
+
? 'ok'
|
|
69
|
+
: `${lookup.state}|${'dir' in lookup ? lookup.dir : ''}|${'reason' in lookup ? lookup.reason : ''}`;
|
|
70
|
+
const bucket = byReason.get(key) ?? { lookup, labels: [] };
|
|
71
|
+
bucket.labels.push(label);
|
|
72
|
+
byReason.set(key, bucket);
|
|
73
|
+
}
|
|
74
|
+
for (const { lookup, labels } of byReason.values()) {
|
|
75
|
+
const what = labels.length === 1 ? labels[0] : `${labels.length} checks (${labels.join(', ')})`;
|
|
76
|
+
io.log(`⚠ ${describeKbLookup(lookup, what)}`);
|
|
77
|
+
}
|
|
78
|
+
return notRun.map(n => n.label);
|
|
79
|
+
}
|
package/package.json
CHANGED