@scientific-method/standard-checker 1.0.0 → 1.1.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 CHANGED
@@ -22,6 +22,14 @@ step of the check did not run, it ends with `spec check passed with skipped step
22
22
  `Skipped: Kaitai compilation of 2 definitions (--no-ksy).` A failing run lists the skipped steps
23
23
  after its problems.
24
24
 
25
+ Without `--base`, the checker compares the spec with where HEAD forked from `origin/$GITHUB_BASE_REF`,
26
+ or from `origin/main` when that variable is unset. Outside a git repository, in a shallow clone, or
27
+ when that branch was never fetched, the fork point does not resolve and the comparison does not
28
+ run. The result line then names it, such as `Skipped: comparison with the base branch (HEAD has no
29
+ merge-base with origin/main, fetch it with enough history or pass --base).` Without git on `PATH`
30
+ the skipped step names the missing git instead. With `--require-base`, either case is a problem and
31
+ the run fails.
32
+
25
33
  A problem that breaks a numbered rule of the standard ends with the rule's label in brackets, such
26
34
  as `[STATUS-14]`. The standard opens that rule with the heading `###### STATUS-14`, anchored at
27
35
  `#status-14` on the site and in the copies restorations vendor, so the rule can be read on its
@@ -34,7 +42,8 @@ problems under other sections carry no label yet.
34
42
  |---|---|---|
35
43
  | `--root <dir>` | The repository to check. | the current directory |
36
44
  | `--check` | Fail when an index or `PARITY.md` is stale, instead of rewriting it. | rewrite |
37
- | `--base <ref>` | Also fail when a spec ID, area or deviation that exists at `<ref>` is gone, or when a superseded format entry has no layout table although it had one at `<ref>`. | where HEAD forked from `origin/$GITHUB_BASE_REF` or `origin/main`, when that resolves |
45
+ | `--base <ref>` | Also fail when a spec ID, area or deviation that exists at `<ref>` is gone, or when a superseded format entry has no layout table although it had one at `<ref>`. | where HEAD forked from `origin/$GITHUB_BASE_REF` or `origin/main`; when that does not resolve, the comparison is named as skipped |
46
+ | `--require-base` | Fail when no `--base` is given and the fork point does not resolve, instead of passing with the comparison skipped. | pass with the comparison skipped |
38
47
  | `--no-ksy` | Skip compiling the Kaitai definitions in `spec/formats/`. The result line names the skipped compilation. | compile |
39
48
  | `--require-ksc` | Fail when `spec/formats/` holds Kaitai definitions and no compiler is found, instead of passing with the compilation skipped. | pass with the compilation skipped |
40
49
  | `--glossary <path>` | Also accept the terms of a draft glossary file, or of a directory of them. | none |
@@ -63,7 +72,8 @@ prints a warning naming its path, with its output.
63
72
  The toolkit's `actions/check-documentation` composite action runs this checker with `--check`,
64
73
  installs a pinned Kaitai compiler when the repository has `.ksy` files (and then passes
65
74
  `--require-ksc`), and exposes every option
66
- above as an input. Pin the action to the toolkit commit whose `packages/standard-checker` matches
75
+ above as an input. On a pull request with no `base` input, it fetches the base branch with enough
76
+ history for the fork point and passes `--require-base`. Pin the action to the toolkit commit whose `packages/standard-checker` matches
67
77
  the version installed here, so CI and local runs apply the same checks.
68
78
 
