@trofimyukdev/repo-truth 0.0.0-stage → 0.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.
Files changed (120) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +273 -2
  3. package/dist/action.d.ts +52 -0
  4. package/dist/action.js +182 -0
  5. package/dist/action.js.map +1 -0
  6. package/dist/checks/action-pins.d.ts +52 -0
  7. package/dist/checks/action-pins.js +196 -0
  8. package/dist/checks/action-pins.js.map +1 -0
  9. package/dist/checks/autosquash.d.ts +23 -0
  10. package/dist/checks/autosquash.js +85 -0
  11. package/dist/checks/autosquash.js.map +1 -0
  12. package/dist/checks/bidi-controls.d.ts +58 -0
  13. package/dist/checks/bidi-controls.js +172 -0
  14. package/dist/checks/bidi-controls.js.map +1 -0
  15. package/dist/checks/binary-files.d.ts +53 -0
  16. package/dist/checks/binary-files.js +157 -0
  17. package/dist/checks/binary-files.js.map +1 -0
  18. package/dist/checks/config-changes.d.ts +57 -0
  19. package/dist/checks/config-changes.js +175 -0
  20. package/dist/checks/config-changes.js.map +1 -0
  21. package/dist/checks/credentials.d.ts +35 -0
  22. package/dist/checks/credentials.js +138 -0
  23. package/dist/checks/credentials.js.map +1 -0
  24. package/dist/checks/install-scripts.d.ts +39 -0
  25. package/dist/checks/install-scripts.js +173 -0
  26. package/dist/checks/install-scripts.js.map +1 -0
  27. package/dist/checks/lint-rules.d.ts +49 -0
  28. package/dist/checks/lint-rules.js +184 -0
  29. package/dist/checks/lint-rules.js.map +1 -0
  30. package/dist/checks/path-policy.d.ts +40 -0
  31. package/dist/checks/path-policy.js +195 -0
  32. package/dist/checks/path-policy.js.map +1 -0
  33. package/dist/checks/raised-timeouts.d.ts +66 -0
  34. package/dist/checks/raised-timeouts.js +248 -0
  35. package/dist/checks/raised-timeouts.js.map +1 -0
  36. package/dist/checks/script-changes.d.ts +51 -0
  37. package/dist/checks/script-changes.js +199 -0
  38. package/dist/checks/script-changes.js.map +1 -0
  39. package/dist/checks/secret-files.d.ts +58 -0
  40. package/dist/checks/secret-files.js +165 -0
  41. package/dist/checks/secret-files.js.map +1 -0
  42. package/dist/checks/strictness.d.ts +59 -0
  43. package/dist/checks/strictness.js +260 -0
  44. package/dist/checks/strictness.js.map +1 -0
  45. package/dist/checks/submodules.d.ts +51 -0
  46. package/dist/checks/submodules.js +172 -0
  47. package/dist/checks/submodules.js.map +1 -0
  48. package/dist/checks/suppressions.d.ts +53 -0
  49. package/dist/checks/suppressions.js +203 -0
  50. package/dist/checks/suppressions.js.map +1 -0
  51. package/dist/checks/workflow-permissions.d.ts +56 -0
  52. package/dist/checks/workflow-permissions.js +237 -0
  53. package/dist/checks/workflow-permissions.js.map +1 -0
  54. package/dist/checks/workflow-weakening.d.ts +38 -0
  55. package/dist/checks/workflow-weakening.js +172 -0
  56. package/dist/checks/workflow-weakening.js.map +1 -0
  57. package/dist/cli.d.ts +31 -0
  58. package/dist/cli.js +300 -0
  59. package/dist/cli.js.map +1 -0
  60. package/dist/commit-range.d.ts +28 -0
  61. package/dist/commit-range.js +152 -0
  62. package/dist/commit-range.js.map +1 -0
  63. package/dist/config.d.ts +35 -0
  64. package/dist/config.js +235 -0
  65. package/dist/config.js.map +1 -0
  66. package/dist/conflict-markers.d.ts +40 -0
  67. package/dist/conflict-markers.js +234 -0
  68. package/dist/conflict-markers.js.map +1 -0
  69. package/dist/dependency-sources.d.ts +32 -0
  70. package/dist/dependency-sources.js +260 -0
  71. package/dist/dependency-sources.js.map +1 -0
  72. package/dist/figure-claims.d.ts +35 -0
  73. package/dist/figure-claims.js +139 -0
  74. package/dist/figure-claims.js.map +1 -0
  75. package/dist/figure-text.d.ts +22 -0
  76. package/dist/figure-text.js +119 -0
  77. package/dist/figure-text.js.map +1 -0
  78. package/dist/index.d.ts +46 -0
  79. package/dist/index.js +38 -0
  80. package/dist/index.js.map +1 -0
  81. package/dist/large-blobs.d.ts +35 -0
  82. package/dist/large-blobs.js +163 -0
  83. package/dist/large-blobs.js.map +1 -0
  84. package/dist/lockfile-drift.d.ts +36 -0
  85. package/dist/lockfile-drift.js +206 -0
  86. package/dist/lockfile-drift.js.map +1 -0
  87. package/dist/lowered-thresholds.d.ts +70 -0
  88. package/dist/lowered-thresholds.js +281 -0
  89. package/dist/lowered-thresholds.js.map +1 -0
  90. package/dist/measurement-rule.d.ts +39 -0
  91. package/dist/measurement-rule.js +213 -0
  92. package/dist/measurement-rule.js.map +1 -0
  93. package/dist/portable-paths.d.ts +37 -0
  94. package/dist/portable-paths.js +215 -0
  95. package/dist/portable-paths.js.map +1 -0
  96. package/dist/registry.d.ts +43 -0
  97. package/dist/registry.js +144 -0
  98. package/dist/registry.js.map +1 -0
  99. package/dist/replay-plan.d.ts +36 -0
  100. package/dist/replay-plan.js +196 -0
  101. package/dist/replay-plan.js.map +1 -0
  102. package/dist/replayed-figures.d.ts +36 -0
  103. package/dist/replayed-figures.js +159 -0
  104. package/dist/replayed-figures.js.map +1 -0
  105. package/dist/snapshot-refreshes.d.ts +36 -0
  106. package/dist/snapshot-refreshes.js +202 -0
  107. package/dist/snapshot-refreshes.js.map +1 -0
  108. package/dist/stray-files.d.ts +52 -0
  109. package/dist/stray-files.js +234 -0
  110. package/dist/stray-files.js.map +1 -0
  111. package/dist/symlinks.d.ts +28 -0
  112. package/dist/symlinks.js +193 -0
  113. package/dist/symlinks.js.map +1 -0
  114. package/dist/trailers.d.ts +36 -0
  115. package/dist/trailers.js +110 -0
  116. package/dist/trailers.js.map +1 -0
  117. package/dist/weakened-tests.d.ts +94 -0
  118. package/dist/weakened-tests.js +458 -0
  119. package/dist/weakened-tests.js.map +1 -0
  120. package/package.json +31 -4
