@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,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path policy.
|
|
3
|
+
*
|
|
4
|
+
* Which paths a change should not reach without a second look is the
|
|
5
|
+
* repository's to say, so this check holds no list: it reads `forbidden` and
|
|
6
|
+
* `allowed` from its options and reports each path the range adds, changes,
|
|
7
|
+
* deletes or renames - both ends of a rename - that a `forbidden` pattern
|
|
8
|
+
* matches or, when `allowed` is given, that no `allowed` pattern matches.
|
|
9
|
+
*
|
|
10
|
+
* Paths come from git's list of the changes between the merge base and the
|
|
11
|
+
* candidate, never from the working tree. A pattern is matched against a whole
|
|
12
|
+
* path: `?` is one character but `/`, `*` is any run but `/`, a segment that
|
|
13
|
+
* is exactly `**` is any number of whole segments (none included), and every
|
|
14
|
+
* other character - case included - matches only itself.
|
|
15
|
+
*/
|
|
16
|
+
import { execFile } from "node:child_process";
|
|
17
|
+
import { SANCTION_KEY } from "../weakened-tests.js";
|
|
18
|
+
export const name = "RT-57";
|
|
19
|
+
/** The options a configuration file may set for this check. */
|
|
20
|
+
export const optionKinds = { forbidden: "strings", allowed: "strings" };
|
|
21
|
+
function matchSegment(pattern, text) {
|
|
22
|
+
// `**` that is not a whole segment reads as two `*`, which is one `*`.
|
|
23
|
+
let p = 0;
|
|
24
|
+
let t = 0;
|
|
25
|
+
let star = -1;
|
|
26
|
+
let resume = 0;
|
|
27
|
+
while (t < text.length) {
|
|
28
|
+
const c = pattern[p];
|
|
29
|
+
if (c === "*") {
|
|
30
|
+
star = p++;
|
|
31
|
+
resume = t;
|
|
32
|
+
}
|
|
33
|
+
else if (p < pattern.length && (c === "?" || c === text[t])) {
|
|
34
|
+
p++;
|
|
35
|
+
t++;
|
|
36
|
+
}
|
|
37
|
+
else if (star >= 0) {
|
|
38
|
+
p = star + 1;
|
|
39
|
+
t = ++resume;
|
|
40
|
+
}
|
|
41
|
+
else
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
while (pattern[p] === "*")
|
|
45
|
+
p++;
|
|
46
|
+
return p === pattern.length;
|
|
47
|
+
}
|
|
48
|
+
function matchSegments(pattern, path, p, s) {
|
|
49
|
+
if (p === pattern.length)
|
|
50
|
+
return s === path.length;
|
|
51
|
+
if (pattern[p] === "**") {
|
|
52
|
+
while (pattern[p + 1] === "**")
|
|
53
|
+
p++;
|
|
54
|
+
for (let skip = s; skip <= path.length; skip++)
|
|
55
|
+
if (matchSegments(pattern, path, p + 1, skip))
|
|
56
|
+
return true;
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
return s < path.length && matchSegment(pattern[p], path[s]) && matchSegments(pattern, path, p + 1, s + 1);
|
|
60
|
+
}
|
|
61
|
+
/** Whether one pattern matches one whole path. */
|
|
62
|
+
export function matchesPattern(pattern, path) {
|
|
63
|
+
return matchSegments(pattern.split("/"), path.split("/"), 0, 0);
|
|
64
|
+
}
|
|
65
|
+
function runGit(cwd, args) {
|
|
66
|
+
return new Promise((resolve, reject) => {
|
|
67
|
+
execFile("git", args, { cwd, maxBuffer: 1024 * 1024 * 1024, encoding: "utf8" }, (error, stdout, stderr) => {
|
|
68
|
+
if (error)
|
|
69
|
+
reject(new Error(stderr.trim() || error.message));
|
|
70
|
+
else
|
|
71
|
+
resolve(stdout);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/** Escape everything outside printable ASCII, so evidence cannot replay a control sequence. */
|
|
76
|
+
function escapeText(text) {
|
|
77
|
+
let escaped = "";
|
|
78
|
+
for (const char of text) {
|
|
79
|
+
const codePoint = char.codePointAt(0) ?? 0;
|
|
80
|
+
escaped += codePoint >= 0x20 && codePoint <= 0x7e ? char : `\\u{${codePoint.toString(16).padStart(4, "0")}}`;
|
|
81
|
+
}
|
|
82
|
+
return escaped;
|
|
83
|
+
}
|
|
84
|
+
async function resolveCommit(cwd, revision) {
|
|
85
|
+
const fail = (cause) => new Error(`revision "${escapeText(String(revision))}" does not name a commit${cause}`);
|
|
86
|
+
// A revision that begins with a dash would be read as an option.
|
|
87
|
+
if (typeof revision !== "string" || revision.length === 0 || revision.startsWith("-"))
|
|
88
|
+
throw fail("");
|
|
89
|
+
let hash = "";
|
|
90
|
+
try {
|
|
91
|
+
hash = (await runGit(cwd, ["rev-parse", "--verify", "--quiet", `${revision}^{commit}`])).trim();
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
throw fail(`: ${error.message}`);
|
|
95
|
+
}
|
|
96
|
+
if (hash.length === 0)
|
|
97
|
+
throw fail("");
|
|
98
|
+
return hash;
|
|
99
|
+
}
|
|
100
|
+
/** Every path a change names, once: both ends of a rename. */
|
|
101
|
+
async function judgedPaths(cwd, from, to) {
|
|
102
|
+
const out = await runGit(cwd, ["diff", "--raw", "-z", "-M", "--no-abbrev", "--no-ext-diff", "--no-relative", from, to, "--"]);
|
|
103
|
+
const fields = out.split("\0");
|
|
104
|
+
const paths = new Set();
|
|
105
|
+
for (let i = 0; i < fields.length; i++) {
|
|
106
|
+
const head = fields[i] ?? "";
|
|
107
|
+
if (!head.startsWith(":"))
|
|
108
|
+
continue;
|
|
109
|
+
const status = head.slice(1).split(" ")[4] ?? "";
|
|
110
|
+
const count = status.startsWith("R") || status.startsWith("C") ? 2 : 1;
|
|
111
|
+
for (let k = 0; k < count; k++)
|
|
112
|
+
paths.add(fields[++i] ?? "");
|
|
113
|
+
}
|
|
114
|
+
return [...paths].sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
|
|
115
|
+
}
|
|
116
|
+
async function readSanctionLines(cwd, from, to) {
|
|
117
|
+
const out = await runGit(cwd, ["log", "--format=%B%x00", `${from}..${to}`, "--"]);
|
|
118
|
+
const prefix = `${SANCTION_KEY}:`;
|
|
119
|
+
return out
|
|
120
|
+
.split(/[\n\0]/)
|
|
121
|
+
.map((line) => line.trim())
|
|
122
|
+
.filter((line) => line.startsWith(prefix))
|
|
123
|
+
.map((line) => line.slice(prefix.length).trim());
|
|
124
|
+
}
|
|
125
|
+
function sanctionFor(id, lines, given) {
|
|
126
|
+
const reason = Object.hasOwn(given, id) ? given[id] : undefined;
|
|
127
|
+
if (typeof reason === "string" && reason.trim().length > 0)
|
|
128
|
+
return reason.trim();
|
|
129
|
+
for (const line of lines) {
|
|
130
|
+
const rest = line.slice(id.length);
|
|
131
|
+
if (line.startsWith(id) && /^\s+\S/.test(rest))
|
|
132
|
+
return rest.trim();
|
|
133
|
+
}
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
function patternList(option, value) {
|
|
137
|
+
if (value === undefined)
|
|
138
|
+
return [];
|
|
139
|
+
if (!Array.isArray(value) || !value.every((entry) => typeof entry === "string")) {
|
|
140
|
+
throw new Error(`option "${option}" is not an array of strings`);
|
|
141
|
+
}
|
|
142
|
+
return value;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Report each path `base..candidate` touches that the policy forbids or does
|
|
146
|
+
* not allow. Fails while any finding is unsanctioned, and fails closed: a bad
|
|
147
|
+
* revision, a bad option or a failing git invocation is a `fail` record naming
|
|
148
|
+
* the cause - never a `pass`, never a throw.
|
|
149
|
+
*/
|
|
150
|
+
export async function check(options) {
|
|
151
|
+
try {
|
|
152
|
+
const { cwd, base, candidate } = options;
|
|
153
|
+
const baseCommit = await resolveCommit(cwd, base);
|
|
154
|
+
const candidateCommit = await resolveCommit(cwd, candidate);
|
|
155
|
+
const forbidden = patternList("forbidden", options.forbidden);
|
|
156
|
+
const allowed = patternList("allowed", options.allowed);
|
|
157
|
+
if (forbidden.length === 0 && allowed.length === 0) {
|
|
158
|
+
return { name, status: "skip", evidence: ["no path policy is configured"] };
|
|
159
|
+
}
|
|
160
|
+
let ancestor;
|
|
161
|
+
try {
|
|
162
|
+
ancestor = (await runGit(cwd, ["merge-base", baseCommit, candidateCommit])).trim();
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
throw new Error(`no merge base of "${escapeText(base)}" and "${escapeText(candidate)}": ${error.message}`);
|
|
166
|
+
}
|
|
167
|
+
const paths = await judgedPaths(cwd, ancestor, candidateCommit);
|
|
168
|
+
const drafts = [];
|
|
169
|
+
for (const path of paths) {
|
|
170
|
+
const hit = forbidden.find((pattern) => matchesPattern(pattern, path));
|
|
171
|
+
if (hit !== undefined)
|
|
172
|
+
drafts.push({ id: `path-forbidden:${path}`, path, category: "path-forbidden", pattern: hit });
|
|
173
|
+
if (allowed.length > 0 && !allowed.some((pattern) => matchesPattern(pattern, path))) {
|
|
174
|
+
drafts.push({ id: `path-not-allowed:${path}`, path, category: "path-not-allowed", pattern: null });
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (drafts.length === 0)
|
|
178
|
+
return { name, status: "pass", evidence: ["no path outside the policy"] };
|
|
179
|
+
const lines = await readSanctionLines(cwd, baseCommit, candidateCommit);
|
|
180
|
+
let open = false;
|
|
181
|
+
const evidence = drafts.map((draft) => {
|
|
182
|
+
const reason = sanctionFor(draft.id, lines, options.sanctions ?? {});
|
|
183
|
+
if (reason === null)
|
|
184
|
+
open = true;
|
|
185
|
+
const pattern = draft.pattern === null ? "" : ` "${escapeText(draft.pattern)}"`;
|
|
186
|
+
const tail = reason === null ? "" : ` - sanctioned: ${escapeText(reason)}`;
|
|
187
|
+
return `${escapeText(draft.path)}: ${draft.category}${pattern}${tail}`;
|
|
188
|
+
});
|
|
189
|
+
return { name, status: open ? "fail" : "pass", evidence };
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
return { name, status: "fail", evidence: [escapeText(error instanceof Error ? error.message : String(error))] };
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
//# sourceMappingURL=path-policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path-policy.js","sourceRoot":"","sources":["../../src/checks/path-policy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Raised timeouts.
|
|
3
|
+
*
|
|
4
|
+
* A test that starts failing by running out of time passes again the moment its
|
|
5
|
+
* timeout goes up. This check reads the lines `base..candidate` changes in test
|
|
6
|
+
* files and in the runner's configuration, pairs each timeout the range removes
|
|
7
|
+
* with the one it adds for the same setting, and reports one that went up
|
|
8
|
+
* (`timeout-raised`) or one added to a file the merge base holds
|
|
9
|
+
* (`timeout-added`) for a human to sanction. Only the diff is read, through git.
|
|
10
|
+
*
|
|
11
|
+
* A value may carry the digit separator `_` between digits, in its integer part
|
|
12
|
+
* and in its fraction. Values are compared with the separators dropped, and the
|
|
13
|
+
* evidence names each value as the line writes it.
|
|
14
|
+
*
|
|
15
|
+
* Which files and spellings count lives in the exported `TIMEOUT_SPELLINGS`,
|
|
16
|
+
* read each time the check runs: a consumer appends its own entry.
|
|
17
|
+
*/
|
|
18
|
+
import type { CheckRecord } from "../index.js";
|
|
19
|
+
export declare const name = "RT-24";
|
|
20
|
+
export type TimeoutCategory = "timeout-raised" | "timeout-added";
|
|
21
|
+
/** One spelling of a timeout. */
|
|
22
|
+
export interface TimeoutSpelling {
|
|
23
|
+
/** What the evidence calls this spelling. */
|
|
24
|
+
readonly name: string;
|
|
25
|
+
/** Tested against a changed path. */
|
|
26
|
+
readonly files: RegExp;
|
|
27
|
+
/**
|
|
28
|
+
* Tested against each line the range removes or adds in such a file. The
|
|
29
|
+
* named group `value` holds the number; the optional group `key` names the
|
|
30
|
+
* setting. A line may hold several matches.
|
|
31
|
+
*/
|
|
32
|
+
readonly pattern: RegExp;
|
|
33
|
+
/** A line `pattern` matches. */
|
|
34
|
+
readonly example: string;
|
|
35
|
+
}
|
|
36
|
+
export declare const TIMEOUT_SPELLINGS: TimeoutSpelling[];
|
|
37
|
+
/** One timeout the range raised or added; `id` is `<category>:<file>:<line>`, the line in the candidate. */
|
|
38
|
+
export interface TimeoutFinding {
|
|
39
|
+
readonly id: string;
|
|
40
|
+
readonly category: TimeoutCategory;
|
|
41
|
+
readonly file: string;
|
|
42
|
+
readonly line: number;
|
|
43
|
+
readonly name: string;
|
|
44
|
+
readonly key: string | null;
|
|
45
|
+
readonly oldValue: string | null;
|
|
46
|
+
readonly newValue: string;
|
|
47
|
+
/** The sanction's reason, or `null` while unsanctioned. */
|
|
48
|
+
readonly sanction: string | null;
|
|
49
|
+
}
|
|
50
|
+
export interface RaisedTimeoutsRecord extends CheckRecord {
|
|
51
|
+
readonly findings: readonly TimeoutFinding[];
|
|
52
|
+
}
|
|
53
|
+
/** `sanctions` keys are finding ids or `<category>:<file>`; values are the reasons. */
|
|
54
|
+
export interface RaisedTimeoutsOptions {
|
|
55
|
+
readonly cwd: string;
|
|
56
|
+
readonly base: string;
|
|
57
|
+
readonly candidate: string;
|
|
58
|
+
readonly sanctions?: Readonly<Record<string, string>> | undefined;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Report the timeouts `base..candidate` raises or adds to an existing file.
|
|
62
|
+
* Fails while any finding is unsanctioned, and fails closed: a revision that
|
|
63
|
+
* names no commit, or any git invocation that fails, is a `fail` record naming
|
|
64
|
+
* the cause - never a `pass`, and never a thrown error.
|
|
65
|
+
*/
|
|
66
|
+
export declare function check(options: RaisedTimeoutsOptions): Promise<RaisedTimeoutsRecord>;
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Raised timeouts.
|
|
3
|
+
*
|
|
4
|
+
* A test that starts failing by running out of time passes again the moment its
|
|
5
|
+
* timeout goes up. This check reads the lines `base..candidate` changes in test
|
|
6
|
+
* files and in the runner's configuration, pairs each timeout the range removes
|
|
7
|
+
* with the one it adds for the same setting, and reports one that went up
|
|
8
|
+
* (`timeout-raised`) or one added to a file the merge base holds
|
|
9
|
+
* (`timeout-added`) for a human to sanction. Only the diff is read, through git.
|
|
10
|
+
*
|
|
11
|
+
* A value may carry the digit separator `_` between digits, in its integer part
|
|
12
|
+
* and in its fraction. Values are compared with the separators dropped, and the
|
|
13
|
+
* evidence names each value as the line writes it.
|
|
14
|
+
*
|
|
15
|
+
* Which files and spellings count lives in the exported `TIMEOUT_SPELLINGS`,
|
|
16
|
+
* read each time the check runs: a consumer appends its own entry.
|
|
17
|
+
*/
|
|
18
|
+
import { execFile } from "node:child_process";
|
|
19
|
+
import { SANCTION_KEY } from "../weakened-tests.js";
|
|
20
|
+
export const name = "RT-24";
|
|
21
|
+
const NUMBER = "(?<value>\\d+(?:_\\d+)*(?:\\.\\d+(?:_\\d+)*)?)";
|
|
22
|
+
/** A value as a number: each `_` between two digits dropped. */
|
|
23
|
+
const numeric = (value) => Number(value.replace(/(?<=\d)_(?=\d)/g, ""));
|
|
24
|
+
const KEYS = "(?<key>testTimeout|hookTimeout|teardownTimeout)";
|
|
25
|
+
const RUNNER_CONFIG = /(?:^|\/)(?:(?:vitest|vite|jest)\.config\.(?:[cm]?[jt]s|json)|package\.json)$/;
|
|
26
|
+
const TEST_FILE = /(?:(?:^|\/)(?:tests?|__tests__|spec)\/(?:.*\/)?[^/]*\.[cm]?[jt]sx?$|\.(?:test|spec)\.(?:[^/]*\.)?[cm]?[jt]sx?$)/;
|
|
27
|
+
const CONFIG_OR_TEST = new RegExp(`${RUNNER_CONFIG.source}|${TEST_FILE.source}`);
|
|
28
|
+
export const TIMEOUT_SPELLINGS = [
|
|
29
|
+
{
|
|
30
|
+
name: "timeout key",
|
|
31
|
+
files: CONFIG_OR_TEST,
|
|
32
|
+
pattern: new RegExp(`(?<![\\w.-])["']?${KEYS}["']?\\s*:\\s*${NUMBER}`),
|
|
33
|
+
example: "testTimeout: 5000,",
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: "jest.setTimeout",
|
|
37
|
+
files: TEST_FILE,
|
|
38
|
+
pattern: new RegExp(`(?<![\\w-])jest\\.setTimeout\\(\\s*${NUMBER}`),
|
|
39
|
+
example: "jest.setTimeout(30000);",
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: "timeout option",
|
|
43
|
+
files: TEST_FILE,
|
|
44
|
+
pattern: new RegExp(`(?<![\\w.-])["']?(?<key>timeout)["']?\\s*:\\s*${NUMBER}`),
|
|
45
|
+
example: "{ timeout: 30000 }",
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: "mocha this.timeout",
|
|
49
|
+
files: TEST_FILE,
|
|
50
|
+
pattern: new RegExp(`(?<![\\w-])this\\.timeout\\(\\s*${NUMBER}`),
|
|
51
|
+
example: "this.timeout(30000);",
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: "test timeout argument",
|
|
55
|
+
files: TEST_FILE,
|
|
56
|
+
pattern: new RegExp(`^\\s*\\}\\s*\\)?\\s*,\\s*${NUMBER}\\s*\\)`),
|
|
57
|
+
example: "}, 30000);",
|
|
58
|
+
},
|
|
59
|
+
];
|
|
60
|
+
function runGit(cwd, args) {
|
|
61
|
+
return new Promise((resolve, reject) => {
|
|
62
|
+
execFile("git", args, { cwd, maxBuffer: 1024 * 1024 * 1024, encoding: "utf8" }, (error, stdout, stderr) => {
|
|
63
|
+
if (error)
|
|
64
|
+
reject(new Error(stderr.trim() || error.message));
|
|
65
|
+
else
|
|
66
|
+
resolve(stdout);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
/** Escape everything outside printable ASCII, so evidence cannot replay a control sequence. */
|
|
71
|
+
function escapeText(text) {
|
|
72
|
+
let escaped = "";
|
|
73
|
+
for (const char of text) {
|
|
74
|
+
const codePoint = char.codePointAt(0) ?? 0;
|
|
75
|
+
escaped += codePoint >= 0x20 && codePoint <= 0x7e ? char : `\\u{${codePoint.toString(16).padStart(4, "0")}}`;
|
|
76
|
+
}
|
|
77
|
+
return escaped;
|
|
78
|
+
}
|
|
79
|
+
function quote(text) {
|
|
80
|
+
return `"${escapeText(text)}"`;
|
|
81
|
+
}
|
|
82
|
+
async function resolveCommit(cwd, revision) {
|
|
83
|
+
const fail = (cause) => new Error(`revision ${quote(String(revision))} does not name a commit${cause}`);
|
|
84
|
+
// A revision that begins with a dash would be read as an option.
|
|
85
|
+
if (typeof revision !== "string" || revision.length === 0 || revision.startsWith("-"))
|
|
86
|
+
throw fail("");
|
|
87
|
+
let hash = "";
|
|
88
|
+
try {
|
|
89
|
+
hash = (await runGit(cwd, ["rev-parse", "--verify", "--quiet", `${revision}^{commit}`])).trim();
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
throw fail(`: ${error.message}`);
|
|
93
|
+
}
|
|
94
|
+
if (hash.length === 0)
|
|
95
|
+
throw fail("");
|
|
96
|
+
return hash;
|
|
97
|
+
}
|
|
98
|
+
/** The lines changed between two blobs; a missing side (`null`) is a file that is not there. */
|
|
99
|
+
async function diffBlobs(cwd, oldBlob, newBlob) {
|
|
100
|
+
if (oldBlob === null || newBlob === null) {
|
|
101
|
+
const only = oldBlob ?? newBlob;
|
|
102
|
+
const text = only === null ? "" : await runGit(cwd, ["cat-file", "blob", only]);
|
|
103
|
+
const lines = text.includes("\0") ? [] : text.replace(/\n$/, "").split("\n").map((t, i) => ({ number: i + 1, text: t.replace(/\r$/, "") }));
|
|
104
|
+
return oldBlob === null ? { removed: [], added: lines } : { removed: lines, added: [] };
|
|
105
|
+
}
|
|
106
|
+
const out = await runGit(cwd, ["diff", "-U0", "--no-color", "--no-ext-diff", "--no-textconv", oldBlob, newBlob]);
|
|
107
|
+
const removed = [];
|
|
108
|
+
const added = [];
|
|
109
|
+
let oldLine = 0;
|
|
110
|
+
let newLine = 0;
|
|
111
|
+
let inHunk = false;
|
|
112
|
+
for (const raw of out.split("\n")) {
|
|
113
|
+
const hunk = /^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/.exec(raw);
|
|
114
|
+
const text = raw.slice(1).replace(/\r$/, "");
|
|
115
|
+
if (hunk !== null) {
|
|
116
|
+
oldLine = Number(hunk[1]);
|
|
117
|
+
newLine = Number(hunk[2]);
|
|
118
|
+
inHunk = true;
|
|
119
|
+
}
|
|
120
|
+
else if (inHunk && raw.startsWith("-"))
|
|
121
|
+
removed.push({ number: oldLine++, text });
|
|
122
|
+
else if (inHunk && raw.startsWith("+"))
|
|
123
|
+
added.push({ number: newLine++, text });
|
|
124
|
+
}
|
|
125
|
+
return { removed, added };
|
|
126
|
+
}
|
|
127
|
+
async function readChanges(cwd, from, to) {
|
|
128
|
+
const out = await runGit(cwd, ["diff", "--raw", "-z", "--no-abbrev", "--find-renames", "--no-relative", from, to, "--"]);
|
|
129
|
+
const fields = out.split("\0");
|
|
130
|
+
const changes = [];
|
|
131
|
+
for (let i = 0; i < fields.length; i++) {
|
|
132
|
+
const head = fields[i] ?? "";
|
|
133
|
+
if (!head.startsWith(":"))
|
|
134
|
+
continue;
|
|
135
|
+
const [oldMode = "", newMode = "", oldSha = "", newSha = "", status = ""] = head.slice(1).split(" ");
|
|
136
|
+
const first = fields[++i] ?? "";
|
|
137
|
+
const second = status.startsWith("R") || status.startsWith("C") ? (fields[++i] ?? "") : first;
|
|
138
|
+
const blob = (mode, sha) => (mode.startsWith("100") ? sha : null);
|
|
139
|
+
const lines = await diffBlobs(cwd, blob(oldMode, oldSha), blob(newMode, newSha));
|
|
140
|
+
changes.push({ oldPath: status.startsWith("A") ? null : first, newPath: status.startsWith("D") ? null : second, ...lines });
|
|
141
|
+
}
|
|
142
|
+
return changes;
|
|
143
|
+
}
|
|
144
|
+
function hitsOf(spelling, path, lines) {
|
|
145
|
+
if (path === null || !new RegExp(spelling.files.source, spelling.files.flags.replace(/[gy]/g, "")).test(path))
|
|
146
|
+
return [];
|
|
147
|
+
const pattern = new RegExp(spelling.pattern.source, spelling.pattern.flags.replace("g", "") + "g");
|
|
148
|
+
const hits = [];
|
|
149
|
+
for (const line of lines) {
|
|
150
|
+
for (const match of line.text.matchAll(pattern)) {
|
|
151
|
+
const value = match.groups?.["value"];
|
|
152
|
+
if (value !== undefined && Number.isFinite(numeric(value)))
|
|
153
|
+
hits.push({ line: line.number, key: match.groups?.["key"] ?? "", value });
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return hits;
|
|
157
|
+
}
|
|
158
|
+
function draftsOf(changes, spellings) {
|
|
159
|
+
const drafts = [];
|
|
160
|
+
for (const change of changes) {
|
|
161
|
+
for (const spelling of spellings) {
|
|
162
|
+
const removed = hitsOf(spelling, change.oldPath, change.removed);
|
|
163
|
+
const added = hitsOf(spelling, change.newPath, change.added);
|
|
164
|
+
for (const key of new Set([...removed, ...added].map((hit) => hit.key))) {
|
|
165
|
+
const before = removed.filter((hit) => hit.key === key);
|
|
166
|
+
added
|
|
167
|
+
.filter((hit) => hit.key === key)
|
|
168
|
+
.forEach((now, index) => {
|
|
169
|
+
const old = before[index];
|
|
170
|
+
const shared = { name: spelling.name, key: key === "" ? null : key, file: change.newPath ?? "", line: now.line, newValue: now.value };
|
|
171
|
+
if (old === undefined) {
|
|
172
|
+
if (change.oldPath !== null)
|
|
173
|
+
drafts.push({ category: "timeout-added", oldValue: null, ...shared });
|
|
174
|
+
}
|
|
175
|
+
else if (numeric(now.value) > numeric(old.value)) {
|
|
176
|
+
drafts.push({ category: "timeout-raised", oldValue: old.value, ...shared });
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return drafts;
|
|
183
|
+
}
|
|
184
|
+
/** The text after the sanction key on every such line of the range's commit messages. */
|
|
185
|
+
async function readSanctionLines(cwd, from, to) {
|
|
186
|
+
const out = await runGit(cwd, ["log", "--format=%B%x00", `${from}..${to}`, "--"]);
|
|
187
|
+
const prefix = `${SANCTION_KEY}:`;
|
|
188
|
+
return out
|
|
189
|
+
.split(/[\n\0]/)
|
|
190
|
+
.map((line) => line.trim())
|
|
191
|
+
.filter((line) => line.startsWith(prefix))
|
|
192
|
+
.map((line) => line.slice(prefix.length).trim());
|
|
193
|
+
}
|
|
194
|
+
function sanctionFor(draft, id, lines, given) {
|
|
195
|
+
for (const key of [id, `${draft.category}:${draft.file}`]) {
|
|
196
|
+
const reason = Object.hasOwn(given, key) ? given[key] : undefined;
|
|
197
|
+
if (typeof reason === "string" && reason.trim().length > 0)
|
|
198
|
+
return reason.trim();
|
|
199
|
+
for (const line of lines) {
|
|
200
|
+
const rest = line.slice(key.length);
|
|
201
|
+
if (line.startsWith(key) && /^\s+\S/.test(rest))
|
|
202
|
+
return rest.trim();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
function describeFinding(finding) {
|
|
208
|
+
const setting = finding.key === null ? finding.name : `${finding.name} ${finding.key}`;
|
|
209
|
+
const moved = finding.oldValue === null
|
|
210
|
+
? `added ${setting} ${finding.newValue}`
|
|
211
|
+
: `raised ${setting} from ${finding.oldValue} to ${finding.newValue}`;
|
|
212
|
+
const tail = finding.sanction === null ? "" : ` - sanctioned: ${finding.sanction}`;
|
|
213
|
+
return `${escapeText(finding.file)}:${finding.line}: ${escapeText(moved + tail)}`;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Report the timeouts `base..candidate` raises or adds to an existing file.
|
|
217
|
+
* Fails while any finding is unsanctioned, and fails closed: a revision that
|
|
218
|
+
* names no commit, or any git invocation that fails, is a `fail` record naming
|
|
219
|
+
* the cause - never a `pass`, and never a thrown error.
|
|
220
|
+
*/
|
|
221
|
+
export async function check(options) {
|
|
222
|
+
try {
|
|
223
|
+
const { cwd, base, candidate } = options;
|
|
224
|
+
const baseCommit = await resolveCommit(cwd, base);
|
|
225
|
+
const candidateCommit = await resolveCommit(cwd, candidate);
|
|
226
|
+
let ancestor;
|
|
227
|
+
try {
|
|
228
|
+
ancestor = (await runGit(cwd, ["merge-base", baseCommit, candidateCommit])).trim();
|
|
229
|
+
}
|
|
230
|
+
catch (error) {
|
|
231
|
+
throw new Error(`no merge base of ${quote(base)} and ${quote(candidate)}: ${error.message}`);
|
|
232
|
+
}
|
|
233
|
+
const drafts = draftsOf(await readChanges(cwd, ancestor, candidateCommit), TIMEOUT_SPELLINGS);
|
|
234
|
+
const lines = drafts.length === 0 ? [] : await readSanctionLines(cwd, baseCommit, candidateCommit);
|
|
235
|
+
const findings = drafts.map((draft) => {
|
|
236
|
+
const id = `${draft.category}:${draft.file}:${draft.line}`;
|
|
237
|
+
return { id, ...draft, sanction: sanctionFor(draft, id, lines, options.sanctions ?? {}) };
|
|
238
|
+
});
|
|
239
|
+
if (findings.length === 0)
|
|
240
|
+
return { name, status: "pass", evidence: ["no timeout raised or added"], findings };
|
|
241
|
+
const open = findings.some((finding) => finding.sanction === null);
|
|
242
|
+
return { name, status: open ? "fail" : "pass", evidence: findings.map(describeFinding), findings };
|
|
243
|
+
}
|
|
244
|
+
catch (error) {
|
|
245
|
+
return { name, status: "fail", evidence: [escapeText(error instanceof Error ? error.message : String(error))], findings: [] };
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
//# sourceMappingURL=raised-timeouts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"raised-timeouts.js","sourceRoot":"","sources":["../../src/checks/raised-timeouts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ladder commands changed.
|
|
3
|
+
*
|
|
4
|
+
* A repository's checks run through its manifest: `npm test` runs whatever the
|
|
5
|
+
* `test` entry of `scripts` says. This check reads every package.json the range
|
|
6
|
+
* adds, deletes, changes or renames, at the root or below it, whole at the
|
|
7
|
+
* merge base and at the candidate through git, and reports each entry of
|
|
8
|
+
* `scripts` the range adds (`script-added`), removes (`script-removed`) or
|
|
9
|
+
* changes (`script-changed`) for a human to sanction. Another field, the order
|
|
10
|
+
* of the keys and white space say nothing. A manifest that cannot be read as a
|
|
11
|
+
* JSON object at either end, or whose `scripts` is not an object of strings, is
|
|
12
|
+
* a failure that names it.
|
|
13
|
+
*
|
|
14
|
+
* A finding is sanctioned by a `Sanctioned-Weakening: <id> <reason>` line in a
|
|
15
|
+
* commit message of the range or by the caller's `sanctions`. Its id is
|
|
16
|
+
* `<category>:<file>:<key>`; `<category>:<file>` sanctions every finding of
|
|
17
|
+
* that category in that file.
|
|
18
|
+
*/
|
|
19
|
+
import type { CheckRecord } from "../index.js";
|
|
20
|
+
export declare const name = "RT-33";
|
|
21
|
+
export type ScriptCategory = "script-added" | "script-removed" | "script-changed";
|
|
22
|
+
export interface ScriptChangesOptions {
|
|
23
|
+
readonly cwd: string;
|
|
24
|
+
readonly base: string;
|
|
25
|
+
readonly candidate: string;
|
|
26
|
+
/** Ids (or `<category>:<file>`) mapped to reasons. */
|
|
27
|
+
readonly sanctions?: Readonly<Record<string, string>> | undefined;
|
|
28
|
+
}
|
|
29
|
+
/** One entry of `scripts` the range added, removed or changed. */
|
|
30
|
+
export interface ScriptFinding {
|
|
31
|
+
readonly id: string;
|
|
32
|
+
readonly category: ScriptCategory;
|
|
33
|
+
readonly file: string;
|
|
34
|
+
readonly key: string;
|
|
35
|
+
/** The command at the merge base; `null` when the entry is added. */
|
|
36
|
+
readonly before: string | null;
|
|
37
|
+
/** The command at the candidate; `null` when the entry is removed. */
|
|
38
|
+
readonly after: string | null;
|
|
39
|
+
/** The sanction's reason, or `null` while unsanctioned. */
|
|
40
|
+
readonly sanction: string | null;
|
|
41
|
+
}
|
|
42
|
+
export interface ScriptChangesRecord extends CheckRecord {
|
|
43
|
+
readonly findings: readonly ScriptFinding[];
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Report each `scripts` entry `base..candidate` adds, removes or changes.
|
|
47
|
+
* Fails while any finding is unsanctioned or a manifest cannot be read, and
|
|
48
|
+
* fails closed: a revision that names no commit, or any git invocation that
|
|
49
|
+
* fails, is a `fail` record naming the cause - never a `pass`, never a throw.
|
|
50
|
+
*/
|
|
51
|
+
export declare function check(options: ScriptChangesOptions): Promise<ScriptChangesRecord>;
|