69
79
  [The documentation standard check guide](https://github.com/kibertoad/refurbished-dinosaurs-toolkit/blob/main/docs/documentation-standard-check.md)
@@ -3,6 +3,7 @@ import type { Deviation } from "./deviations.ts";
3
3
  /**
4
4
  * Reports a spec ID, area or deviation that exists at the base (--base, or where HEAD forked from
5
5
  * the base branch) and is gone now, and a superseded format entry whose Layout has no table although
6
- * it had one at the base. Does nothing when there is no base to compare with.
6
+ * it had one at the base. Without --base, a fork point that does not resolve is recorded as a
7
+ * skipped step, or with --require-base reported as a problem.
7
8
  */
8
9
  export declare function checkBase(ctx: Context, deviations: Map<string, Deviation>): void;
@@ -6,15 +6,16 @@ import { splitSections, tables } from "../markdown.js";
6
6
  /**
7
7
  * Reports a spec ID, area or deviation that exists at the base (--base, or where HEAD forked from
8
8
  * the base branch) and is gone now, and a superseded format entry whose Layout has no table although
9
- * it had one at the base. Does nothing when there is no base to compare with.
9
+ * it had one at the base. Without --base, a fork point that does not resolve is recorded as a
10
+ * skipped step, or with --require-base reported as a problem.
10
11
  */
11
12
  export function checkBase(ctx, deviations) {
12
- const { problem } = ctx;
13
+ const { problem, skip } = ctx;
13
14
  const { entries, areas } = ctx.spec;
14
- const { repoDir, baseArg } = ctx.config;
15
+ const { repoDir, baseArg, requireBase } = ctx.config;
15
16
  // Without --base, compare with the point this branch left the base branch (the pull request's
16
17
  // target in CI), not that branch's tip: an entry added on the base branch after this branch
17
- // forked is not one this branch deleted.
18
+ // forked is not one this branch deleted. A base that resolves but cannot be listed is a problem.
18
19
  const git = (...args) => execFileSync("git", ["-C", repoDir, ...args], { stdio: ["ignore", "pipe", "ignore"] }).toString();
19
20
  let base = baseArg;
20
21
  if (!base) {
@@ -22,68 +23,75 @@ export function checkBase(ctx, deviations) {
22
23
  try {
23
24
  base = git("merge-base", "HEAD", target).trim();
24
25
  }
25
- catch {
26
- base = null;
26
+ catch (error) {
27
+ // Outside a git repository, in a shallow clone, or without the base branch fetched, there is
28
+ // no fork point, and the deleted-ID checks cannot run. The run says so instead of reading as
29
+ // a full check.
30
+ const missing = error.code === "ENOENT"
31
+ ? `git was not found to look up HEAD's merge-base with ${target}, install it or pass --base`
32
+ : `HEAD has no merge-base with ${target}, fetch it with enough history or pass --base`;
33
+ if (requireBase)
34
+ problem(null, `${missing}. --require-base requires the comparison with the base branch`);
35
+ else
36
+ skip(`comparison with the base branch (${missing})`);
37
+ return;
27
38
  }
28
39
  }
29
- let listing = null;
30
- if (base)
40
+ let listing;
41
+ try {
42
+ listing = git("ls-tree", "-r", "--name-only", base, "--", "spec", "deviations");
43
+ }
44
+ catch {
45
+ problem(null, `cannot list spec/ at ${base}`);
46
+ return;
47
+ }
48
+ for (const p of listing.split("\n")) {
49
+ const m = /^spec\/(?:builds|sources|formats|rules|findings|experiments|bugs|screens)\/([A-Z]+-[A-Z0-9.-]+)\.md$/.exec(p);
50
+ if (m && !entries.has(m[1]))
51
+ problem(null, `${m[1]} exists at ${base} and has been deleted or renamed`, "IDENTIFIERS-6");
52
+ const d = /^deviations\/(DEV-[A-Z0-9]+-\d+)\.md$/.exec(p);
53
+ if (d && !deviations.has(d[1]))
54
+ problem(null, `${d[1]} exists at ${base} and has been deleted or renamed`);
55
+ }
56
+ // ./ makes the path relative to --root, which need not be the top of the repository. A file
57
+ // that does not exist at the base has nothing to compare, and each is read on its own so a
58
+ // missing README does not skip the deviation comparison.
59
+ const show = (path) => {
31
60
  try {
32
- listing = git("ls-tree", "-r", "--name-only", base, "--", "spec", "deviations");
61
+ return git("show", `${base}:./${path}`).replace(/\r\n/g, "\n");
33
62
  }
34
63
  catch {
35
- if (baseArg)
36
- problem(null, `cannot list spec/ at ${base}`);
64
+ return null;
37
65
  }
38
- if (listing) {
39
- for (const p of listing.split("\n")) {
40
- const m = /^spec\/(?:builds|sources|formats|rules|findings|experiments|bugs|screens)\/([A-Z]+-[A-Z0-9.-]+)\.md$/.exec(p);
41
- if (m && !entries.has(m[1]))
42
- problem(null, `${m[1]} exists at ${base} and has been deleted or renamed`, "IDENTIFIERS-6");
43
- const d = /^deviations\/(DEV-[A-Z0-9]+-\d+)\.md$/.exec(p);
44
- if (d && !deviations.has(d[1]))
45
- problem(null, `${d[1]} exists at ${base} and has been deleted or renamed`);
46
- }
47
- // ./ makes the path relative to --root, which need not be the top of the repository. A file
48
- // that does not exist at the base has nothing to compare, and each is read on its own so a
49
- // missing README does not skip the deviation comparison.
50
- const show = (path) => {
51
- try {
52
- return git("show", `${base}:./${path}`).replace(/\r\n/g, "\n");
53
- }
54
- catch {
55
- return null;
56
- }
57
- };
58
- const oldReadme = show("spec/README.md");
59
- // Reads the area table the way the current README is read, so areas with or without
60
- // backticks are both found.
61
- const oldAreaSection = oldReadme && splitSections(oldReadme).find((s) => s.title === "Areas");
62
- const oldAreaTable = oldAreaSection && tables(oldAreaSection.text)[0];
63
- for (const row of (oldAreaTable || undefined)?.rows ?? []) {
64
- const a = row[0].replaceAll("`", "");
65
- if (!areas.includes(a))
66
- problem(null, `area ${a} exists at ${base} and has been removed or renamed`, "IDENTIFIERS-5");
67
- }
68
- // The format checks let a superseded entry have no layout table, so one retired from unknown
69
- // keeps its None known. A superseded entry stays as it was when it was replaced (IDENTIFIERS-7),
70
- // so one that had a table at the base may not drop it on the way.
71
- const layoutTables = (body) => {
72
- const layout = splitSections(body).find((s) => s.title === "Layout");
73
- return layout ? tables(layout.text).length : 0;
74
- };
75
- for (const e of entries.values()) {
76
- if (e.kind !== "FMT" || e.meta.status !== "superseded" || layoutTables(e.body) > 0)
77
- continue;
78
- const old = show(relative(repoDir, e.file).replaceAll("\\", "/"));
79
- if (old && layoutTables(old) > 0)
80
- problem(e.file, `Layout has no table, but it had one at ${base}`, "IDENTIFIERS-7");
81
- }
82
- // A base from before the deviation log became a directory keeps its deviations in DEVIATIONS.md.
83
- const oldDev = show("DEVIATIONS.md");
84
- if (oldDev)
85
- for (const m of oldDev.matchAll(/^## (DEV-[A-Z0-9]+-\d+)$/gm))
86
- if (!deviations.has(m[1]))
87
- problem(null, `${m[1]} exists at ${base} and has been removed`);
66
+ };
67
+ const oldReadme = show("spec/README.md");
68
+ // Reads the area table the way the current README is read, so areas with or without
69
+ // backticks are both found.
70
+ const oldAreaSection = oldReadme && splitSections(oldReadme).find((s) => s.title === "Areas");
71
+ const oldAreaTable = oldAreaSection && tables(oldAreaSection.text)[0];
72
+ for (const row of (oldAreaTable || undefined)?.rows ?? []) {
73
+ const a = row[0].replaceAll("`", "");
74
+ if (!areas.includes(a))
75
+ problem(null, `area ${a} exists at ${base} and has been removed or renamed`, "IDENTIFIERS-5");
76
+ }
77
+ // The format checks let a superseded entry have no layout table, so one retired from unknown
78
+ // keeps its None known. A superseded entry stays as it was when it was replaced (IDENTIFIERS-7),
79
+ // so one that had a table at the base may not drop it on the way.
80
+ const layoutTables = (body) => {
81
+ const layout = splitSections(body).find((s) => s.title === "Layout");
82
+ return layout ? tables(layout.text).length : 0;
83
+ };
84
+ for (const e of entries.values()) {
85
+ if (e.kind !== "FMT" || e.meta.status !== "superseded" || layoutTables(e.body) > 0)
86
+ continue;
87
+ const old = show(relative(repoDir, e.file).replaceAll("\\", "/"));
88
+ if (old && layoutTables(old) > 0)
89
+ problem(e.file, `Layout has no table, but it had one at ${base}`, "IDENTIFIERS-7");
88
90
  }
91
+ // A base from before the deviation log became a directory keeps its deviations in DEVIATIONS.md.
92
+ const oldDev = show("DEVIATIONS.md");
93
+ if (oldDev)
94
+ for (const m of oldDev.matchAll(/^## (DEV-[A-Z0-9]+-\d+)$/gm))
95
+ if (!deviations.has(m[1]))
96
+ problem(null, `${m[1]} exists at ${base} and has been removed`);
89
97
  }
package/dist/options.d.ts CHANGED
@@ -12,6 +12,8 @@ export interface Config {
12
12
  requireKsc: boolean;
13
13
  /** --base, or null when it was not given. */
14
14
  baseArg: string | null;
15
+ /** --require-base: fail when no --base is given and the base branch's fork point does not resolve. */
16
+ requireBase: boolean;
15
17
  /** Every --glossary path, in order. */
16
18
  glossaryDrafts: string[];
17
19
  /** The --code directories, relative to repoDir. */
package/dist/options.js CHANGED
@@ -10,7 +10,7 @@ export const dirList = (value, fallback) => value === undefined
10
10
  .split(",")
11
11
  .map((x) => x.trim())
12
12
  .filter(Boolean);
13
- const FLAGS = ["--check", "--no-ksy", "--require-ksc"];
13
+ const FLAGS = ["--check", "--no-ksy", "--require-ksc", "--require-base"];
14
14
  const VALUED = [
15
15
  "--root",
16
16
  "--base",
@@ -82,6 +82,7 @@ export function parseOptions(argv) {
82
82
  skipKsy,
83
83
  requireKsc,
84
84
  baseArg,
85
+ requireBase: options["require-base"] === true,
85
86
  glossaryDrafts: options.glossary,
86
87
  codeRoots,
87
88
  referenceRoots,
@@ -9,7 +9,10 @@
9
9
  // --root <dir> the repository to check (default: the current directory)
10
10
  // --check fail when an index or PARITY.md is stale instead of rewriting it
11
11
  // --base <ref> also fail when an ID or area that exists at <ref> is gone (default: where
12
- // HEAD forked from origin/$GITHUB_BASE_REF or origin/main, when it resolves)
12
+ // HEAD forked from origin/$GITHUB_BASE_REF or origin/main; when that does not
13
+ // resolve, the result line names the comparison as skipped)
14
+ // --require-base fail when no --base is given and the fork point does not resolve, instead
15
+ // of passing with the comparison skipped
13
16
  // --no-ksy skip compiling the Kaitai definitions; the result line names the skip
14
17
  // --require-ksc fail when spec/formats/ holds Kaitai definitions and no compiler is found,
15
18
  // instead of passing with the compilation skipped
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scientific-method/standard-checker",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Checks a restoration's spec/, parity/ and deviations/ against version 1 of the dinorefurb documentation standard.",
5
5
  "type": "module",
6
6
  "license": "MIT",