@@ -0,0 +1,260 @@
1
+ /**
2
+ * Compiler strictness lowered.
3
+ *
4
+ * Reads every tsconfig the range adds, deletes, changes or renames, whole
5
+ * through git, at each end at which the path is a tsconfig, as the compiler
6
+ * reads one (JSON with comments and trailing commas). Reports each strict
7
+ * option that held its strict value at the merge base and does not at the
8
+ * candidate (`strictness-lowered`) and each change of `extends`
9
+ * (`extends-changed`). A file the range adds is compared with nothing; one it
10
+ * deletes is compared with a file that sets nothing. `extends` is not followed.
11
+ * A file that cannot be read at an end at which it is read fails the record.
12
+ *
13
+ * A finding is sanctioned by a `Sanctioned-Weakening: <id> <reason>` line in a
14
+ * commit message of the range or by the caller's `sanctions`. Its id is
15
+ * `<category>:<file>:<option>`; `<category>:<file>` sanctions every finding of
16
+ * that category in that file.
17
+ */
18
+ import { execFile } from "node:child_process";
19
+ import { SANCTION_KEY } from "../weakened-tests.js";
20
+ export const name = "RT-34";
21
+ /** The options the check judges. A consumer appends to it; it is read at every run. */
22
+ export const STRICT_OPTIONS = [
23
+ { name: "strict", strict: true, fromStrict: false },
24
+ ...[
25
+ "noImplicitAny",
26
+ "strictNullChecks",
27
+ "strictFunctionTypes",
28
+ "strictBindCallApply",
29
+ "strictPropertyInitialization",
30
+ "strictBuiltinIteratorReturn",
31
+ "noImplicitThis",
32
+ "alwaysStrict",
33
+ "useUnknownInCatchVariables",
34
+ ].map((option) => ({ name: option, strict: true, fromStrict: true })),
35
+ ...[
36
+ "noUncheckedIndexedAccess",
37
+ "exactOptionalPropertyTypes",
38
+ "noImplicitOverride",
39
+ "noImplicitReturns",
40
+ "noFallthroughCasesInSwitch",
41
+ "noPropertyAccessFromIndexSignature",
42
+ ].map((option) => ({ name: option, strict: true, fromStrict: false })),
43
+ { name: "skipLibCheck", strict: false, fromStrict: false },
44
+ ];
45
+ function runGit(cwd, args) {
46
+ return new Promise((resolve, reject) => {
47
+ execFile("git", args, { cwd, maxBuffer: 1024 * 1024 * 1024, encoding: "utf8" }, (error, stdout, stderr) => {
48
+ if (error)
49
+ reject(new Error(stderr.trim() || error.message));
50
+ else
51
+ resolve(stdout);
52
+ });
53
+ });
54
+ }
55
+ /** Escape everything outside printable ASCII, so evidence cannot replay a control sequence. */
56
+ function escapeText(text) {
57
+ let escaped = "";
58
+ for (const char of text) {
59
+ const codePoint = char.codePointAt(0) ?? 0;
60
+ escaped += codePoint >= 0x20 && codePoint <= 0x7e ? char : `\\u{${codePoint.toString(16).padStart(4, "0")}}`;
61
+ }
62
+ return escaped;
63
+ }
64
+ const quoted = (text) => `"${escapeText(text).replace(/"/g, '\\"')}"`;
65
+ async function resolveCommit(cwd, revision) {
66
+ const fail = (cause) => new Error(`revision "${escapeText(String(revision))}" does not name a commit${cause}`);
67
+ // A revision that begins with a dash would be read as an option.
68
+ if (typeof revision !== "string" || revision.length === 0 || revision.startsWith("-"))
69
+ throw fail("");
70
+ let hash = "";
71
+ try {
72
+ hash = (await runGit(cwd, ["rev-parse", "--verify", "--quiet", `${revision}^{commit}`])).trim();
73
+ }
74
+ catch (error) {
75
+ throw fail(`: ${error.message}`);
76
+ }
77
+ if (hash.length === 0)
78
+ throw fail("");
79
+ return hash;
80
+ }
81
+ /** JSON with comments and trailing commas, as the compiler reads a tsconfig. */
82
+ export function parseTsconfig(text) {
83
+ const out = [];
84
+ let lastComma = -1; // index in `out` of a comma with only blanks and comments after it
85
+ for (let i = text.startsWith("") ? 1 : 0; i < text.length; i++) {
86
+ const char = text.charAt(i);
87
+ const next = text.charAt(i + 1);
88
+ if (char === '"') {
89
+ let end = i + 1;
90
+ while (end < text.length && text.charAt(end) !== '"')
91
+ end += text.charAt(end) === "\\" ? 2 : 1;
92
+ out.push(text.slice(i, end + 1));
93
+ i = end;
94
+ lastComma = -1;
95
+ }
96
+ else if (char === "/" && next === "/") {
97
+ while (i < text.length && text.charAt(i) !== "\n")
98
+ i++;
99
+ out.push("\n");
100
+ }
101
+ else if (char === "/" && next === "*") {
102
+ const end = text.indexOf("*/", i + 2);
103
+ if (end < 0)
104
+ throw new Error("unterminated comment");
105
+ i = end + 1;
106
+ out.push(" ");
107
+ }
108
+ else if (char === " " || char === "\t" || char === "\n" || char === "\r") {
109
+ out.push(char);
110
+ }
111
+ else {
112
+ if ((char === "}" || char === "]") && lastComma >= 0)
113
+ out[lastComma] = " ";
114
+ lastComma = char === "," ? out.length : -1;
115
+ out.push(char);
116
+ }
117
+ }
118
+ return JSON.parse(out.join(""));
119
+ }
120
+ const isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
121
+ const EMPTY = { options: {}, extendsText: "" };
122
+ async function readConfig(cwd, mode, sha) {
123
+ if (!mode.startsWith("100"))
124
+ return { problem: "is not a regular file" };
125
+ let parsed;
126
+ try {
127
+ parsed = parseTsconfig(await runGit(cwd, ["cat-file", "blob", sha]));
128
+ }
129
+ catch (error) {
130
+ return { problem: `is not JSON: ${error.message}` };
131
+ }
132
+ if (!isObject(parsed))
133
+ return { problem: "is not a JSON object" };
134
+ const options = Object.hasOwn(parsed, "compilerOptions") ? parsed["compilerOptions"] : {};
135
+ if (!isObject(options))
136
+ return { problem: "has a compilerOptions that is not an object" };
137
+ const ext = Object.hasOwn(parsed, "extends") ? parsed["extends"] : undefined;
138
+ return { options, extendsText: ext === undefined ? "" : typeof ext === "string" ? ext : JSON.stringify(ext) };
139
+ }
140
+ /** An option's value in a file: its own boolean, else `strict` for a member, else false. */
141
+ function valueOf(options, option) {
142
+ const own = Object.hasOwn(options, option.name) ? options[option.name] : undefined;
143
+ if (typeof own === "boolean")
144
+ return own;
145
+ const strict = Object.hasOwn(options, "strict") ? options["strict"] : undefined;
146
+ return option.fromStrict && typeof strict === "boolean" ? strict : false;
147
+ }
148
+ function isTsconfig(path) {
149
+ if (path === null)
150
+ return false;
151
+ const file = path.slice(path.lastIndexOf("/") + 1);
152
+ return file === "tsconfig.json" || file === "jsconfig.json" || (file.startsWith("tsconfig.") && file.endsWith(".json"));
153
+ }
154
+ async function compare(cwd, from, to) {
155
+ const out = await runGit(cwd, ["diff", "--raw", "-z", "--no-abbrev", "--find-renames", "--no-relative", from, to, "--"]);
156
+ const fields = out.split("\0");
157
+ const drafts = [];
158
+ const problems = [];
159
+ for (let i = 0; i < fields.length; i++) {
160
+ const head = fields[i] ?? "";
161
+ if (!head.startsWith(":"))
162
+ continue;
163
+ const [oldMode = "", newMode = "", oldSha = "", newSha = "", status = ""] = head.slice(1).split(" ");
164
+ const first = fields[++i] ?? "";
165
+ const renamed = status.startsWith("R") || status.startsWith("C");
166
+ const second = renamed ? (fields[++i] ?? "") : first;
167
+ const oldPath = status.startsWith("A") ? null : first;
168
+ const newPath = status.startsWith("D") ? null : second;
169
+ const oldHere = isTsconfig(oldPath);
170
+ const newHere = isTsconfig(newPath);
171
+ if (!oldHere && !newHere)
172
+ continue;
173
+ const file = newHere ? newPath : (oldPath ?? "");
174
+ const before = oldHere ? await readConfig(cwd, oldMode, oldSha) : undefined;
175
+ const after = newHere ? await readConfig(cwd, newMode, newSha) : undefined;
176
+ if (before !== undefined && "problem" in before)
177
+ problems.push(`${escapeText(oldPath ?? file)}: at the merge base the file ${escapeText(before.problem)}`);
178
+ if (after !== undefined && "problem" in after)
179
+ problems.push(`${escapeText(file)}: at the candidate the file ${escapeText(after.problem)}`);
180
+ // Only at the candidate there is nothing to compare with; an unreadable end is already reported.
181
+ if (before === undefined || "problem" in before || (after !== undefined && "problem" in after))
182
+ continue;
183
+ const now = after ?? EMPTY;
184
+ if (before.extendsText !== now.extendsText) {
185
+ drafts.push({ category: "extends-changed", file, option: "extends", before: before.extendsText, after: now.extendsText });
186
+ }
187
+ for (const option of STRICT_OPTIONS) {
188
+ const was = valueOf(before.options, option);
189
+ const is = valueOf(now.options, option);
190
+ if (was === option.strict && is !== option.strict) {
191
+ drafts.push({ category: "strictness-lowered", file, option: option.name, before: was, after: is });
192
+ }
193
+ }
194
+ }
195
+ return { drafts, problems };
196
+ }
197
+ /** The text after the sanction key on every such line of the range's commit messages. */
198
+ async function readSanctionLines(cwd, from, to) {
199
+ const out = await runGit(cwd, ["log", "--format=%B%x00", `${from}..${to}`, "--"]);
200
+ const prefix = `${SANCTION_KEY}:`;
201
+ return out
202
+ .split(/[\n\0]/)
203
+ .map((line) => line.trim())
204
+ .filter((line) => line.startsWith(prefix))
205
+ .map((line) => line.slice(prefix.length).trim());
206
+ }
207
+ function sanctionFor(category, file, id, lines, given) {
208
+ for (const key of [id, `${category}:${file}`]) {
209
+ const reason = Object.hasOwn(given, key) ? given[key] : undefined;
210
+ if (typeof reason === "string" && reason.trim().length > 0)
211
+ return reason.trim();
212
+ for (const line of lines) {
213
+ const rest = line.slice(key.length);
214
+ if (line.startsWith(key) && /^\s+\S/.test(rest))
215
+ return rest.trim();
216
+ }
217
+ }
218
+ return null;
219
+ }
220
+ function describeFinding(finding) {
221
+ const text = finding.category === "extends-changed"
222
+ ? `extends changed ${quoted(String(finding.before))} -> ${quoted(String(finding.after))}`
223
+ : `lowered ${escapeText(finding.option)}: ${String(finding.before)} -> ${String(finding.after)}`;
224
+ const tail = finding.sanction === null ? "" : ` - sanctioned: ${escapeText(finding.sanction)}`;
225
+ return `${escapeText(finding.file)}: ${text}${tail}`;
226
+ }
227
+ /**
228
+ * Report each strict option `base..candidate` lowers and each change of
229
+ * `extends`. Fails closed: a revision that names no commit, or a git
230
+ * invocation that fails, is a `fail` record naming the cause.
231
+ */
232
+ export async function check(options) {
233
+ try {
234
+ const { cwd, base, candidate } = options;
235
+ const baseCommit = await resolveCommit(cwd, base);
236
+ const candidateCommit = await resolveCommit(cwd, candidate);
237
+ let ancestor;
238
+ try {
239
+ ancestor = (await runGit(cwd, ["merge-base", baseCommit, candidateCommit])).trim();
240
+ }
241
+ catch (error) {
242
+ throw new Error(`no merge base of "${escapeText(base)}" and "${escapeText(candidate)}": ${error.message}`);
243
+ }
244
+ const { drafts, problems } = await compare(cwd, ancestor, candidateCommit);
245
+ const lines = drafts.length === 0 ? [] : await readSanctionLines(cwd, baseCommit, candidateCommit);
246
+ const findings = drafts.map((draft) => {
247
+ const id = `${draft.category}:${draft.file}:${draft.option}`;
248
+ return { id, ...draft, sanction: sanctionFor(draft.category, draft.file, id, lines, options.sanctions ?? {}) };
249
+ });
250
+ if (findings.length === 0 && problems.length === 0) {
251
+ return { name, status: "pass", evidence: ["no strict option lowered"], findings };
252
+ }
253
+ const open = problems.length > 0 || findings.some((finding) => finding.sanction === null);
254
+ return { name, status: open ? "fail" : "pass", evidence: [...problems, ...findings.map(describeFinding)], findings };
255
+ }
256
+ catch (error) {
257
+ return { name, status: "fail", evidence: [escapeText(error instanceof Error ? error.message : String(error))], findings: [] };
258
+ }
259
+ }
260
+ //# sourceMappingURL=strictness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"strictness.js","sourceRoot":"","sources":["../../src/checks/strictness.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Submodules added or moved.
3
+ *
4
+ * A submodule is a commit id written into the tree (a gitlink, mode 160000);
5
+ * the diff of a range that adds one or moves it shows a single line, and no
6
+ * check that reads added lines or blobs reads the code that commit holds. A
7
+ * `url` in .gitmodules points the next update at another repository. This
8
+ * check reports each gitlink the range adds (`submodule-added`) or moves to
9
+ * another commit (`submodule-moved`), and each `url` value .gitmodules gains,
10
+ * in any section (`submodule-url`, of the file .gitmodules). A gitlink the
11
+ * range removes or leaves at its commit is no finding.
12
+ *
13
+ * The range is read through git, from the merge base of `base` and `candidate`
14
+ * to `candidate`, by listing the trees of the two commits: never from the
15
+ * working tree, never through a diff that the repository's `diff.ignoreSubmodules`
16
+ * or a `submodule.<name>.ignore` could quiet. No submodule is fetched,
17
+ * initialised or entered. The check declares no options.
18
+ *
19
+ * A finding is sanctioned by a `Sanctioned-Weakening: <id> <reason>` line in a
20
+ * commit message of the range or by the caller's `sanctions`. Its id is
21
+ * `<category>:<path>`; for a URL it is `submodule-url:.gitmodules`.
22
+ */
23
+ import type { CheckRecord } from "../index.js";
24
+ export declare const name = "RT-87";
25
+ export type SubmoduleCategory = "submodule-added" | "submodule-moved" | "submodule-url";
26
+ export interface SubmodulesOptions {
27
+ readonly cwd: string;
28
+ readonly base: string;
29
+ readonly candidate: string;
30
+ /** Ids mapped to reasons. */
31
+ readonly sanctions?: Readonly<Record<string, string>> | undefined;
32
+ }
33
+ export interface SubmoduleFinding {
34
+ readonly id: string;
35
+ readonly category: SubmoduleCategory;
36
+ readonly path: string;
37
+ /** The text after `<path>: <category>` on the evidence line, before any sanction. */
38
+ readonly detail: string;
39
+ /** The sanction's reason, or `null` while unsanctioned. */
40
+ readonly sanction: string | null;
41
+ }
42
+ export interface SubmodulesRecord extends CheckRecord {
43
+ readonly findings: readonly SubmoduleFinding[];
44
+ }
45
+ /**
46
+ * Report each gitlink `base..candidate` adds or moves and each submodule URL it
47
+ * gains. Fails while any finding is unsanctioned, and fails closed: a revision
48
+ * that names no commit, or any git invocation that fails, is a `fail` record
49
+ * naming the cause - never a `pass`, never a throw.
50
+ */
51
+ export declare function check(options: SubmodulesOptions): Promise<SubmodulesRecord>;
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Submodules added or moved.
3
+ *
4
+ * A submodule is a commit id written into the tree (a gitlink, mode 160000);
5
+ * the diff of a range that adds one or moves it shows a single line, and no
6
+ * check that reads added lines or blobs reads the code that commit holds. A
7
+ * `url` in .gitmodules points the next update at another repository. This
8
+ * check reports each gitlink the range adds (`submodule-added`) or moves to
9
+ * another commit (`submodule-moved`), and each `url` value .gitmodules gains,
10
+ * in any section (`submodule-url`, of the file .gitmodules). A gitlink the
11
+ * range removes or leaves at its commit is no finding.
12
+ *
13
+ * The range is read through git, from the merge base of `base` and `candidate`
14
+ * to `candidate`, by listing the trees of the two commits: never from the
15
+ * working tree, never through a diff that the repository's `diff.ignoreSubmodules`
16
+ * or a `submodule.<name>.ignore` could quiet. No submodule is fetched,
17
+ * initialised or entered. The check declares no options.
18
+ *
19
+ * A finding is sanctioned by a `Sanctioned-Weakening: <id> <reason>` line in a
20
+ * commit message of the range or by the caller's `sanctions`. Its id is
21
+ * `<category>:<path>`; for a URL it is `submodule-url:.gitmodules`.
22
+ */
23
+ import { execFile } from "node:child_process";
24
+ import { SANCTION_KEY } from "../weakened-tests.js";
25
+ export const name = "RT-87";
26
+ const GITMODULES = ".gitmodules";
27
+ function runGit(cwd, args) {
28
+ return new Promise((resolve, reject) => {
29
+ execFile("git", args, { cwd, maxBuffer: 1024 * 1024 * 1024, encoding: "utf8" }, (error, stdout, stderr) => {
30
+ if (error)
31
+ reject(new Error(stderr.trim() || error.message));
32
+ else
33
+ resolve(stdout);
34
+ });
35
+ });
36
+ }
37
+ /** Escape everything outside printable ASCII, so evidence cannot replay a control sequence. */
38
+ function escapeText(text) {
39
+ let escaped = "";
40
+ for (const char of text) {
41
+ const codePoint = char.codePointAt(0) ?? 0;
42
+ escaped += codePoint >= 0x20 && codePoint <= 0x7e ? char : `\\u{${codePoint.toString(16).padStart(4, "0")}}`;
43
+ }
44
+ return escaped;
45
+ }
46
+ async function resolveCommit(cwd, revision) {
47
+ const fail = (cause) => new Error(`revision "${escapeText(String(revision))}" does not name a commit${cause}`);
48
+ // A revision that begins with a dash would be read as an option.
49
+ if (typeof revision !== "string" || revision.length === 0 || revision.startsWith("-"))
50
+ throw fail("");
51
+ let hash = "";
52
+ try {
53
+ hash = (await runGit(cwd, ["rev-parse", "--verify", "--quiet", `${revision}^{commit}`])).trim();
54
+ }
55
+ catch (error) {
56
+ throw fail(`: ${error.message}`);
57
+ }
58
+ if (hash.length === 0)
59
+ throw fail("");
60
+ return hash;
61
+ }
62
+ /** Every gitlink of a commit's tree, from its path to its commit id. */
63
+ async function readGitlinks(cwd, commit) {
64
+ const out = await runGit(cwd, ["ls-tree", "-r", "-z", "--full-tree", commit]);
65
+ const links = new Map();
66
+ for (const entry of out.split("\0")) {
67
+ const tab = entry.indexOf("\t");
68
+ if (tab < 0)
69
+ continue;
70
+ const [mode = "", , sha = ""] = entry.slice(0, tab).split(" ");
71
+ if (mode === "160000")
72
+ links.set(entry.slice(tab + 1), sha);
73
+ }
74
+ return links;
75
+ }
76
+ /** Every `url` value .gitmodules holds in a commit, in any section; none when the file is absent. */
77
+ async function readUrls(cwd, commit) {
78
+ const listed = await runGit(cwd, ["ls-tree", "-z", "--full-tree", commit, "--", GITMODULES]);
79
+ const mode = listed.split(" ")[0] ?? "";
80
+ if (!mode.startsWith("100"))
81
+ return [];
82
+ const out = await runGit(cwd, ["config", "--blob", `${commit}:${GITMODULES}`, "-z", "--list"]);
83
+ const urls = [];
84
+ for (const entry of out.split("\0")) {
85
+ const newline = entry.indexOf("\n");
86
+ if (newline < 0)
87
+ continue;
88
+ const key = entry.slice(0, newline);
89
+ // The variable is what follows the last dot of the key; the section, whatever it is, does not matter.
90
+ if (key.endsWith(".url"))
91
+ urls.push(entry.slice(newline + 1));
92
+ }
93
+ return urls;
94
+ }
95
+ /** The text after the sanction key on every such line of the range's commit messages. */
96
+ async function readSanctionLines(cwd, from, to) {
97
+ const out = await runGit(cwd, ["log", "--format=%B%x00", `${from}..${to}`, "--"]);
98
+ const prefix = `${SANCTION_KEY}:`;
99
+ return out
100
+ .split(/[\n\0]/)
101
+ .map((line) => line.trim())
102
+ .filter((line) => line.startsWith(prefix))
103
+ .map((line) => line.slice(prefix.length).trim());
104
+ }
105
+ function sanctionFor(id, lines, given) {
106
+ const reason = Object.hasOwn(given, id) ? given[id] : undefined;
107
+ if (typeof reason === "string" && reason.trim().length > 0)
108
+ return reason.trim();
109
+ for (const line of lines) {
110
+ const rest = line.slice(id.length);
111
+ if (line.startsWith(id) && /^\s+\S/.test(rest))
112
+ return rest.trim();
113
+ }
114
+ return null;
115
+ }
116
+ function describeFinding(finding) {
117
+ const tail = finding.sanction === null ? "" : ` - sanctioned: ${escapeText(finding.sanction)}`;
118
+ return `${escapeText(finding.path)}: ${finding.category} ${finding.detail}${tail}`;
119
+ }
120
+ /**
121
+ * Report each gitlink `base..candidate` adds or moves and each submodule URL it
122
+ * gains. Fails while any finding is unsanctioned, and fails closed: a revision
123
+ * that names no commit, or any git invocation that fails, is a `fail` record
124
+ * naming the cause - never a `pass`, never a throw.
125
+ */
126
+ export async function check(options) {
127
+ try {
128
+ const { cwd, base, candidate } = options;
129
+ const baseCommit = await resolveCommit(cwd, base);
130
+ const candidateCommit = await resolveCommit(cwd, candidate);
131
+ let ancestor;
132
+ try {
133
+ ancestor = (await runGit(cwd, ["merge-base", baseCommit, candidateCommit])).trim();
134
+ }
135
+ catch (error) {
136
+ throw new Error(`no merge base of "${escapeText(base)}" and "${escapeText(candidate)}": ${error.message}`);
137
+ }
138
+ const [before, after, urlsBefore, urlsAfter] = await Promise.all([
139
+ readGitlinks(cwd, ancestor),
140
+ readGitlinks(cwd, candidateCommit),
141
+ readUrls(cwd, ancestor),
142
+ readUrls(cwd, candidateCommit),
143
+ ]);
144
+ const drafts = [];
145
+ for (const [path, sha] of [...after].sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))) {
146
+ const old = before.get(path);
147
+ if (old === undefined)
148
+ drafts.push({ category: "submodule-added", path, detail: sha });
149
+ else if (old !== sha)
150
+ drafts.push({ category: "submodule-moved", path, detail: `${old} -> ${sha}` });
151
+ }
152
+ const known = new Set(urlsBefore);
153
+ for (const url of [...new Set(urlsAfter)]) {
154
+ if (!known.has(url))
155
+ drafts.push({ category: "submodule-url", path: GITMODULES, detail: escapeText(url) });
156
+ }
157
+ if (drafts.length === 0) {
158
+ return { name, status: "pass", evidence: ["no submodule added or moved, and no submodule URL changed"], findings: [] };
159
+ }
160
+ const lines = await readSanctionLines(cwd, ancestor, candidateCommit);
161
+ const findings = drafts.map((draft) => {
162
+ const id = `${draft.category}:${draft.path}`;
163
+ return { id, ...draft, sanction: sanctionFor(id, lines, options.sanctions ?? {}) };
164
+ });
165
+ const open = findings.some((finding) => finding.sanction === null);
166
+ return { name, status: open ? "fail" : "pass", evidence: findings.map(describeFinding), findings };
167
+ }
168
+ catch (error) {
169
+ return { name, status: "fail", evidence: [escapeText(error instanceof Error ? error.message : String(error))], findings: [] };
170
+ }
171
+ }
172
+ //# sourceMappingURL=submodules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"submodules.js","sourceRoot":"","sources":["../../src/checks/submodules.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Suppression directives.
3
+ *
4
+ * A line that no longer type-checks, a rule the change breaks, a branch no test
5
+ * reaches: each passes once a comment tells the gate not to look. This check
6
+ * reads the lines `base..candidate` removes and adds in source files, pairs each
7
+ * directive the range removes with one it adds in the same file, and reports an
8
+ * added directive left without a pair (`suppression-added`) for a human to
9
+ * sanction. Only the diff is read, through git.
10
+ *
11
+ * Which files and directives count lives in the exported
12
+ * `SUPPRESSION_DIRECTIVES`, read each time the check runs: a consumer appends
13
+ * its own entry. A directive only counts right after a comment opener.
14
+ */
15
+ import type { CheckRecord } from "../index.js";
16
+ export declare const name = "RT-31";
17
+ /** One directive. */
18
+ export interface SuppressionDirective {
19
+ /** The directive a finding names. */
20
+ readonly name: string;
21
+ /** Tested against a changed path. */
22
+ readonly files: RegExp;
23
+ /** Tested against each line the range removes or adds in such a file. */
24
+ readonly pattern: RegExp;
25
+ }
26
+ export declare const SUPPRESSION_DIRECTIVES: SuppressionDirective[];
27
+ /** One directive the range added without a pair; `id` is `suppression-added:<file>:<line>`, the line in the candidate. */
28
+ export interface SuppressionFinding {
29
+ readonly id: string;
30
+ readonly category: "suppression-added";
31
+ readonly file: string;
32
+ readonly line: number;
33
+ readonly name: string;
34
+ /** The sanction's reason, or `null` while unsanctioned. */
35
+ readonly sanction: string | null;
36
+ }
37
+ export interface SuppressionsRecord extends CheckRecord {
38
+ readonly findings: readonly SuppressionFinding[];
39
+ }
40
+ /** `sanctions` keys are finding ids or `suppression-added:<file>`; values are the reasons. */
41
+ export interface SuppressionsOptions {
42
+ readonly cwd: string;
43
+ readonly base: string;
44
+ readonly candidate: string;
45
+ readonly sanctions?: Readonly<Record<string, string>> | undefined;
46
+ }
47
+ /**
48
+ * Report the suppression directives `base..candidate` adds without a pair.
49
+ * Fails while any finding is unsanctioned, and fails closed: a revision that
50
+ * names no commit, or any git invocation that fails, is a `fail` record naming
51
+ * the cause - never a `pass`, and never a thrown error.
52
+ */
53
+ export declare function check(options: SuppressionsOptions): Promise<SuppressionsRecord>;