@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,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Commit-range ASCII and identity hygiene.
|
|
3
|
+
*
|
|
4
|
+
* `commit-msg` hooks see one commit at a time, at the moment it is written.
|
|
5
|
+
* `cherry-pick`, `revert`, every form of `rebase`, and `git am` all create
|
|
6
|
+
* commits without ever invoking that hook - a property of git, not a defect
|
|
7
|
+
* of the hook. A rule enforced only at commit time is therefore enforced on
|
|
8
|
+
* a subset of history it cannot even name. This check instead reads the
|
|
9
|
+
* whole range `base..candidate` after the fact and answers for all of it.
|
|
10
|
+
*/
|
|
11
|
+
import { execFile } from "node:child_process";
|
|
12
|
+
/** The commit fields this check reads and validates, one line at a time. */
|
|
13
|
+
const FIELDS = [
|
|
14
|
+
{ key: "message", label: "commit message" },
|
|
15
|
+
{ key: "authorName", label: "author name" },
|
|
16
|
+
{ key: "authorEmail", label: "author e-mail" },
|
|
17
|
+
{ key: "committerName", label: "committer name" },
|
|
18
|
+
{ key: "committerEmail", label: "committer e-mail" },
|
|
19
|
+
];
|
|
20
|
+
function runGit(cwd, args) {
|
|
21
|
+
return new Promise((resolve, reject) => {
|
|
22
|
+
execFile("git", args, { cwd, maxBuffer: 64 * 1024 * 1024, encoding: "utf8" }, (error, stdout, stderr) => {
|
|
23
|
+
if (error) {
|
|
24
|
+
reject(new Error(stderr.trim() || error.message));
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
resolve({ stdout });
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Escape a character outside printable ASCII (plus tab) so evidence can be
|
|
33
|
+
* printed to a terminal without replaying whatever control sequence a
|
|
34
|
+
* malicious or malformed commit smuggled in.
|
|
35
|
+
*/
|
|
36
|
+
function escapeChar(char) {
|
|
37
|
+
const codePoint = char.codePointAt(0) ?? 0;
|
|
38
|
+
return `\\u{${codePoint.toString(16).padStart(4, "0")}}`;
|
|
39
|
+
}
|
|
40
|
+
function escapeLine(line) {
|
|
41
|
+
let escaped = "";
|
|
42
|
+
for (const char of line) {
|
|
43
|
+
const codePoint = char.codePointAt(0) ?? 0;
|
|
44
|
+
if (codePoint === 0x09 || (codePoint >= 0x20 && codePoint <= 0x7e)) {
|
|
45
|
+
escaped += char;
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
escaped += escapeChar(char);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return escaped;
|
|
52
|
+
}
|
|
53
|
+
function isAllowedCodePoint(codePoint) {
|
|
54
|
+
return codePoint === 0x09 || (codePoint >= 0x20 && codePoint <= 0x7e);
|
|
55
|
+
}
|
|
56
|
+
function lineIsAscii(line) {
|
|
57
|
+
for (const char of line) {
|
|
58
|
+
if (!isAllowedCodePoint(char.codePointAt(0) ?? 0)) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
function validateField(hash, label, content, evidence) {
|
|
65
|
+
for (const line of content.split("\n")) {
|
|
66
|
+
if (!lineIsAscii(line)) {
|
|
67
|
+
evidence.push(`${hash.slice(0, 12)}: ${label} contains a character outside printable ASCII/tab: "${escapeLine(line)}"`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
async function listCommits(cwd, base, candidate) {
|
|
72
|
+
const { stdout } = await runGit(cwd, ["rev-list", "--reverse", `${base}..${candidate}`, "--"]);
|
|
73
|
+
return stdout
|
|
74
|
+
.split("\n")
|
|
75
|
+
.map((line) => line.trim())
|
|
76
|
+
.filter((line) => line.length > 0);
|
|
77
|
+
}
|
|
78
|
+
async function readCommitField(cwd, hash, prettyFormat) {
|
|
79
|
+
const { stdout } = await runGit(cwd, ["show", "-s", `--format=${prettyFormat}`, hash]);
|
|
80
|
+
// `git show` always appends a trailing newline after a pretty-printed
|
|
81
|
+
// format; that newline is formatting, not content the commit wrote.
|
|
82
|
+
return stdout.replace(/\n$/, "");
|
|
83
|
+
}
|
|
84
|
+
async function readCommitFields(cwd, hash) {
|
|
85
|
+
// Read each field with its own `git show` call, one argument vector per
|
|
86
|
+
// invocation, rather than joining them with a delimiter: a delimiter drawn
|
|
87
|
+
// from the same alphabet this check polices could itself appear in a
|
|
88
|
+
// message or identity, and Node's `execFile` rejects NUL outright as an
|
|
89
|
+
// argument byte, so there is no safe separator to join on.
|
|
90
|
+
const [authorName, authorEmail, committerName, committerEmail, message] = await Promise.all([
|
|
91
|
+
readCommitField(cwd, hash, "%an"),
|
|
92
|
+
readCommitField(cwd, hash, "%ae"),
|
|
93
|
+
readCommitField(cwd, hash, "%cn"),
|
|
94
|
+
readCommitField(cwd, hash, "%ce"),
|
|
95
|
+
readCommitField(cwd, hash, "%B"),
|
|
96
|
+
]);
|
|
97
|
+
return { hash, authorName, authorEmail, committerName, committerEmail, message };
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Check every commit in `base..candidate` for characters outside printable
|
|
101
|
+
* ASCII plus tab, in the message and in both identities. Fails closed: any
|
|
102
|
+
* git invocation that cannot be completed (unknown revision, unreachable
|
|
103
|
+
* base, non-zero exit) is reported as `fail`, never as `pass`, and never as
|
|
104
|
+
* a thrown error the caller must catch to stay correct.
|
|
105
|
+
*/
|
|
106
|
+
export async function checkCommitRange(options) {
|
|
107
|
+
const { cwd, base, candidate } = options;
|
|
108
|
+
let hashes;
|
|
109
|
+
try {
|
|
110
|
+
hashes = await listCommits(cwd, base, candidate);
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
return {
|
|
114
|
+
name: "RT-01",
|
|
115
|
+
status: "fail",
|
|
116
|
+
evidence: [`could not compute range ${base}..${candidate}: ${error.message}`],
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
if (hashes.length === 0) {
|
|
120
|
+
return {
|
|
121
|
+
name: "RT-01",
|
|
122
|
+
status: "pass",
|
|
123
|
+
evidence: [`range ${base}..${candidate} is empty`],
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
const evidence = [];
|
|
127
|
+
for (const hash of hashes) {
|
|
128
|
+
let fields;
|
|
129
|
+
try {
|
|
130
|
+
fields = await readCommitFields(cwd, hash);
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
return {
|
|
134
|
+
name: "RT-01",
|
|
135
|
+
status: "fail",
|
|
136
|
+
evidence: [`could not read commit ${hash}: ${error.message}`],
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
for (const field of FIELDS) {
|
|
140
|
+
validateField(fields.hash, field.label, fields[field.key], evidence);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (evidence.length > 0) {
|
|
144
|
+
return { name: "RT-01", status: "fail", evidence };
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
name: "RT-01",
|
|
148
|
+
status: "pass",
|
|
149
|
+
evidence: [`${hashes.length} commit(s) in ${base}..${candidate} are printable ASCII/tab, identities included`],
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=commit-range.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commit-range.js","sourceRoot":"","sources":["../src/commit-range.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export declare const CONFIG_FILE = "repo-truth.json";
|
|
2
|
+
export declare const CONFIG_SCHEMA = "repo-truth.config/v1";
|
|
3
|
+
export type OptionKind = "integer" | "boolean" | "string" | "strings";
|
|
4
|
+
export declare const OPTION_KINDS: readonly OptionKind[];
|
|
5
|
+
/** Check name to option name to the kind of that option. */
|
|
6
|
+
export type OptionKinds = {
|
|
7
|
+
readonly [check: string]: {
|
|
8
|
+
readonly [option: string]: OptionKind;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
export type ConfigOptions = {
|
|
12
|
+
[check: string]: {
|
|
13
|
+
[option: string]: unknown;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
export declare class ConfigError extends Error {
|
|
17
|
+
readonly issues: string[];
|
|
18
|
+
constructor(issues: readonly string[], source?: string);
|
|
19
|
+
}
|
|
20
|
+
/** Reads `text` as a configuration document; throws a `ConfigError` naming every problem. */
|
|
21
|
+
export declare function parseConfig(text: string, kinds: OptionKinds): ConfigOptions;
|
|
22
|
+
/**
|
|
23
|
+
* With no `path`, the settings are repo-truth.json at the top directory of the
|
|
24
|
+
* base commit, whatever directory of the repository `cwd` is in; a
|
|
25
|
+
* subdirectory's repo-truth.json is never read. A `path` is read as given, a
|
|
26
|
+
* relative one against `cwd`.
|
|
27
|
+
*/
|
|
28
|
+
export declare function readConfig(options: {
|
|
29
|
+
cwd: string;
|
|
30
|
+
base: string;
|
|
31
|
+
path?: string;
|
|
32
|
+
}, kinds: OptionKinds): Promise<{
|
|
33
|
+
source: string | null;
|
|
34
|
+
options: ConfigOptions;
|
|
35
|
+
}>;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The configuration file: `repo-truth.json`, read from the base commit through
|
|
3
|
+
* git (never from the working tree or the candidate) or from a file the caller
|
|
4
|
+
* names, and checked against the options each check declares. Every refusal is
|
|
5
|
+
* a `ConfigError` holding one issue per problem.
|
|
6
|
+
*/
|
|
7
|
+
import { execFile } from "node:child_process";
|
|
8
|
+
import { readFile } from "node:fs/promises";
|
|
9
|
+
import { isAbsolute, resolve } from "node:path";
|
|
10
|
+
export const CONFIG_FILE = "repo-truth.json";
|
|
11
|
+
export const CONFIG_SCHEMA = "repo-truth.config/v1";
|
|
12
|
+
export const OPTION_KINDS = ["integer", "boolean", "string", "strings"];
|
|
13
|
+
/** Everything outside printable ASCII becomes `\u{hex}`, the form the checks already use. */
|
|
14
|
+
function escapeText(text) {
|
|
15
|
+
let escaped = "";
|
|
16
|
+
for (const char of text) {
|
|
17
|
+
const codePoint = char.codePointAt(0);
|
|
18
|
+
escaped += codePoint >= 0x20 && codePoint <= 0x7e ? char : `\\u{${codePoint.toString(16).padStart(4, "0")}}`;
|
|
19
|
+
}
|
|
20
|
+
return escaped;
|
|
21
|
+
}
|
|
22
|
+
export class ConfigError extends Error {
|
|
23
|
+
issues;
|
|
24
|
+
constructor(issues, source) {
|
|
25
|
+
const lines = issues.map((issue) => escapeText(issue));
|
|
26
|
+
const head = `${source === undefined ? "" : `${escapeText(source)}: `}${lines.length} configuration ${lines.length === 1 ? "issue" : "issues"}`;
|
|
27
|
+
super([head, ...lines.map((line) => ` ${line}`)].join("\n"));
|
|
28
|
+
this.name = "ConfigError";
|
|
29
|
+
this.issues = lines;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function isObject(value) {
|
|
33
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
34
|
+
}
|
|
35
|
+
function own(object, key) {
|
|
36
|
+
return Object.prototype.hasOwnProperty.call(object, key);
|
|
37
|
+
}
|
|
38
|
+
function describe(value) {
|
|
39
|
+
if (value === null)
|
|
40
|
+
return "null";
|
|
41
|
+
if (Array.isArray(value))
|
|
42
|
+
return "an array";
|
|
43
|
+
return `a ${typeof value}`;
|
|
44
|
+
}
|
|
45
|
+
function kindProblem(kind, value) {
|
|
46
|
+
switch (kind) {
|
|
47
|
+
case "integer":
|
|
48
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? null : "a non-negative safe integer";
|
|
49
|
+
case "boolean":
|
|
50
|
+
return typeof value === "boolean" ? null : "true or false";
|
|
51
|
+
case "string":
|
|
52
|
+
return typeof value === "string" ? null : "a string";
|
|
53
|
+
case "strings":
|
|
54
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string") ? null : "an array of strings";
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* `JSON.parse` keeps the last of two equal member names and says nothing, which
|
|
59
|
+
* is why the repeats are found here, over the tokens of a text already known to
|
|
60
|
+
* be valid JSON. Each repeat is one issue naming its path.
|
|
61
|
+
*/
|
|
62
|
+
function repeatedNames(text) {
|
|
63
|
+
const repeats = [];
|
|
64
|
+
const stack = [];
|
|
65
|
+
for (const [token] of text.matchAll(/"(?:[^"\\]|\\.)*"|[{}[\],:]|[^\s{}[\],:"]+/g)) {
|
|
66
|
+
const top = stack[stack.length - 1];
|
|
67
|
+
if (token === "{" || token === "[") {
|
|
68
|
+
let path = "";
|
|
69
|
+
if (top !== undefined)
|
|
70
|
+
path = top.array ? `${top.path}[${top.index}]` : top.member;
|
|
71
|
+
stack.push({ array: token === "[", path, names: new Set(), index: 0, wantsName: true, member: "" });
|
|
72
|
+
}
|
|
73
|
+
else if (token === "}" || token === "]") {
|
|
74
|
+
stack.pop();
|
|
75
|
+
}
|
|
76
|
+
else if (token === ",") {
|
|
77
|
+
if (top !== undefined) {
|
|
78
|
+
top.index += 1;
|
|
79
|
+
top.wantsName = true;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
else if (token === ":") {
|
|
83
|
+
if (top !== undefined)
|
|
84
|
+
top.wantsName = false;
|
|
85
|
+
}
|
|
86
|
+
else if (top !== undefined && !top.array && top.wantsName) {
|
|
87
|
+
const name = JSON.parse(token);
|
|
88
|
+
top.member = top.path === "" ? name : `${top.path}.${name}`;
|
|
89
|
+
if (top.names.has(name))
|
|
90
|
+
repeats.push(`${top.member}: given more than once`);
|
|
91
|
+
top.names.add(name);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return repeats;
|
|
95
|
+
}
|
|
96
|
+
/** Reads `text` as a configuration document; throws a `ConfigError` naming every problem. */
|
|
97
|
+
export function parseConfig(text, kinds) {
|
|
98
|
+
let document;
|
|
99
|
+
try {
|
|
100
|
+
document = JSON.parse(text);
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
throw new ConfigError([`the document: not valid JSON (${error instanceof Error ? error.message : String(error)})`]);
|
|
104
|
+
}
|
|
105
|
+
if (!isObject(document))
|
|
106
|
+
throw new ConfigError([`the document: expected a JSON object, found ${describe(document)}`]);
|
|
107
|
+
const issues = repeatedNames(text);
|
|
108
|
+
const options = {};
|
|
109
|
+
if (!own(document, "schema"))
|
|
110
|
+
issues.push(`schema: required, must be "${CONFIG_SCHEMA}"`);
|
|
111
|
+
for (const key of Object.keys(document)) {
|
|
112
|
+
const value = document[key];
|
|
113
|
+
if (key === "schema") {
|
|
114
|
+
if (value !== CONFIG_SCHEMA)
|
|
115
|
+
issues.push(`schema: expected "${CONFIG_SCHEMA}", found ${JSON.stringify(value)}`);
|
|
116
|
+
}
|
|
117
|
+
else if (key === "checks") {
|
|
118
|
+
if (!isObject(value)) {
|
|
119
|
+
issues.push(`checks: expected an object, found ${describe(value)}`);
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
for (const check of Object.keys(value)) {
|
|
123
|
+
const given = value[check];
|
|
124
|
+
if (!own(kinds, check)) {
|
|
125
|
+
issues.push(`checks.${check}: unknown check`);
|
|
126
|
+
}
|
|
127
|
+
else if (!isObject(given)) {
|
|
128
|
+
issues.push(`checks.${check}: expected an object, found ${describe(given)}`);
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
const declared = kinds[check];
|
|
132
|
+
const chosen = {};
|
|
133
|
+
for (const option of Object.keys(given)) {
|
|
134
|
+
const path = `checks.${check}.${option}`;
|
|
135
|
+
if (!own(declared, option)) {
|
|
136
|
+
issues.push(`${path}: unknown option of ${check}`);
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
const expected = kindProblem(declared[option], given[option]);
|
|
140
|
+
if (expected === null)
|
|
141
|
+
chosen[option] = given[option];
|
|
142
|
+
else
|
|
143
|
+
issues.push(`${path}: expected ${expected}, found ${JSON.stringify(given[option])}`);
|
|
144
|
+
}
|
|
145
|
+
options[check] = chosen;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
issues.push(`${key}: unknown key, only schema and checks are allowed`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (issues.length > 0)
|
|
154
|
+
throw new ConfigError(issues);
|
|
155
|
+
return options;
|
|
156
|
+
}
|
|
157
|
+
function git(cwd, args) {
|
|
158
|
+
return new Promise((done, fail) => {
|
|
159
|
+
execFile("git", args, { cwd, maxBuffer: 1024 * 1024 * 1024, encoding: "utf8" }, (error, stdout, stderr) => {
|
|
160
|
+
if (error === null)
|
|
161
|
+
return done({ code: 0, stdout, stderr });
|
|
162
|
+
const code = error.code;
|
|
163
|
+
if (typeof code === "number")
|
|
164
|
+
return done({ code, stdout, stderr });
|
|
165
|
+
fail(new ConfigError([`git could not be run: ${error.message}`]));
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
function failed(args, result) {
|
|
170
|
+
return new ConfigError([`git ${args[0]} failed (exit ${result.code}): ${result.stderr.trim() || "no message"}`]);
|
|
171
|
+
}
|
|
172
|
+
function parseFrom(text, kinds, source) {
|
|
173
|
+
try {
|
|
174
|
+
return parseConfig(text, kinds);
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
if (error instanceof ConfigError)
|
|
178
|
+
throw new ConfigError(error.issues, source);
|
|
179
|
+
throw error;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* With no `path`, the settings are repo-truth.json at the top directory of the
|
|
184
|
+
* base commit, whatever directory of the repository `cwd` is in; a
|
|
185
|
+
* subdirectory's repo-truth.json is never read. A `path` is read as given, a
|
|
186
|
+
* relative one against `cwd`.
|
|
187
|
+
*/
|
|
188
|
+
export async function readConfig(options, kinds) {
|
|
189
|
+
try {
|
|
190
|
+
const { cwd, base, path } = options;
|
|
191
|
+
if (path !== undefined) {
|
|
192
|
+
let text;
|
|
193
|
+
try {
|
|
194
|
+
text = await readFile(isAbsolute(path) ? path : resolve(cwd, path), "utf8");
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
throw new ConfigError([`${path}: cannot read the configuration file (${error instanceof Error ? error.message : String(error)})`]);
|
|
198
|
+
}
|
|
199
|
+
return { source: path, options: parseFrom(text, kinds, path) };
|
|
200
|
+
}
|
|
201
|
+
const revParse = ["rev-parse", "--verify", "--quiet", "--end-of-options", `${base}^{commit}`];
|
|
202
|
+
const resolved = await git(cwd, revParse);
|
|
203
|
+
if (resolved.code === 1 && resolved.stderr.trim() === "")
|
|
204
|
+
throw new ConfigError([`revision ${base}: names no commit`]);
|
|
205
|
+
if (resolved.code !== 0)
|
|
206
|
+
throw failed(revParse, resolved);
|
|
207
|
+
const commit = resolved.stdout.trim();
|
|
208
|
+
const lsTree = ["ls-tree", "-z", "--full-tree", commit, "--", CONFIG_FILE];
|
|
209
|
+
const listing = await git(cwd, lsTree);
|
|
210
|
+
if (listing.code !== 0)
|
|
211
|
+
throw failed(lsTree, listing);
|
|
212
|
+
const entry = listing.stdout.split("\0")[0];
|
|
213
|
+
if (entry === "")
|
|
214
|
+
return { source: null, options: {} };
|
|
215
|
+
const short = commit.slice(0, 12);
|
|
216
|
+
const match = /^(\d+) (\w+) ([0-9a-f]+)\t/.exec(entry);
|
|
217
|
+
if (match === null)
|
|
218
|
+
throw new ConfigError([`${CONFIG_FILE}: unreadable tree entry at ${short}`]);
|
|
219
|
+
if (match[1] !== "100644" && match[1] !== "100755") {
|
|
220
|
+
throw new ConfigError([`${CONFIG_FILE}: expected a regular file, found a ${match[2]} (mode ${match[1]}) at ${short}`]);
|
|
221
|
+
}
|
|
222
|
+
const catFile = ["cat-file", "blob", match[3]];
|
|
223
|
+
const blob = await git(cwd, catFile);
|
|
224
|
+
if (blob.code !== 0)
|
|
225
|
+
throw failed(catFile, blob);
|
|
226
|
+
const source = `${CONFIG_FILE} at ${short}`;
|
|
227
|
+
return { source, options: parseFrom(blob.stdout, kinds, source) };
|
|
228
|
+
}
|
|
229
|
+
catch (error) {
|
|
230
|
+
if (error instanceof ConfigError)
|
|
231
|
+
throw error;
|
|
232
|
+
throw new ConfigError([`unexpected failure: ${error instanceof Error ? error.message : String(error)}`]);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conflict markers.
|
|
3
|
+
*
|
|
4
|
+
* A merge, a rebase or a cherry-pick that stops on a conflict writes marker
|
|
5
|
+
* lines into the file, and whoever resolves it by editing around them can
|
|
6
|
+
* commit them. A compiler catches that in some files and not in others: a
|
|
7
|
+
* Markdown page, a YAML fixture or a JSON snapshot carries the markers
|
|
8
|
+
* silently. This check reads the lines `git diff base...candidate` shows as
|
|
9
|
+
* ADDED and reports every conflict marker among them with its file and line.
|
|
10
|
+
*
|
|
11
|
+
* Only added lines count. A marker the base already holds is not the range's
|
|
12
|
+
* doing, and a line the range removes is never read as one it adds, so the
|
|
13
|
+
* commit that deletes a leftover marker is not itself reported. The lines come
|
|
14
|
+
* from git's diff of the two commits, never from the working tree.
|
|
15
|
+
*
|
|
16
|
+
* A marker is a line that begins with exactly seven of one marker character
|
|
17
|
+
* followed by whitespace or the end of the line. `<` opens a conflict, `|`
|
|
18
|
+
* opens the common ancestor's part and `>` closes it; those three are always
|
|
19
|
+
* findings. `=` separates the two sides, and a line of seven equals signs is
|
|
20
|
+
* also what underlines a Markdown heading, so a separator is a finding only
|
|
21
|
+
* between an opening marker and the next closing marker among the added lines
|
|
22
|
+
* of the same file.
|
|
23
|
+
*/
|
|
24
|
+
import type { CheckRecord } from "./index.js";
|
|
25
|
+
/** Where to look, and which two revisions bound the range. */
|
|
26
|
+
export interface ConflictMarkersOptions {
|
|
27
|
+
/** The git repository to run in. */
|
|
28
|
+
readonly cwd: string;
|
|
29
|
+
/** The revision the range starts after. */
|
|
30
|
+
readonly base: string;
|
|
31
|
+
/** The revision the range ends at. */
|
|
32
|
+
readonly candidate: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Check the lines `base...candidate` adds for leftover conflict markers.
|
|
36
|
+
* Fails closed: a revision that names no commit, or any git invocation that
|
|
37
|
+
* fails, is a `fail` record naming the cause - never a `pass`, and never a
|
|
38
|
+
* thrown error.
|
|
39
|
+
*/
|
|
40
|
+
export declare function checkConflictMarkers(options: ConflictMarkersOptions): Promise<CheckRecord>;
|