@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.
- package/LICENSE +21 -0
- package/README.md +273 -2
- package/dist/action.d.ts +52 -0
- package/dist/action.js +182 -0
- package/dist/action.js.map +1 -0
- package/dist/checks/action-pins.d.ts +52 -0
- package/dist/checks/action-pins.js +196 -0
- package/dist/checks/action-pins.js.map +1 -0
- package/dist/checks/autosquash.d.ts +23 -0
- package/dist/checks/autosquash.js +85 -0
- package/dist/checks/autosquash.js.map +1 -0
- package/dist/checks/bidi-controls.d.ts +58 -0
- package/dist/checks/bidi-controls.js +172 -0
- package/dist/checks/bidi-controls.js.map +1 -0
- package/dist/checks/binary-files.d.ts +53 -0
- package/dist/checks/binary-files.js +157 -0
- package/dist/checks/binary-files.js.map +1 -0
- package/dist/checks/config-changes.d.ts +57 -0
- package/dist/checks/config-changes.js +175 -0
- package/dist/checks/config-changes.js.map +1 -0
- package/dist/checks/credentials.d.ts +35 -0
- package/dist/checks/credentials.js +138 -0
- package/dist/checks/credentials.js.map +1 -0
- package/dist/checks/install-scripts.d.ts +39 -0
- package/dist/checks/install-scripts.js +173 -0
- package/dist/checks/install-scripts.js.map +1 -0
- package/dist/checks/lint-rules.d.ts +49 -0
- package/dist/checks/lint-rules.js +184 -0
- package/dist/checks/lint-rules.js.map +1 -0
- package/dist/checks/path-policy.d.ts +40 -0
- package/dist/checks/path-policy.js +195 -0
- package/dist/checks/path-policy.js.map +1 -0
- package/dist/checks/raised-timeouts.d.ts +66 -0
- package/dist/checks/raised-timeouts.js +248 -0
- package/dist/checks/raised-timeouts.js.map +1 -0
- package/dist/checks/script-changes.d.ts +51 -0
- package/dist/checks/script-changes.js +199 -0
- package/dist/checks/script-changes.js.map +1 -0
- package/dist/checks/secret-files.d.ts +58 -0
- package/dist/checks/secret-files.js +165 -0
- package/dist/checks/secret-files.js.map +1 -0
- package/dist/checks/strictness.d.ts +59 -0
- package/dist/checks/strictness.js +260 -0
- package/dist/checks/strictness.js.map +1 -0
- package/dist/checks/submodules.d.ts +51 -0
- package/dist/checks/submodules.js +172 -0
- package/dist/checks/submodules.js.map +1 -0
- package/dist/checks/suppressions.d.ts +53 -0
- package/dist/checks/suppressions.js +203 -0
- package/dist/checks/suppressions.js.map +1 -0
- package/dist/checks/workflow-permissions.d.ts +56 -0
- package/dist/checks/workflow-permissions.js +237 -0
- package/dist/checks/workflow-permissions.js.map +1 -0
- package/dist/checks/workflow-weakening.d.ts +38 -0
- package/dist/checks/workflow-weakening.js +172 -0
- package/dist/checks/workflow-weakening.js.map +1 -0
- package/dist/cli.d.ts +31 -0
- package/dist/cli.js +300 -0
- package/dist/cli.js.map +1 -0
- package/dist/commit-range.d.ts +28 -0
- package/dist/commit-range.js +152 -0
- package/dist/commit-range.js.map +1 -0
- package/dist/config.d.ts +35 -0
- package/dist/config.js +235 -0
- package/dist/config.js.map +1 -0
- package/dist/conflict-markers.d.ts +40 -0
- package/dist/conflict-markers.js +234 -0
- package/dist/conflict-markers.js.map +1 -0
- package/dist/dependency-sources.d.ts +32 -0
- package/dist/dependency-sources.js +260 -0
- package/dist/dependency-sources.js.map +1 -0
- package/dist/figure-claims.d.ts +35 -0
- package/dist/figure-claims.js +139 -0
- package/dist/figure-claims.js.map +1 -0
- package/dist/figure-text.d.ts +22 -0
- package/dist/figure-text.js +119 -0
- package/dist/figure-text.js.map +1 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.js +38 -0
- package/dist/index.js.map +1 -0
- package/dist/large-blobs.d.ts +35 -0
- package/dist/large-blobs.js +163 -0
- package/dist/large-blobs.js.map +1 -0
- package/dist/lockfile-drift.d.ts +36 -0
- package/dist/lockfile-drift.js +206 -0
- package/dist/lockfile-drift.js.map +1 -0
- package/dist/lowered-thresholds.d.ts +70 -0
- package/dist/lowered-thresholds.js +281 -0
- package/dist/lowered-thresholds.js.map +1 -0
- package/dist/measurement-rule.d.ts +39 -0
- package/dist/measurement-rule.js +213 -0
- package/dist/measurement-rule.js.map +1 -0
- package/dist/portable-paths.d.ts +37 -0
- package/dist/portable-paths.js +215 -0
- package/dist/portable-paths.js.map +1 -0
- package/dist/registry.d.ts +43 -0
- package/dist/registry.js +144 -0
- package/dist/registry.js.map +1 -0
- package/dist/replay-plan.d.ts +36 -0
- package/dist/replay-plan.js +196 -0
- package/dist/replay-plan.js.map +1 -0
- package/dist/replayed-figures.d.ts +36 -0
- package/dist/replayed-figures.js +159 -0
- package/dist/replayed-figures.js.map +1 -0
- package/dist/snapshot-refreshes.d.ts +36 -0
- package/dist/snapshot-refreshes.js +202 -0
- package/dist/snapshot-refreshes.js.map +1 -0
- package/dist/stray-files.d.ts +52 -0
- package/dist/stray-files.js +234 -0
- package/dist/stray-files.js.map +1 -0
- package/dist/symlinks.d.ts +28 -0
- package/dist/symlinks.js +193 -0
- package/dist/symlinks.js.map +1 -0
- package/dist/trailers.d.ts +36 -0
- package/dist/trailers.js +110 -0
- package/dist/trailers.js.map +1 -0
- package/dist/weakened-tests.d.ts +94 -0
- package/dist/weakened-tests.js +458 -0
- package/dist/weakened-tests.js.map +1 -0
- package/package.json +31 -4
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Figure claims.
|
|
3
|
+
*
|
|
4
|
+
* A measured figure is written in one form: a git command in a code span, the
|
|
5
|
+
* word `printed`, and the output in a code span. This finds the claims of that
|
|
6
|
+
* form a range adds, in its Markdown pages and in its commit messages. It
|
|
7
|
+
* reads only through git, with an argument vector and never a shell, and never
|
|
8
|
+
* from the working tree: a page is read as a blob of the candidate, and only
|
|
9
|
+
* the lines the range adds to it - against the file of the merge base it was
|
|
10
|
+
* renamed from, when git pairs it with one - can hold a claim. The text of a
|
|
11
|
+
* page or a body is read by `claimsInText`, which is RT-28's.
|
|
12
|
+
*/
|
|
13
|
+
import { execFile } from "node:child_process";
|
|
14
|
+
import { claimsInText } from "./figure-text.js";
|
|
15
|
+
const ZERO_ID = /^0+$/;
|
|
16
|
+
function runGit(cwd, args) {
|
|
17
|
+
return new Promise((resolve, reject) => {
|
|
18
|
+
execFile("git", args, { cwd, maxBuffer: 1024 * 1024 * 1024, encoding: "utf8" }, (error, stdout, stderr) => {
|
|
19
|
+
if (error) {
|
|
20
|
+
reject(new Error(stderr.trim() || error.message));
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
resolve(stdout);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
async function resolveCommit(cwd, revision) {
|
|
28
|
+
// A revision that begins with a dash would be read as an option.
|
|
29
|
+
if (typeof revision !== "string" || revision.length === 0 || revision.startsWith("-")) {
|
|
30
|
+
throw new Error(`revision ${JSON.stringify(String(revision))} does not name a commit`);
|
|
31
|
+
}
|
|
32
|
+
let hash = "";
|
|
33
|
+
try {
|
|
34
|
+
hash = (await runGit(cwd, ["rev-parse", "--verify", "--quiet", `${revision}^{commit}`])).trim();
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
throw new Error(`revision ${JSON.stringify(revision)} does not name a commit: ${error.message}`);
|
|
38
|
+
}
|
|
39
|
+
if (hash.length === 0)
|
|
40
|
+
throw new Error(`revision ${JSON.stringify(revision)} does not name a commit`);
|
|
41
|
+
return hash;
|
|
42
|
+
}
|
|
43
|
+
/** The Markdown pages of the candidate that differ from the merge base, paired by rename detection. */
|
|
44
|
+
async function changedPages(cwd, mergeBase, candidate) {
|
|
45
|
+
const out = await runGit(cwd, [
|
|
46
|
+
"diff-tree", "-r", "-M", "-z", "--no-ext-diff", "--no-textconv", mergeBase, candidate,
|
|
47
|
+
]);
|
|
48
|
+
const fields = out.split("\0");
|
|
49
|
+
const pages = [];
|
|
50
|
+
let i = 0;
|
|
51
|
+
while (i < fields.length) {
|
|
52
|
+
const meta = fields[i++];
|
|
53
|
+
if (!meta.startsWith(":"))
|
|
54
|
+
continue;
|
|
55
|
+
const [, newMode = "", oldId = "", newId = "", status = ""] = meta.split(/\s+/);
|
|
56
|
+
const first = fields[i++] ?? "";
|
|
57
|
+
const path = status.startsWith("R") || status.startsWith("C") ? (fields[i++] ?? "") : first;
|
|
58
|
+
if (status.startsWith("D") || !path.endsWith(".md"))
|
|
59
|
+
continue;
|
|
60
|
+
if (!newMode.startsWith("100") || ZERO_ID.test(newId))
|
|
61
|
+
continue;
|
|
62
|
+
pages.push({ path, oldId: ZERO_ID.test(oldId) ? null : oldId, newId });
|
|
63
|
+
}
|
|
64
|
+
return pages;
|
|
65
|
+
}
|
|
66
|
+
/** The lines, counted from 1, that the new blob holds and the old one does not. */
|
|
67
|
+
async function addedLines(cwd, page, text) {
|
|
68
|
+
const added = new Set();
|
|
69
|
+
if (page.oldId === null) {
|
|
70
|
+
const total = text.split("\n").length;
|
|
71
|
+
for (let line = 1; line <= total; line += 1)
|
|
72
|
+
added.add(line);
|
|
73
|
+
return added;
|
|
74
|
+
}
|
|
75
|
+
if (page.oldId === page.newId)
|
|
76
|
+
return added;
|
|
77
|
+
const diff = await runGit(cwd, [
|
|
78
|
+
"diff", "--no-color", "--no-ext-diff", "--no-textconv", "--no-renames", "-U0", page.oldId, page.newId,
|
|
79
|
+
]);
|
|
80
|
+
for (const header of diff.matchAll(/^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@/gm)) {
|
|
81
|
+
const start = Number(header[1]);
|
|
82
|
+
const count = header[2] === undefined ? 1 : Number(header[2]);
|
|
83
|
+
for (let line = start; line < start + count; line += 1)
|
|
84
|
+
added.add(line);
|
|
85
|
+
}
|
|
86
|
+
return added;
|
|
87
|
+
}
|
|
88
|
+
async function pageClaims(cwd, mergeBase, candidate) {
|
|
89
|
+
const claims = [];
|
|
90
|
+
for (const page of await changedPages(cwd, mergeBase, candidate)) {
|
|
91
|
+
const text = await runGit(cwd, ["cat-file", "blob", page.newId]);
|
|
92
|
+
const added = await addedLines(cwd, page, text);
|
|
93
|
+
for (const claim of claimsInText(text, "paragraphs")) {
|
|
94
|
+
if (added.has(claim.line)) {
|
|
95
|
+
claims.push({ where: `${page.path}:${claim.line}`, command: claim.command, claimed: claim.claimed });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return claims;
|
|
100
|
+
}
|
|
101
|
+
async function bodyClaims(cwd, base, candidate) {
|
|
102
|
+
const out = await runGit(cwd, ["log", "-z", "--format=%H%n%B", `${base}..${candidate}`]);
|
|
103
|
+
const claims = [];
|
|
104
|
+
for (const record of out.split("\0")) {
|
|
105
|
+
const newline = record.indexOf("\n");
|
|
106
|
+
if (newline < 0)
|
|
107
|
+
continue;
|
|
108
|
+
const id = record.slice(0, newline).replace(/^\n+/, "");
|
|
109
|
+
const body = record.slice(newline + 1);
|
|
110
|
+
for (const claim of claimsInText(body, "whole")) {
|
|
111
|
+
claims.push({ where: id, command: claim.command, claimed: claim.claimed });
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return claims;
|
|
115
|
+
}
|
|
116
|
+
/** Every figure claim the range adds, or a failure naming why they could not be read. */
|
|
117
|
+
export async function readFigureClaims(options) {
|
|
118
|
+
try {
|
|
119
|
+
const { cwd } = options;
|
|
120
|
+
const base = await resolveCommit(cwd, options.base);
|
|
121
|
+
const candidate = await resolveCommit(cwd, options.candidate);
|
|
122
|
+
let mergeBase = "";
|
|
123
|
+
try {
|
|
124
|
+
mergeBase = (await runGit(cwd, ["merge-base", base, candidate])).trim();
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
throw new Error(`no merge base of ${base} and ${candidate}: ${error.message}`);
|
|
128
|
+
}
|
|
129
|
+
if (mergeBase.length === 0)
|
|
130
|
+
throw new Error(`no merge base of ${base} and ${candidate}`);
|
|
131
|
+
const pages = await pageClaims(cwd, mergeBase, candidate);
|
|
132
|
+
const bodies = await bodyClaims(cwd, base, candidate);
|
|
133
|
+
return { claims: [...pages, ...bodies] };
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
return { failure: error instanceof Error ? error.message : String(error) };
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=figure-claims.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"figure-claims.js","sourceRoot":"","sources":["../src/figure-claims.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claim text: the figure claims one page or one commit body holds.
|
|
3
|
+
*
|
|
4
|
+
* A measured figure is written in one form: a git command in a code span, the
|
|
5
|
+
* word `printed`, and the output in a code span. This reads that form in a
|
|
6
|
+
* text. It is pure: no git, no file, no process, no import. Code spans are
|
|
7
|
+
* read as CommonMark reads them: a backslash before ASCII punctuation outside
|
|
8
|
+
* a span escapes it, and inside a span a backslash is an ordinary character.
|
|
9
|
+
*/
|
|
10
|
+
/** One figure claim found in a text. */
|
|
11
|
+
export interface FigureTextClaim {
|
|
12
|
+
/** The line, counted from 1, on which the claimed output's code span opens. */
|
|
13
|
+
line: number;
|
|
14
|
+
/** The text of the command's code span. */
|
|
15
|
+
command: string;
|
|
16
|
+
/** The text of the claimed output's code span. */
|
|
17
|
+
claimed: string;
|
|
18
|
+
}
|
|
19
|
+
/** A page is read by its paragraphs, a commit body as one block. */
|
|
20
|
+
export type FigureBlocks = "paragraphs" | "whole";
|
|
21
|
+
/** The figure claims in `text`, in the order their claimed outputs appear. */
|
|
22
|
+
export declare function claimsInText(text: string, blocks: FigureBlocks): FigureTextClaim[];
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claim text: the figure claims one page or one commit body holds.
|
|
3
|
+
*
|
|
4
|
+
* A measured figure is written in one form: a git command in a code span, the
|
|
5
|
+
* word `printed`, and the output in a code span. This reads that form in a
|
|
6
|
+
* text. It is pure: no git, no file, no process, no import. Code spans are
|
|
7
|
+
* read as CommonMark reads them: a backslash before ASCII punctuation outside
|
|
8
|
+
* a span escapes it, and inside a span a backslash is an ordinary character.
|
|
9
|
+
*/
|
|
10
|
+
const WORD = "printed";
|
|
11
|
+
const ESCAPABLE = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
|
|
12
|
+
const WORD_CHARACTER = /^[\p{L}\p{N}_]$/u;
|
|
13
|
+
/** Split into lines at a line feed; a carriage return just before one is dropped. */
|
|
14
|
+
function linesOf(text) {
|
|
15
|
+
const lines = text.split("\n");
|
|
16
|
+
return lines.map((line, index) => (index < lines.length - 1 && line.endsWith("\r") ? line.slice(0, -1) : line));
|
|
17
|
+
}
|
|
18
|
+
function blocksOf(text, blocks) {
|
|
19
|
+
const lines = linesOf(text);
|
|
20
|
+
if (blocks !== "paragraphs")
|
|
21
|
+
return [{ text: lines.join("\n"), firstLine: 1 }];
|
|
22
|
+
const found = [];
|
|
23
|
+
let current = [];
|
|
24
|
+
let first = 1;
|
|
25
|
+
lines.forEach((line, index) => {
|
|
26
|
+
if (/^[ \t]*$/.test(line)) {
|
|
27
|
+
if (current.length > 0)
|
|
28
|
+
found.push({ text: current.join("\n"), firstLine: first });
|
|
29
|
+
current = [];
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
if (current.length === 0)
|
|
33
|
+
first = index + 1;
|
|
34
|
+
current.push(line);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
if (current.length > 0)
|
|
38
|
+
found.push({ text: current.join("\n"), firstLine: first });
|
|
39
|
+
return found;
|
|
40
|
+
}
|
|
41
|
+
/** The length of the backtick string that starts at `from`. */
|
|
42
|
+
function runLength(text, from) {
|
|
43
|
+
let end = from;
|
|
44
|
+
while (text[end] === "`")
|
|
45
|
+
end += 1;
|
|
46
|
+
return end - from;
|
|
47
|
+
}
|
|
48
|
+
/** The content of a code span, read as CommonMark reads it. */
|
|
49
|
+
function spanContent(raw) {
|
|
50
|
+
const content = raw.replace(/\n/g, " ");
|
|
51
|
+
if (content.length >= 2 && content.startsWith(" ") && content.endsWith(" ") && /[^ ]/.test(content)) {
|
|
52
|
+
return content.slice(1, -1);
|
|
53
|
+
}
|
|
54
|
+
return content;
|
|
55
|
+
}
|
|
56
|
+
function claimsInBlock(block) {
|
|
57
|
+
const { text } = block;
|
|
58
|
+
const claims = [];
|
|
59
|
+
let command;
|
|
60
|
+
let commandAtWord;
|
|
61
|
+
let claimStart = -1;
|
|
62
|
+
let i = 0;
|
|
63
|
+
while (i < text.length) {
|
|
64
|
+
const char = text[i];
|
|
65
|
+
if (char === "\\") {
|
|
66
|
+
i += ESCAPABLE.includes(text[i + 1] ?? "") ? 2 : 1;
|
|
67
|
+
}
|
|
68
|
+
else if (char === "`") {
|
|
69
|
+
const length = runLength(text, i);
|
|
70
|
+
let search = i + length;
|
|
71
|
+
let close = -1;
|
|
72
|
+
while (search < text.length) {
|
|
73
|
+
if (text[search] !== "`") {
|
|
74
|
+
search += 1;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const other = runLength(text, search);
|
|
78
|
+
if (other === length) {
|
|
79
|
+
close = search;
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
search += other;
|
|
83
|
+
}
|
|
84
|
+
if (close < 0) {
|
|
85
|
+
i += length;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
const content = spanContent(text.slice(i + length, close));
|
|
89
|
+
if (i === claimStart && commandAtWord !== undefined) {
|
|
90
|
+
const line = block.firstLine + (text.slice(0, i).match(/\n/g)?.length ?? 0);
|
|
91
|
+
claims.push({ line, command: commandAtWord, claimed: content });
|
|
92
|
+
}
|
|
93
|
+
claimStart = -1;
|
|
94
|
+
if (content.startsWith("git "))
|
|
95
|
+
command = content;
|
|
96
|
+
i = close + length;
|
|
97
|
+
}
|
|
98
|
+
else if (text.startsWith(WORD, i) && !WORD_CHARACTER.test(text[i - 1] ?? "")) {
|
|
99
|
+
const wordEnd = i + WORD.length;
|
|
100
|
+
let end = wordEnd;
|
|
101
|
+
while (text[end] === " ")
|
|
102
|
+
end += 1;
|
|
103
|
+
commandAtWord = command;
|
|
104
|
+
claimStart = end > wordEnd && command !== undefined ? end : -1;
|
|
105
|
+
i = wordEnd;
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
i += 1;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return claims;
|
|
112
|
+
}
|
|
113
|
+
/** The figure claims in `text`, in the order their claimed outputs appear. */
|
|
114
|
+
export function claimsInText(text, blocks) {
|
|
115
|
+
if (typeof text !== "string")
|
|
116
|
+
return [];
|
|
117
|
+
return blocksOf(text, blocks).flatMap(claimsInBlock);
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=figure-text.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"figure-text.js","sourceRoot":"","sources":["../src/figure-text.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* repo-truth - the shape every check in this package answers in.
|
|
3
|
+
*
|
|
4
|
+
* Nothing here decides anything yet. What this module fixes is the RESULT
|
|
5
|
+
* TYPE, because that is the half a caller depends on and the half that is
|
|
6
|
+
* expensive to change once six checks have their own version of it. A check
|
|
7
|
+
* returns a record; a gate reads records; neither side reads a process exit
|
|
8
|
+
* code or a line of prose.
|
|
9
|
+
*
|
|
10
|
+
* The checks themselves arrive one task at a time - see `factory/tasks/`.
|
|
11
|
+
*/
|
|
12
|
+
/** What a check concluded. */
|
|
13
|
+
export type CheckStatus = "pass" | "fail" | "skip";
|
|
14
|
+
/**
|
|
15
|
+
* One machine-readable answer.
|
|
16
|
+
*
|
|
17
|
+
* `evidence` is a list rather than a string because a failure that names one
|
|
18
|
+
* offending commit and a failure that names forty are the same kind of answer,
|
|
19
|
+
* and a caller that has to split a paragraph to tell them apart is a caller
|
|
20
|
+
* that will get it wrong. A passing check MAY carry evidence - what it looked
|
|
21
|
+
* at is worth printing - and an empty list is not a defect.
|
|
22
|
+
*/
|
|
23
|
+
export interface CheckRecord {
|
|
24
|
+
/** Stable identifier, matching the check's task id in `factory/tasks/`. */
|
|
25
|
+
readonly name: string;
|
|
26
|
+
readonly status: CheckStatus;
|
|
27
|
+
/** Human-readable lines naming what was found, or what was looked at. */
|
|
28
|
+
readonly evidence: readonly string[];
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The one check that exists, and it checks the package's own premise: that a
|
|
32
|
+
* check is a record and not an exit code.
|
|
33
|
+
*
|
|
34
|
+
* It is a placeholder in what it MEASURES and not in what it RETURNS. It is
|
|
35
|
+
* kept once the real checks land, because a package whose whole contract is a
|
|
36
|
+
* record type should have one test that fails if the record type moves.
|
|
37
|
+
*/
|
|
38
|
+
export declare function selfCheck(): CheckRecord;
|
|
39
|
+
/**
|
|
40
|
+
* Whether a set of records lets a candidate through.
|
|
41
|
+
*
|
|
42
|
+
* A `skip` does not block: a check that could not run has not found anything,
|
|
43
|
+
* and treating "did not run" as "found a defect" is how a gate gets switched
|
|
44
|
+
* off wholesale. A check that must not be skippable says so by failing instead.
|
|
45
|
+
*/
|
|
46
|
+
export declare function allPassed(records: readonly CheckRecord[]): boolean;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* repo-truth - the shape every check in this package answers in.
|
|
3
|
+
*
|
|
4
|
+
* Nothing here decides anything yet. What this module fixes is the RESULT
|
|
5
|
+
* TYPE, because that is the half a caller depends on and the half that is
|
|
6
|
+
* expensive to change once six checks have their own version of it. A check
|
|
7
|
+
* returns a record; a gate reads records; neither side reads a process exit
|
|
8
|
+
* code or a line of prose.
|
|
9
|
+
*
|
|
10
|
+
* The checks themselves arrive one task at a time - see `factory/tasks/`.
|
|
11
|
+
*/
|
|
12
|
+
import { REGISTRY } from "./registry.js";
|
|
13
|
+
/**
|
|
14
|
+
* The one check that exists, and it checks the package's own premise: that a
|
|
15
|
+
* check is a record and not an exit code.
|
|
16
|
+
*
|
|
17
|
+
* It is a placeholder in what it MEASURES and not in what it RETURNS. It is
|
|
18
|
+
* kept once the real checks land, because a package whose whole contract is a
|
|
19
|
+
* record type should have one test that fails if the record type moves.
|
|
20
|
+
*/
|
|
21
|
+
export function selfCheck() {
|
|
22
|
+
return {
|
|
23
|
+
name: "self",
|
|
24
|
+
status: "pass",
|
|
25
|
+
evidence: [`repo-truth is installed; ${REGISTRY.length} checks registered`],
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Whether a set of records lets a candidate through.
|
|
30
|
+
*
|
|
31
|
+
* A `skip` does not block: a check that could not run has not found anything,
|
|
32
|
+
* and treating "did not run" as "found a defect" is how a gate gets switched
|
|
33
|
+
* off wholesale. A check that must not be skippable says so by failing instead.
|
|
34
|
+
*/
|
|
35
|
+
export function allPassed(records) {
|
|
36
|
+
return records.every((record) => record.status !== "fail");
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAsBzC;;;;;;;GAOG;AACH,MAAM,UAAU,SAAS;IACvB,OAAO;QACL,IAAI,EAAE,MAAM;QACZ,MAAM,EAAE,MAAM;QACd,QAAQ,EAAE,CAAC,4BAA4B,QAAQ,CAAC,MAAM,oBAAoB,CAAC;KAC5E,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAAC,OAA+B;IACvD,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC;AAC7D,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Large blobs.
|
|
3
|
+
*
|
|
4
|
+
* History keeps every blob a commit ever held, so a build artefact or a
|
|
5
|
+
* dataset committed by accident and deleted again is gone from the tree and
|
|
6
|
+
* still in every clone. This check measures every blob the range brings into
|
|
7
|
+
* the history - those reachable from `candidate` and not from `base` - and
|
|
8
|
+
* names each one over a size limit, including blobs the candidate's own tree
|
|
9
|
+
* no longer holds.
|
|
10
|
+
*
|
|
11
|
+
* A blob reachable from `base` is never a finding, so a rename or a copy of a
|
|
12
|
+
* large base blob (same object, new path) and a change beside it stay quiet.
|
|
13
|
+
* Sizes come from the object store through `git cat-file`, never from the
|
|
14
|
+
* working tree. A blob larger than the limit is a finding; one exactly at it
|
|
15
|
+
* is not. The limit is `maxBytes`, and `DEFAULT_MAX_BLOB_BYTES` when absent.
|
|
16
|
+
*
|
|
17
|
+
* The check fails closed: an unresolvable revision, a failing git invocation
|
|
18
|
+
* or an invalid `maxBytes` is a `fail` record naming the cause.
|
|
19
|
+
*/
|
|
20
|
+
import type { CheckRecord } from "./index.js";
|
|
21
|
+
/** One mebibyte: the limit when the caller sets none. */
|
|
22
|
+
export declare const DEFAULT_MAX_BLOB_BYTES = 1048576;
|
|
23
|
+
/** Where to look, which two revisions bound the range, and the size limit. */
|
|
24
|
+
export interface LargeBlobsOptions {
|
|
25
|
+
/** The git repository to run in. */
|
|
26
|
+
readonly cwd: string;
|
|
27
|
+
/** The revision the range starts after. */
|
|
28
|
+
readonly base: string;
|
|
29
|
+
/** The revision the range ends at. */
|
|
30
|
+
readonly candidate: string;
|
|
31
|
+
/** The largest blob size, in bytes, that is not a finding. */
|
|
32
|
+
readonly maxBytes?: number;
|
|
33
|
+
}
|
|
34
|
+
/** Measure every blob the range adds to the history against the size limit. */
|
|
35
|
+
export declare function checkLargeBlobs(options: LargeBlobsOptions): Promise<CheckRecord>;
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Large blobs.
|
|
3
|
+
*
|
|
4
|
+
* History keeps every blob a commit ever held, so a build artefact or a
|
|
5
|
+
* dataset committed by accident and deleted again is gone from the tree and
|
|
6
|
+
* still in every clone. This check measures every blob the range brings into
|
|
7
|
+
* the history - those reachable from `candidate` and not from `base` - and
|
|
8
|
+
* names each one over a size limit, including blobs the candidate's own tree
|
|
9
|
+
* no longer holds.
|
|
10
|
+
*
|
|
11
|
+
* A blob reachable from `base` is never a finding, so a rename or a copy of a
|
|
12
|
+
* large base blob (same object, new path) and a change beside it stay quiet.
|
|
13
|
+
* Sizes come from the object store through `git cat-file`, never from the
|
|
14
|
+
* working tree. A blob larger than the limit is a finding; one exactly at it
|
|
15
|
+
* is not. The limit is `maxBytes`, and `DEFAULT_MAX_BLOB_BYTES` when absent.
|
|
16
|
+
*
|
|
17
|
+
* The check fails closed: an unresolvable revision, a failing git invocation
|
|
18
|
+
* or an invalid `maxBytes` is a `fail` record naming the cause.
|
|
19
|
+
*/
|
|
20
|
+
import { execFile } from "node:child_process";
|
|
21
|
+
const NAME = "RT-12";
|
|
22
|
+
/** One mebibyte: the limit when the caller sets none. */
|
|
23
|
+
export const DEFAULT_MAX_BLOB_BYTES = 1048576;
|
|
24
|
+
function runGit(cwd, args, input) {
|
|
25
|
+
return new Promise((resolve, reject) => {
|
|
26
|
+
const child = execFile("git", args, { cwd, maxBuffer: 1024 * 1024 * 1024, encoding: "buffer" }, (error, stdout, stderr) => {
|
|
27
|
+
if (error) {
|
|
28
|
+
const detail = stderr.toString("utf8").trim();
|
|
29
|
+
reject(new Error(detail.length > 0 ? detail : error.message));
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
resolve(stdout);
|
|
33
|
+
});
|
|
34
|
+
child.stdin?.on("error", () => undefined);
|
|
35
|
+
child.stdin?.end(input ?? "");
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
/** Escape control characters so a name or revision cannot replay a terminal sequence or split a line. */
|
|
39
|
+
function escapeControl(text) {
|
|
40
|
+
let escaped = "";
|
|
41
|
+
for (const char of text) {
|
|
42
|
+
const codePoint = char.codePointAt(0) ?? 0;
|
|
43
|
+
if (codePoint < 0x20 || (codePoint >= 0x7f && codePoint < 0xa0)) {
|
|
44
|
+
escaped += `\\u{${codePoint.toString(16).padStart(4, "0")}}`;
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
escaped += char;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return escaped;
|
|
51
|
+
}
|
|
52
|
+
function quote(text) {
|
|
53
|
+
return `"${escapeControl(text)}"`;
|
|
54
|
+
}
|
|
55
|
+
function decode(bytes) {
|
|
56
|
+
try {
|
|
57
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return bytes.toString("latin1");
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async function resolveCommit(cwd, revision) {
|
|
64
|
+
if (revision.length === 0 || revision.startsWith("-")) {
|
|
65
|
+
throw new Error(`revision ${quote(revision)} names no commit`);
|
|
66
|
+
}
|
|
67
|
+
let hash;
|
|
68
|
+
try {
|
|
69
|
+
const out = await runGit(cwd, ["rev-parse", "--verify", "--quiet", `${revision}^{commit}`]);
|
|
70
|
+
hash = out.toString("utf8").trim();
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
throw new Error(`revision ${quote(revision)} names no commit: ${error.message}`);
|
|
74
|
+
}
|
|
75
|
+
if (hash.length === 0) {
|
|
76
|
+
throw new Error(`revision ${quote(revision)} names no commit`);
|
|
77
|
+
}
|
|
78
|
+
return hash;
|
|
79
|
+
}
|
|
80
|
+
const OBJECT_LINE = /^([0-9a-f]{40}|[0-9a-f]{64})(?: (.*))?$/;
|
|
81
|
+
/** Every object `rev-list --objects` lists, with the first path (in sort order) it names. */
|
|
82
|
+
function parseObjects(out) {
|
|
83
|
+
const objects = new Map();
|
|
84
|
+
for (const line of decode(out).split("\n")) {
|
|
85
|
+
const match = OBJECT_LINE.exec(line);
|
|
86
|
+
if (match === null)
|
|
87
|
+
continue;
|
|
88
|
+
const oid = match[1];
|
|
89
|
+
const path = match[2];
|
|
90
|
+
const known = objects.get(oid);
|
|
91
|
+
if (!objects.has(oid) || (path !== undefined && (known === undefined || path < known))) {
|
|
92
|
+
objects.set(oid, path);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return objects;
|
|
96
|
+
}
|
|
97
|
+
async function findLargeBlobs(cwd, base, candidate, max) {
|
|
98
|
+
const added = parseObjects(await runGit(cwd, ["rev-list", "--objects", candidate, `^${base}`, "--"]));
|
|
99
|
+
// Reachability from the base is by history, not by the base's tip alone.
|
|
100
|
+
const held = parseObjects(await runGit(cwd, ["rev-list", "--objects", base, "--"]));
|
|
101
|
+
const fresh = [...added.keys()].filter((oid) => !held.has(oid));
|
|
102
|
+
if (fresh.length === 0)
|
|
103
|
+
return { findings: [], added: 0 };
|
|
104
|
+
const sizes = decode(await runGit(cwd, ["cat-file", "--batch-check=%(objectname) %(objecttype) %(objectsize)"], `${fresh.join("\n")}\n`));
|
|
105
|
+
const large = new Map();
|
|
106
|
+
let addedBlobs = 0;
|
|
107
|
+
for (const line of sizes.split("\n")) {
|
|
108
|
+
if (line.length === 0)
|
|
109
|
+
continue;
|
|
110
|
+
const parts = line.split(" ");
|
|
111
|
+
if (parts.length !== 3 || parts[1] === "missing") {
|
|
112
|
+
throw new Error(`git cat-file could not measure an object: ${quote(line)}`);
|
|
113
|
+
}
|
|
114
|
+
if (parts[1] === "blob")
|
|
115
|
+
addedBlobs += 1;
|
|
116
|
+
if (parts[1] === "blob" && Number(parts[2]) > max) {
|
|
117
|
+
large.set(parts[0], Number(parts[2]));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (large.size === 0)
|
|
121
|
+
return { findings: [], added: addedBlobs };
|
|
122
|
+
const tree = decode(await runGit(cwd, ["ls-tree", "-r", "--format=%(objectname)", candidate, "--"]));
|
|
123
|
+
const inTree = new Set(tree.split("\n").filter((line) => line.length > 0));
|
|
124
|
+
const findings = [];
|
|
125
|
+
for (const [oid, size] of large) {
|
|
126
|
+
findings.push({ path: added.get(oid) ?? oid, size, inTree: inTree.has(oid) });
|
|
127
|
+
}
|
|
128
|
+
findings.sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));
|
|
129
|
+
return { findings, added: addedBlobs };
|
|
130
|
+
}
|
|
131
|
+
function failure(cause) {
|
|
132
|
+
return { name: NAME, status: "fail", evidence: [cause] };
|
|
133
|
+
}
|
|
134
|
+
/** Measure every blob the range adds to the history against the size limit. */
|
|
135
|
+
export async function checkLargeBlobs(options) {
|
|
136
|
+
try {
|
|
137
|
+
const max = options.maxBytes ?? DEFAULT_MAX_BLOB_BYTES;
|
|
138
|
+
if (typeof max !== "number" || !Number.isSafeInteger(max) || max <= 0) {
|
|
139
|
+
return failure(`maxBytes ${quote(String(max))} is not a positive integer`);
|
|
140
|
+
}
|
|
141
|
+
const base = await resolveCommit(options.cwd, options.base);
|
|
142
|
+
const candidate = await resolveCommit(options.cwd, options.candidate);
|
|
143
|
+
const { findings, added } = await findLargeBlobs(options.cwd, base, candidate, max);
|
|
144
|
+
if (findings.length === 0) {
|
|
145
|
+
return {
|
|
146
|
+
name: NAME,
|
|
147
|
+
status: "pass",
|
|
148
|
+
evidence: [
|
|
149
|
+
`${added} blob(s) added in ${quote(options.base)}..${quote(options.candidate)}, none over ${max} bytes`,
|
|
150
|
+
],
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
name: NAME,
|
|
155
|
+
status: "fail",
|
|
156
|
+
evidence: findings.map((f) => `${quote(f.path)} is ${f.size} bytes, over the limit of ${max} bytes${f.inTree ? "" : " (only in history)"}`),
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
return failure(`large-blobs check could not run: ${error.message}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=large-blobs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"large-blobs.js","sourceRoot":"","sources":["../src/large-blobs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAI9C,MAAM,IAAI,GAAG,OAAO,CAAC;AAErB,yDAAyD;AACzD,MAAM,CAAC,MAAM,sBAAsB,GAAG,OAAO,CAAC;AAc9C,SAAS,MAAM,CAAC,GAAW,EAAE,IAAuB,EAAE,KAAc;IAClE,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,KAAK,GAAG,QAAQ,CACpB,KAAK,EACL,IAAgB,EAChB,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAC1D,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;YACxB,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;gBAC9C,MAAM,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;gBAC9D,OAAO;YACT,CAAC;YACD,OAAO,CAAC,MAAM,CAAC,CAAC;QAClB,CAAC,CACF,CAAC;QACF,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAC1C,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;AACL,CAAC;AAED,yGAAyG;AACzG,SAAS,aAAa,CAAC,IAAY;IACjC,IAAI,OAAO,GAAG,EAAE,CAAC;IACjB,KAAK,MAAM,IAAI,IAAI,IAAI,EAAE,CAAC;QACxB,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC3C,IAAI,SAAS,GAAG,IAAI,IAAI,CAAC,SAAS,IAAI,IAAI,IAAI,SAAS,GAAG,IAAI,CAAC,EAAE,CAAC;YAChE,OAAO,IAAI,OAAO,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC;QAC/D,CAAC;aAAM,CAAC;YACN,OAAO,IAAI,IAAI,CAAC;QAClB,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,KAAK,CAAC,IAAY;IACzB,OAAO,IAAI,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC;AACpC,CAAC;AAED,SAAS,MAAM,CAAC,KAAa;IAC3B,IAAI,CAAC;QACH,OAAO,IAAI,WAAW,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACjE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAClC,CAAC;AACH,CAAC;AAED,KAAK,UAAU,aAAa,CAAC,GAAW,EAAE,QAAgB;IACxD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACtD,MAAM,IAAI,KAAK,CAAC,YAAY,KAAK,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC;IACjE,CAAC;IACD,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,QAAQ,WAAW,CAAC,CAAC,CAAC;QAC5F,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;IACrC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,YAAY,KAAK,CAAC,QAAQ,CAAC,qBAAsB,KAAe,CAAC,OAAO,EAAE,CAAC,CAAC;IAC9F,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,YAAY,KAAK,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC;IACjE,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,WAAW,GAAG,yCAAyC,CAAC;AAE9D,6FAA6F;AAC7F,SAAS,YAAY,CAAC,GAAW;IAC/B,MAAM,OAAO,GAAG,IAAI,GAAG,EAA8B,CAAC;IACtD,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3C,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrC,IAAI,KAAK,KAAK,IAAI;YAAE,SAAS;QAC7B,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAW,CAAC;QAC/B,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,KAAK,KAAK,SAAS,IAAI,IAAI,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC;YACvF,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAQD,KAAK,UAAU,cAAc,CAC3B,GAAW,EACX,IAAY,EACZ,SAAiB,EACjB,GAAW;IAEX,MAAM,KAAK,GAAG,YAAY,CAAC,MAAM,MAAM,CAAC,GAAG,EAAE,CAAC,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;IACtG,yEAAyE;IACzE,MAAM,IAAI,GAAG,YAAY,CAAC,MAAM,MAAM,CAAC,GAAG,EAAE,CAAC,UAAU,EAAE,WAAW,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;IACpF,MAAM,KAAK,GAAG,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IAChE,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;IAE1D,MAAM,KAAK,GAAG,MAAM,CAClB,MAAM,MAAM,CAAC,GAAG,EAAE,CAAC,UAAU,EAAE,yDAAyD,CAAC,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CACpH,CAAC;IACF,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;IACxC,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACrC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QAChC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC9B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,SAAS,EAAE,CAAC;YACjD,MAAM,IAAI,KAAK,CAAC,6CAA6C,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC9E,CAAC;QACD,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,MAAM;YAAE,UAAU,IAAI,CAAC,CAAC;QACzC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,MAAM,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC;YAClD,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAW,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAClD,CAAC;IACH,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;IAEjE,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,MAAM,CAAC,GAAG,EAAE,CAAC,SAAS,EAAE,IAAI,EAAE,wBAAwB,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;IACrG,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;IAE3E,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,KAAK,EAAE,CAAC;QAChC,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChF,CAAC;IACD,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1E,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;AACzC,CAAC;AAED,SAAS,OAAO,CAAC,KAAa;IAC5B,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;AAC3D,CAAC;AAED,+EAA+E;AAC/E,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,OAA0B;IAC9D,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,OAAO,CAAC,QAAQ,IAAI,sBAAsB,CAAC;QACvD,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC;YACtE,OAAO,OAAO,CAAC,YAAY,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,4BAA4B,CAAC,CAAC;QAC7E,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;QAC5D,MAAM,SAAS,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;QACtE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC;QACpF,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO;gBACL,IAAI,EAAE,IAAI;gBACV,MAAM,EAAE,MAAM;gBACd,QAAQ,EAAE;oBACR,GAAG,KAAK,qBAAqB,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,eAAe,GAAG,QAAQ;iBACxG;aACF,CAAC;QACJ,CAAC;QACD,OAAO;YACL,IAAI,EAAE,IAAI;YACV,MAAM,EAAE,MAAM;YACd,QAAQ,EAAE,QAAQ,CAAC,GAAG,CACpB,CAAC,CAAC,EAAE,EAAE,CACJ,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,6BAA6B,GAAG,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,oBAAoB,EAAE,CAC/G;SACF,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,OAAO,CAAC,oCAAqC,KAAe,CAAC,OAAO,EAAE,CAAC,CAAC;IACjF,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lockfile drift.
|
|
3
|
+
*
|
|
4
|
+
* The manifest (the root `package.json`) and the lockfile are two statements of
|
|
5
|
+
* one install. A range that changes a dependency field of the manifest without
|
|
6
|
+
* the lockfile leaves a pin that no longer matches what was asked for (a stale
|
|
7
|
+
* pin); a range that changes the lockfile without a dependency field of the
|
|
8
|
+
* manifest moves a version with no request behind it (an unrequested change).
|
|
9
|
+
* The two are reported in different words because the fix and the alarm differ.
|
|
10
|
+
*
|
|
11
|
+
* Lockfiles are never parsed: they are compared by git object id at the two
|
|
12
|
+
* revisions. Only the manifest is parsed, and only to compare its dependency
|
|
13
|
+
* fields, so a script or description edit is not drift. A repository with no
|
|
14
|
+
* manifest or no recognised lockfile gets a `skip` naming what was looked for.
|
|
15
|
+
*/
|
|
16
|
+
import type { CheckRecord } from "./index.js";
|
|
17
|
+
/** Where to look, and which two revisions bound the range. */
|
|
18
|
+
export interface LockfileDriftOptions {
|
|
19
|
+
/** The git repository to run in. */
|
|
20
|
+
readonly cwd: string;
|
|
21
|
+
/** The revision the range starts after. */
|
|
22
|
+
readonly base: string;
|
|
23
|
+
/** The revision the range ends at. */
|
|
24
|
+
readonly candidate: string;
|
|
25
|
+
}
|
|
26
|
+
/** The manifest fields that say what to install. */
|
|
27
|
+
export declare const DEPENDENCY_FIELDS: string[];
|
|
28
|
+
/** The lockfiles recognised at the repository root. */
|
|
29
|
+
export declare const LOCKFILES: string[];
|
|
30
|
+
/**
|
|
31
|
+
* Compare the manifest's dependency fields and the root lockfiles between the
|
|
32
|
+
* merge base of `base` and `candidate` and `candidate`. Fails closed: a
|
|
33
|
+
* revision that names no commit, or any git invocation that fails, is a `fail`
|
|
34
|
+
* record naming the cause - never a `pass`, and never a thrown error.
|
|
35
|
+
*/
|
|
36
|
+
export declare function checkLockfileDrift(options: LockfileDriftOptions): Promise<CheckRecord>;
|