pr-checkmate 3.3.0 → 4.0.1
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 +8 -3
- package/README.md +59 -4
- package/cspell.json +1 -0
- package/dist/cjs/agent/hook.d.ts +38 -0
- package/dist/cjs/agent/hook.js +175 -0
- package/dist/cjs/agent/registration.d.ts +10 -0
- package/dist/cjs/agent/registration.js +28 -0
- package/dist/cjs/agent/setup.d.ts +21 -0
- package/dist/cjs/agent/setup.js +229 -0
- package/dist/cjs/cli.js +88 -0
- package/dist/cjs/config/constants.d.ts +2 -0
- package/dist/cjs/config/constants.js +5 -1
- package/dist/cjs/config/pr-checkmate.template.json +10 -0
- package/dist/cjs/core/checks/findings.js +33 -7
- package/dist/cjs/core/checks/git/diff-smell.js +3 -1
- package/dist/cjs/core/checks/git/large-files.d.ts +1 -0
- package/dist/cjs/core/checks/git/large-files.js +3 -3
- package/dist/cjs/core/checks/languages/lint.js +27 -0
- package/dist/cjs/core/checks/languages/locate.d.ts +7 -0
- package/dist/cjs/core/checks/languages/locate.js +68 -0
- package/dist/cjs/core/checks/languages/prettier.js +15 -2
- package/dist/cjs/core/checks/languages/typecheck.js +4 -1
- package/dist/cjs/core/checks/pr/pr-size-check.d.ts +2 -0
- package/dist/cjs/core/checks/pr/pr-size-check.js +5 -5
- package/dist/cjs/core/checks/security/agent-instructions.d.ts +4 -0
- package/dist/cjs/core/checks/security/agent-instructions.js +149 -0
- package/dist/cjs/core/checks/security/diff-security.d.ts +7 -0
- package/dist/cjs/core/checks/security/diff-security.js +21 -1
- package/dist/cjs/core/checks/security/security.d.ts +0 -13
- package/dist/cjs/core/checks/security/security.js +27 -0
- package/dist/cjs/core/checks/security/workflow-security.js +33 -0
- package/dist/cjs/core/commands.d.ts +6 -0
- package/dist/cjs/core/commands.js +11 -1
- package/dist/cjs/core/finding-store.d.ts +16 -0
- package/dist/cjs/core/finding-store.js +73 -0
- package/dist/cjs/core/registry.js +2 -0
- package/dist/cjs/core/reporters.js +9 -0
- package/dist/cjs/core/run-checks.js +15 -5
- package/dist/cjs/core/sarif.d.ts +15 -0
- package/dist/cjs/core/sarif.js +60 -0
- package/dist/cjs/core/types.d.ts +7 -2
- package/dist/cjs/index.d.ts +2 -1
- package/dist/cjs/index.js +4 -1
- package/dist/cjs/init.js +11 -0
- package/dist/cjs/interfaces/index.d.ts +12 -0
- package/dist/cjs/mcp/config-tools.d.ts +30 -0
- package/dist/cjs/mcp/config-tools.js +320 -0
- package/dist/cjs/mcp/dependency.d.ts +34 -0
- package/dist/cjs/mcp/dependency.js +413 -0
- package/dist/cjs/mcp/protocol.d.ts +91 -0
- package/dist/cjs/mcp/protocol.js +181 -0
- package/dist/cjs/mcp/review.d.ts +64 -0
- package/dist/cjs/mcp/review.js +150 -0
- package/dist/cjs/mcp/serve.d.ts +8 -0
- package/dist/cjs/mcp/serve.js +49 -0
- package/dist/cjs/mcp/settings.d.ts +13 -0
- package/dist/cjs/mcp/settings.js +50 -0
- package/dist/cjs/mcp/tools.d.ts +23 -0
- package/dist/cjs/mcp/tools.js +618 -0
- package/dist/cjs/utils/execa.d.ts +7 -0
- package/dist/cjs/utils/execa.js +41 -1
- package/dist/cjs/utils/result-collector.d.ts +17 -1
- package/dist/cjs/utils/result-collector.js +7 -2
- package/dist/esm/agent/hook.d.ts +38 -0
- package/dist/esm/agent/hook.js +167 -0
- package/dist/esm/agent/registration.d.ts +10 -0
- package/dist/esm/agent/registration.js +21 -0
- package/dist/esm/agent/setup.d.ts +21 -0
- package/dist/esm/agent/setup.js +220 -0
- package/dist/esm/cli.js +86 -1
- package/dist/esm/config/constants.d.ts +2 -0
- package/dist/esm/config/constants.js +4 -0
- package/dist/esm/config/pr-checkmate.template.json +10 -0
- package/dist/esm/core/checks/findings.js +33 -7
- package/dist/esm/core/checks/git/diff-smell.js +3 -1
- package/dist/esm/core/checks/git/large-files.d.ts +1 -0
- package/dist/esm/core/checks/git/large-files.js +1 -1
- package/dist/esm/core/checks/languages/lint.js +28 -1
- package/dist/esm/core/checks/languages/locate.d.ts +7 -0
- package/dist/esm/core/checks/languages/locate.js +60 -0
- package/dist/esm/core/checks/languages/prettier.js +15 -2
- package/dist/esm/core/checks/languages/typecheck.js +4 -1
- package/dist/esm/core/checks/pr/pr-size-check.d.ts +2 -0
- package/dist/esm/core/checks/pr/pr-size-check.js +2 -2
- package/dist/esm/core/checks/security/agent-instructions.d.ts +4 -0
- package/dist/esm/core/checks/security/agent-instructions.js +146 -0
- package/dist/esm/core/checks/security/diff-security.d.ts +7 -0
- package/dist/esm/core/checks/security/diff-security.js +20 -0
- package/dist/esm/core/checks/security/security.d.ts +0 -13
- package/dist/esm/core/checks/security/security.js +27 -0
- package/dist/esm/core/checks/security/workflow-security.js +33 -0
- package/dist/esm/core/commands.d.ts +6 -0
- package/dist/esm/core/commands.js +10 -0
- package/dist/esm/core/finding-store.d.ts +16 -0
- package/dist/esm/core/finding-store.js +67 -0
- package/dist/esm/core/registry.js +2 -0
- package/dist/esm/core/reporters.js +9 -0
- package/dist/esm/core/run-checks.js +15 -5
- package/dist/esm/core/sarif.d.ts +15 -0
- package/dist/esm/core/sarif.js +57 -0
- package/dist/esm/core/types.d.ts +7 -2
- package/dist/esm/index.d.ts +2 -1
- package/dist/esm/index.js +2 -0
- package/dist/esm/init.js +12 -1
- package/dist/esm/interfaces/index.d.ts +12 -0
- package/dist/esm/mcp/config-tools.d.ts +30 -0
- package/dist/esm/mcp/config-tools.js +310 -0
- package/dist/esm/mcp/dependency.d.ts +34 -0
- package/dist/esm/mcp/dependency.js +408 -0
- package/dist/esm/mcp/protocol.d.ts +91 -0
- package/dist/esm/mcp/protocol.js +175 -0
- package/dist/esm/mcp/review.d.ts +64 -0
- package/dist/esm/mcp/review.js +139 -0
- package/dist/esm/mcp/serve.d.ts +8 -0
- package/dist/esm/mcp/serve.js +43 -0
- package/dist/esm/mcp/settings.d.ts +13 -0
- package/dist/esm/mcp/settings.js +43 -0
- package/dist/esm/mcp/tools.d.ts +23 -0
- package/dist/esm/mcp/tools.js +611 -0
- package/dist/esm/utils/execa.d.ts +7 -0
- package/dist/esm/utils/execa.js +40 -1
- package/dist/esm/utils/result-collector.d.ts +17 -1
- package/dist/esm/utils/result-collector.js +7 -2
- package/package.json +7 -3
- package/pr-checkmate.schema.json +35 -0
- package/src/config/cspell.json +1 -0
- package/src/config/pr-checkmate.schema.json +35 -0
package/dist/cjs/cli.js
CHANGED
|
@@ -1,13 +1,25 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
3
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const hook_js_1 = require("./agent/hook.js");
|
|
10
|
+
const setup_js_1 = require("./agent/setup.js");
|
|
4
11
|
const loader_js_1 = require("./config/loader.js");
|
|
5
12
|
const commands_js_1 = require("./core/commands.js");
|
|
6
13
|
const index_js_1 = require("./core/index.js");
|
|
7
14
|
const doctor_js_1 = require("./core/install/doctor.js");
|
|
8
15
|
const grype_db_js_1 = require("./core/install/grype-db.js");
|
|
16
|
+
const sarif_js_1 = require("./core/sarif.js");
|
|
9
17
|
const init_js_1 = require("./init.js");
|
|
18
|
+
const serve_js_1 = require("./mcp/serve.js");
|
|
19
|
+
const tools_js_1 = require("./mcp/tools.js");
|
|
20
|
+
const execa_js_1 = require("./utils/execa.js");
|
|
10
21
|
const index_js_2 = require("./utils/index.js");
|
|
22
|
+
const package_version_js_1 = require("./utils/package-version.js");
|
|
11
23
|
const argv = process.argv.slice(2);
|
|
12
24
|
/**
|
|
13
25
|
* Flags are separated from positional arguments so they compose with every command
|
|
@@ -54,6 +66,28 @@ function rejectConflictingScope() {
|
|
|
54
66
|
index_js_2.logger.error('[cli]: "--all" applies only to "install". Did you mean "--full"?');
|
|
55
67
|
process.exit(1);
|
|
56
68
|
}
|
|
69
|
+
// `mcp` speaks a protocol on stdout and `hook` is invoked by a harness with a
|
|
70
|
+
// fixed payload: neither has anything to configure from the command line, and a
|
|
71
|
+
// flag that quietly did nothing would look like it had.
|
|
72
|
+
if ((cmd === 'mcp' || cmd === 'hook') && flags.length > 0) {
|
|
73
|
+
index_js_2.logger.error(`[cli]: "${cmd}" takes no flags, got: ${flags.join(', ')}.`);
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
// Only a command that runs checks has findings to export; `all`, `precommit` and a
|
|
77
|
+
// named check do, every other standalone command does not.
|
|
78
|
+
const runsChecks = !cmd || cmd === 'all' || cmd === 'precommit' || !commands_js_1.STANDALONE_COMMANDS.includes(cmd);
|
|
79
|
+
if (flags.includes('--sarif') && !runsChecks) {
|
|
80
|
+
index_js_2.logger.error('[cli]: "--sarif" applies only to commands that run checks.');
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
if (flags.includes('--no-hook') && cmd !== 'agent-setup') {
|
|
84
|
+
index_js_2.logger.error('[cli]: "--no-hook" applies only to "agent-setup".');
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
if (flags.includes('--full') && commands_js_1.AGENT_COMMANDS.includes(cmd ?? '')) {
|
|
88
|
+
index_js_2.logger.error(`[cli]: "${cmd}" reviews nothing on its own, so "--full" has no meaning.`);
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
57
91
|
if (flags.includes('--full') && commands_js_1.TOOLING_COMMANDS.includes(cmd ?? '')) {
|
|
58
92
|
index_js_2.logger.error(`[cli]: "${cmd}" manages tooling and reviews nothing, so "--full" has no meaning.`);
|
|
59
93
|
process.exit(1);
|
|
@@ -75,6 +109,55 @@ function rejectExtraArgs() {
|
|
|
75
109
|
index_js_2.logger.error(`[cli]: "${cmd}" takes no other arguments, got: ${args.slice(1).join(', ')}. Run them as a separate command.`);
|
|
76
110
|
process.exit(1);
|
|
77
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* Write the report as SARIF when `--sarif` was passed. Written before the exit code
|
|
114
|
+
* is decided, so a failing run still leaves the file a dashboard needs to show why.
|
|
115
|
+
*/
|
|
116
|
+
function writeSarifIfRequested(report) {
|
|
117
|
+
if (!flags.includes('--sarif'))
|
|
118
|
+
return;
|
|
119
|
+
const file = node_path_1.default.resolve(process.cwd(), 'pr-checkmate.sarif');
|
|
120
|
+
node_fs_1.default.writeFileSync(file, `${JSON.stringify((0, sarif_js_1.toSarif)(report, (0, package_version_js_1.packageVersion)()), null, 2)}\n`);
|
|
121
|
+
index_js_2.logger.info(`[sarif]: wrote ${file}`);
|
|
122
|
+
}
|
|
123
|
+
/** Everything the harness wrote to stdin, as text; empty when stdin is closed or a terminal. */
|
|
124
|
+
async function readStdin() {
|
|
125
|
+
if (process.stdin.isTTY)
|
|
126
|
+
return '';
|
|
127
|
+
const chunks = [];
|
|
128
|
+
for await (const chunk of process.stdin)
|
|
129
|
+
chunks.push(Buffer.from(chunk));
|
|
130
|
+
return Buffer.concat(chunks).toString('utf-8');
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* The commands that serve AI agents. Returns whether `cmd` was one of them (and has
|
|
134
|
+
* therefore already exited); kept out of the main dispatcher so that one stays
|
|
135
|
+
* readable as the list of commands grows.
|
|
136
|
+
*
|
|
137
|
+
* `mcp` must stay ahead of anything that could print: from here until stdin closes,
|
|
138
|
+
* stdout carries protocol messages and nothing else.
|
|
139
|
+
*/
|
|
140
|
+
async function runAgentCommand() {
|
|
141
|
+
if (cmd === 'mcp') {
|
|
142
|
+
await (0, serve_js_1.serve)((0, tools_js_1.createServerDefinition)());
|
|
143
|
+
process.exit(0);
|
|
144
|
+
}
|
|
145
|
+
if (cmd === 'hook') {
|
|
146
|
+
// Discarded rather than shown: the harness reads stdout and stderr of a hook
|
|
147
|
+
// as instructions to the model, so only the verdict may reach either.
|
|
148
|
+
(0, execa_js_1.isolateInheritedStdio)('discard');
|
|
149
|
+
index_js_2.logger.setSink({ out: () => undefined, err: () => undefined });
|
|
150
|
+
const verdict = await (0, hook_js_1.evaluateHook)(await readStdin(), process.cwd());
|
|
151
|
+
if (verdict.stderr)
|
|
152
|
+
process.stderr.write(`${verdict.stderr}\n`);
|
|
153
|
+
process.exit(verdict.exitCode);
|
|
154
|
+
}
|
|
155
|
+
if (cmd === 'agent-setup') {
|
|
156
|
+
(0, setup_js_1.reportSetup)((0, setup_js_1.setupAgent)(process.cwd(), { hook: !flags.includes('--no-hook') }));
|
|
157
|
+
process.exit(0);
|
|
158
|
+
}
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
78
161
|
void (async () => {
|
|
79
162
|
try {
|
|
80
163
|
rejectUnknownFlags();
|
|
@@ -84,6 +167,8 @@ void (async () => {
|
|
|
84
167
|
await (0, init_js_1.init)();
|
|
85
168
|
process.exit(0);
|
|
86
169
|
}
|
|
170
|
+
if (await runAgentCommand())
|
|
171
|
+
return;
|
|
87
172
|
// Tooling management. These run no checks — they prepare, inspect or clean
|
|
88
173
|
// up what the checks need, so they never touch the diff or the reporter.
|
|
89
174
|
if (cmd === 'install') {
|
|
@@ -125,12 +210,14 @@ void (async () => {
|
|
|
125
210
|
index_js_2.logger.info('');
|
|
126
211
|
notice.forEach(line => index_js_2.logger.info(line));
|
|
127
212
|
}
|
|
213
|
+
writeSarifIfRequested(report);
|
|
128
214
|
process.exit(report.ok ? 0 : 1);
|
|
129
215
|
}
|
|
130
216
|
// Local pre-commit: check the staged changes (index vs HEAD), print to
|
|
131
217
|
// stdout, never modify files. Exits non-zero on failure to block the commit.
|
|
132
218
|
if (cmd === 'precommit') {
|
|
133
219
|
const report = await (0, index_js_1.runChecks)({ staged: true, reporter: 'stdout', write: false });
|
|
220
|
+
writeSarifIfRequested(report);
|
|
134
221
|
process.exit(report.ok ? 0 : 1);
|
|
135
222
|
}
|
|
136
223
|
// Every remaining argument is a check name, so `pr-checkmate lint spellcheck`
|
|
@@ -143,6 +230,7 @@ void (async () => {
|
|
|
143
230
|
}
|
|
144
231
|
const checkNames = [...new Set(args.map(arg => commands_js_1.COMMAND_TO_CHECK[arg]))];
|
|
145
232
|
const report = await (0, index_js_1.runChecks)({ only: checkNames, reporter: 'stdout', write: true, full });
|
|
233
|
+
writeSarifIfRequested(report);
|
|
146
234
|
process.exit(report.ok ? 0 : 1);
|
|
147
235
|
}
|
|
148
236
|
catch (err) {
|
|
@@ -95,6 +95,7 @@ export declare const SOURCE_EXTENSIONS: {
|
|
|
95
95
|
json: string[];
|
|
96
96
|
all: string[];
|
|
97
97
|
};
|
|
98
|
+
export declare const AGENT_SETUP_COMMAND = "npx pr-checkmate agent-setup";
|
|
98
99
|
export declare const DEFAULT_COMMANDS: {
|
|
99
100
|
'npx pr-checkmate init': string;
|
|
100
101
|
'npx pr-checkmate all': string;
|
|
@@ -109,6 +110,7 @@ export declare const DEFAULT_COMMANDS: {
|
|
|
109
110
|
'npx pr-checkmate diff-smell': string;
|
|
110
111
|
'npx pr-checkmate pr-body': string;
|
|
111
112
|
'npx pr-checkmate duplicate': string;
|
|
113
|
+
"npx pr-checkmate agent-setup": string;
|
|
112
114
|
};
|
|
113
115
|
/**
|
|
114
116
|
* Packages allow-listed for the license check by default.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.KNOWN_CONFIG_KEYS = exports.documentation = exports.FULL_CONFIG_URL = exports.DOCS_URL = exports.DEFAULT_ALLOW_PACKAGES = exports.DEFAULT_COMMANDS = exports.SOURCE_EXTENSIONS = exports.DUPLICATE_CODE_FORMATS = exports.CONFIG_FILE_PATTERNS = exports.DEAD_CODE_IGNORE_DEFAULTS = exports.GENERATED_DOC_FILES = exports.DIFF_SECURITY_IGNORE_DEFAULTS = exports.DUPLICATE_IGNORE_DEFAULTS = exports.TEST_PATH_GLOBS = exports.IGNORE_PATTERNS = exports.IGNORED_DIRS = void 0;
|
|
3
|
+
exports.KNOWN_CONFIG_KEYS = exports.documentation = exports.FULL_CONFIG_URL = exports.DOCS_URL = exports.DEFAULT_ALLOW_PACKAGES = exports.DEFAULT_COMMANDS = exports.AGENT_SETUP_COMMAND = exports.SOURCE_EXTENSIONS = exports.DUPLICATE_CODE_FORMATS = exports.CONFIG_FILE_PATTERNS = exports.DEAD_CODE_IGNORE_DEFAULTS = exports.GENERATED_DOC_FILES = exports.DIFF_SECURITY_IGNORE_DEFAULTS = exports.DUPLICATE_IGNORE_DEFAULTS = exports.TEST_PATH_GLOBS = exports.IGNORE_PATTERNS = exports.IGNORED_DIRS = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Build output, dependency, tooling, and report directories that are never
|
|
6
6
|
* product code. Every check excludes them by directory-name segment match (see
|
|
@@ -228,6 +228,7 @@ exports.SOURCE_EXTENSIONS = {
|
|
|
228
228
|
json: ['.json'],
|
|
229
229
|
all: ['.ts', '.tsx', '.js', '.jsx', '.json', '.md', '.mdx'],
|
|
230
230
|
};
|
|
231
|
+
exports.AGENT_SETUP_COMMAND = 'npx pr-checkmate agent-setup';
|
|
231
232
|
exports.DEFAULT_COMMANDS = {
|
|
232
233
|
'npx pr-checkmate init': 'Create pr-checkmate.json and the CI workflow (first-run setup)',
|
|
233
234
|
'npx pr-checkmate all': 'Run all checks over the changes — the PR diff in CI, your branch changes locally',
|
|
@@ -242,6 +243,7 @@ exports.DEFAULT_COMMANDS = {
|
|
|
242
243
|
'npx pr-checkmate diff-smell': 'Scan git diff for code smells',
|
|
243
244
|
'npx pr-checkmate pr-body': 'Validate PR description',
|
|
244
245
|
'npx pr-checkmate duplicate': 'Detect duplicate code blocks (jscpd)',
|
|
246
|
+
[exports.AGENT_SETUP_COMMAND]: 'Connect your AI agent: MCP server, rules for it and edit hooks (writes .mcp.json)',
|
|
245
247
|
};
|
|
246
248
|
/**
|
|
247
249
|
* Packages allow-listed for the license check by default.
|
|
@@ -323,6 +325,8 @@ exports.KNOWN_CONFIG_KEYS = new Set([
|
|
|
323
325
|
'diffSecurity',
|
|
324
326
|
'workflowSecurity',
|
|
325
327
|
'dockerfileSecurity',
|
|
328
|
+
'agentInstructions',
|
|
329
|
+
'mcp',
|
|
326
330
|
'sensitiveFileGuard',
|
|
327
331
|
'migrationSafety',
|
|
328
332
|
// dependencies
|
|
@@ -97,6 +97,16 @@
|
|
|
97
97
|
"enabled": true,
|
|
98
98
|
"ignore": []
|
|
99
99
|
},
|
|
100
|
+
"agentInstructions": {
|
|
101
|
+
"enabled": true,
|
|
102
|
+
"ignore": []
|
|
103
|
+
},
|
|
104
|
+
"//mcp": "What the MCP server offers an AI agent here. 'enabled': false turns it off; 'disabledTools' hides tools by name; 'readOnly' hides the ones that write files. Applies from the agent's next request.",
|
|
105
|
+
"mcp": {
|
|
106
|
+
"enabled": true,
|
|
107
|
+
"disabledTools": [],
|
|
108
|
+
"readOnly": false
|
|
109
|
+
},
|
|
100
110
|
"//sensitiveFileGuard": "Flags .env files, private keys, CI config and lockfiles. 'ignorePaths' excuses ONE file by glob (a .pem test fixture); 'ignore' silences a whole CATEGORY and would excuse the next real key too — reach for ignorePaths first.",
|
|
101
111
|
"sensitiveFileGuard": {
|
|
102
112
|
"enabled": true,
|
|
@@ -23,6 +23,7 @@ const node_fs_1 = __importDefault(require("node:fs"));
|
|
|
23
23
|
const node_path_1 = __importDefault(require("node:path"));
|
|
24
24
|
const execa_js_1 = require("../../utils/execa.js");
|
|
25
25
|
const index_js_1 = require("../../utils/index.js");
|
|
26
|
+
const finding_store_js_1 = require("../finding-store.js");
|
|
26
27
|
const targets_js_1 = require("../targets.js");
|
|
27
28
|
const types_js_1 = require("../types.js");
|
|
28
29
|
/**
|
|
@@ -391,6 +392,8 @@ exports.DEFAULT_DATAFLOW_WINDOW = 10;
|
|
|
391
392
|
const CROSS_LINE_SQL_LABEL = 'Interpolated value reaches a query (SQL injection risk, cross-line)';
|
|
392
393
|
/** `@@ ... @@` — a new hunk, and the boundary taint never crosses. */
|
|
393
394
|
const DIFF_HUNK_HEADER = /^@@ /;
|
|
395
|
+
/** The first line of the new side of a hunk: `@@ -3,2 +10,4 @@` → 10. */
|
|
396
|
+
const HUNK_NEW_START = /^@@ -\d+(?:,\d+)? \+(\d+)/;
|
|
394
397
|
/**
|
|
395
398
|
* Whether this line hands `name` to a query sink.
|
|
396
399
|
*
|
|
@@ -464,6 +467,9 @@ function scanDiffPatterns(stdout, patterns, opts = {}) {
|
|
|
464
467
|
const mode = opts.comments ?? 'language';
|
|
465
468
|
const ignorePaths = opts.ignorePaths ?? [];
|
|
466
469
|
let syntax = 'both';
|
|
470
|
+
// Where the walk is, for findings that need to say so.
|
|
471
|
+
let file = '';
|
|
472
|
+
let newLine = 0;
|
|
467
473
|
// Block comments span lines, so the state belongs to the walk, not to one line.
|
|
468
474
|
let inBlockComment = false;
|
|
469
475
|
// Whole files can be excluded by glob; the flag persists until the next header.
|
|
@@ -474,6 +480,7 @@ function scanDiffPatterns(stdout, patterns, opts = {}) {
|
|
|
474
480
|
for (const line of stdout.split('\n')) {
|
|
475
481
|
const header = DIFF_FILE_HEADER.exec(line);
|
|
476
482
|
if (header) {
|
|
483
|
+
file = header[1];
|
|
477
484
|
syntax = mode === 'language' ? commentSyntaxFor(header[1]) : 'both';
|
|
478
485
|
inBlockComment = false;
|
|
479
486
|
skippingFile = ignorePaths.length > 0 && (0, index_js_1.matchesAnyGlob)(header[1], ignorePaths);
|
|
@@ -482,9 +489,15 @@ function scanDiffPatterns(stdout, patterns, opts = {}) {
|
|
|
482
489
|
}
|
|
483
490
|
// Checked before the `+` filter below, which a hunk header does not satisfy.
|
|
484
491
|
if (DIFF_HUNK_HEADER.test(line)) {
|
|
492
|
+
newLine = Number(HUNK_NEW_START.exec(line)?.[1] ?? 0);
|
|
485
493
|
taint.reset();
|
|
486
494
|
continue;
|
|
487
495
|
}
|
|
496
|
+
// Counted before any skip: an ignored or excluded line still occupies a line
|
|
497
|
+
// number in the file, and the ones after it must not shift.
|
|
498
|
+
const lineNo = newLine;
|
|
499
|
+
if (line.startsWith('+'))
|
|
500
|
+
newLine++;
|
|
488
501
|
if (skippingFile)
|
|
489
502
|
continue;
|
|
490
503
|
// Only added lines carry block-comment state: a `-` line is code being
|
|
@@ -496,18 +509,28 @@ function scanDiffPatterns(stdout, patterns, opts = {}) {
|
|
|
496
509
|
const prepared = uncomment(line, { mode, syntax, inBlockComment });
|
|
497
510
|
inBlockComment = prepared.inBlockComment;
|
|
498
511
|
const asCode = stripStringLiterals(prepared.text);
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
const bucket = severity === 'error' ? errors : warnings;
|
|
502
|
-
(bucket[label] ?? (bucket[label] = [])).push(line.slice(1).trim().substring(0, SAMPLE_MAX_LENGTH));
|
|
503
|
-
}
|
|
504
|
-
}
|
|
512
|
+
const where = { file, line: lineNo };
|
|
513
|
+
recordMatches(patterns, { text: prepared.text, asCode }, line, where, { errors, warnings });
|
|
505
514
|
if (taint.inspect(prepared.text, asCode)) {
|
|
506
|
-
(warnings
|
|
515
|
+
record(warnings, CROSS_LINE_SQL_LABEL, line, where);
|
|
507
516
|
}
|
|
508
517
|
}
|
|
509
518
|
return { errors, warnings };
|
|
510
519
|
}
|
|
520
|
+
/** File one added line under `label` and remember where it was, for findings that say so. */
|
|
521
|
+
function record(bucket, label, line, where) {
|
|
522
|
+
const sample = line.slice(1).trim().substring(0, SAMPLE_MAX_LENGTH);
|
|
523
|
+
(bucket[label] ?? (bucket[label] = [])).push(sample);
|
|
524
|
+
(0, finding_store_js_1.noteLocation)(label, sample, where.file, where.line);
|
|
525
|
+
}
|
|
526
|
+
/** File every pattern that matches one prepared line under its own label and severity. */
|
|
527
|
+
function recordMatches(patterns, view, line, where, into) {
|
|
528
|
+
for (const { pattern, label, severity, view: wants } of patterns) {
|
|
529
|
+
if (pattern.test(wants === 'code' ? view.asCode : view.text)) {
|
|
530
|
+
record(severity === 'error' ? into.errors : into.warnings, label, line, where);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
511
534
|
/**
|
|
512
535
|
* Reduce one added line to what should be matched against: block comments
|
|
513
536
|
* blanked (carrying the open state on), then the trailing line comment removed.
|
|
@@ -598,6 +621,7 @@ function bucketTotal(bucket) {
|
|
|
598
621
|
*/
|
|
599
622
|
function reportCountedFindings(findings, ctx, opts) {
|
|
600
623
|
const sev = severityEmitter(opts.severity, opts.fallback ?? 'warn');
|
|
624
|
+
(0, finding_store_js_1.fileFindings)(findings, sev.icon === '❌' ? 'error' : 'warn');
|
|
601
625
|
logFindings(findings, {
|
|
602
626
|
prefix: opts.prefix,
|
|
603
627
|
sampleLimit: opts.sampleLimit ?? ctx.config.log?.sampleLimit ?? 3,
|
|
@@ -702,6 +726,8 @@ function reportSeverityBuckets(errorsIn, warningsIn, opts) {
|
|
|
702
726
|
const ignore = opts.ignore ?? [];
|
|
703
727
|
const filed = applyOverrides(dropIgnored(errorsIn, ignore), dropIgnored(warningsIn, ignore), opts.severityOverrides ?? {});
|
|
704
728
|
const { errors, warnings } = filed;
|
|
729
|
+
(0, finding_store_js_1.fileFindings)(errors, 'error');
|
|
730
|
+
(0, finding_store_js_1.fileFindings)(warnings, 'warn');
|
|
705
731
|
const totalErrors = bucketTotal(errors);
|
|
706
732
|
const totalWarnings = bucketTotal(warnings);
|
|
707
733
|
if (totalErrors === 0 && totalWarnings === 0)
|
|
@@ -22,7 +22,9 @@ exports.CODE_SMELLS = [
|
|
|
22
22
|
view: 'code',
|
|
23
23
|
},
|
|
24
24
|
{
|
|
25
|
-
|
|
25
|
+
// A statement, not a mention: `debugger` ends the line or follows a `;`, `{`, `}` or `)`.
|
|
26
|
+
// A regex literal (`/debugger/`) or an object key (`{ debugger: 1 }`) is not one.
|
|
27
|
+
pattern: /^\+(?!\+)(?:.*[;{})]|\s*)\s*debugger\b\s*;?\s*$/,
|
|
26
28
|
label: 'debugger statement',
|
|
27
29
|
severity: 'warn',
|
|
28
30
|
view: 'code',
|
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.largeFilesCheck = void 0;
|
|
6
|
+
exports.largeFilesCheck = exports.DEFAULT_MAX_KB = void 0;
|
|
7
7
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
8
|
const node_path_1 = __importDefault(require("node:path"));
|
|
9
9
|
const execa_js_1 = require("../../../utils/execa.js");
|
|
@@ -11,7 +11,7 @@ const index_js_1 = require("../../../utils/index.js");
|
|
|
11
11
|
const targets_js_1 = require("../../targets.js");
|
|
12
12
|
const types_js_1 = require("../../types.js");
|
|
13
13
|
const findings_js_1 = require("../findings.js");
|
|
14
|
-
|
|
14
|
+
exports.DEFAULT_MAX_KB = 500;
|
|
15
15
|
/**
|
|
16
16
|
* Lockfiles are legitimately large and always generated, never accidental — so
|
|
17
17
|
* they're exempt from the size/binary guard by default (a 600 KB
|
|
@@ -70,7 +70,7 @@ exports.largeFilesCheck = {
|
|
|
70
70
|
return (0, types_js_1.skip)('git unavailable');
|
|
71
71
|
if (files.length === 0)
|
|
72
72
|
return (0, types_js_1.pass)();
|
|
73
|
-
const maxKb = ctx.config.largeFiles?.maxSizeKb ?? DEFAULT_MAX_KB;
|
|
73
|
+
const maxKb = ctx.config.largeFiles?.maxSizeKb ?? exports.DEFAULT_MAX_KB;
|
|
74
74
|
const maxBytes = maxKb * 1024;
|
|
75
75
|
const allow = [...LOCKFILE_ALLOW, ...(ctx.config.largeFiles?.allow ?? [])];
|
|
76
76
|
if (!ctx.baseSha && ctx.config.run?.fullScanBehaviour === 'skip')
|
|
@@ -4,13 +4,18 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.lintCheck = void 0;
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
7
9
|
const node_path_1 = __importDefault(require("node:path"));
|
|
8
10
|
const index_js_1 = require("../../../config/index.js");
|
|
9
11
|
const execa_js_1 = require("../../../utils/execa.js");
|
|
10
12
|
const index_js_2 = require("../../../utils/index.js");
|
|
13
|
+
const finding_store_js_1 = require("../../finding-store.js");
|
|
11
14
|
const reuse_js_1 = require("../../install/reuse.js");
|
|
12
15
|
const targets_js_1 = require("../../targets.js");
|
|
13
16
|
const types_js_1 = require("../../types.js");
|
|
17
|
+
const locate_js_1 = require("./locate.js");
|
|
18
|
+
const MAX_LOGGED_PROBLEMS = 50;
|
|
14
19
|
/**
|
|
15
20
|
* Resolve the ESLint binary under the client's `install.reuseLocal` policy.
|
|
16
21
|
*
|
|
@@ -36,6 +41,17 @@ async function getESLintMajorVersion(eslintBin) {
|
|
|
36
41
|
return 8;
|
|
37
42
|
}
|
|
38
43
|
}
|
|
44
|
+
function readProblems(reportFile, cwd) {
|
|
45
|
+
try {
|
|
46
|
+
return (0, locate_js_1.parseEslintJson)(node_fs_1.default.readFileSync(reportFile, 'utf-8'), cwd);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
finally {
|
|
52
|
+
(0, index_js_2.cleanupTempFile)(reportFile);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
39
55
|
exports.lintCheck = {
|
|
40
56
|
name: 'ESLint',
|
|
41
57
|
phase: 'blocking',
|
|
@@ -87,12 +103,23 @@ exports.lintCheck = {
|
|
|
87
103
|
if (!hasLocal && version < 9 && toolRoots.length > 0) {
|
|
88
104
|
args.push('--resolve-plugins-relative-to', toolRoots[0]);
|
|
89
105
|
}
|
|
106
|
+
// JSON to a file, not stylish text to the terminal: the file is what lets a
|
|
107
|
+
// finding name its file, line and rule instead of saying "lint errors found".
|
|
108
|
+
// The same problems are logged below in a compact form, so nothing is lost.
|
|
109
|
+
const reportFile = node_path_1.default.join(node_os_1.default.tmpdir(), `pr-checkmate-eslint-${process.pid}-${Date.now()}.json`);
|
|
110
|
+
args.push('--format', 'json', '--output-file', reportFile);
|
|
90
111
|
const result = await (0, execa_js_1.execa)(process.execPath, [eslintBin, ...args], {
|
|
91
112
|
cwd: ctx.cwd,
|
|
92
113
|
stdio: 'inherit',
|
|
93
114
|
reject: false,
|
|
94
115
|
env: { PR_CHECKMATE_TOOL_ROOTS: toolRoots.join(node_path_1.default.delimiter) },
|
|
95
116
|
});
|
|
117
|
+
const problems = readProblems(reportFile, ctx.cwd);
|
|
118
|
+
problems.slice(0, MAX_LOGGED_PROBLEMS).forEach(f => {
|
|
119
|
+
index_js_2.logger.warn(`[ESLint]: ${f.file}:${f.line ?? 0} ${f.rule} ${f.snippet ?? ''}`);
|
|
120
|
+
});
|
|
121
|
+
if (result.exitCode === 1)
|
|
122
|
+
problems.forEach(finding_store_js_1.addFinding);
|
|
96
123
|
if (result.exitCode === 0)
|
|
97
124
|
return (0, types_js_1.pass)();
|
|
98
125
|
// ESLint exits 1 for lint errors and 2 for a fatal error — a config it cannot
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Finding } from '../../types.js';
|
|
2
|
+
/** `src/a.ts(3,5): error TS2322: Type …` lines of `tsc --pretty false`. */
|
|
3
|
+
export declare function parseTscDiagnostics(output: string): Finding[];
|
|
4
|
+
/** The array `eslint --format json` writes. Anything else yields no findings. */
|
|
5
|
+
export declare function parseEslintJson(raw: string, cwd: string): Finding[];
|
|
6
|
+
/** One path per line, as `prettier --list-different` prints them. */
|
|
7
|
+
export declare function parseListDifferent(stdout: string): Finding[];
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.parseTscDiagnostics = parseTscDiagnostics;
|
|
7
|
+
exports.parseEslintJson = parseEslintJson;
|
|
8
|
+
exports.parseListDifferent = parseListDifferent;
|
|
9
|
+
/**
|
|
10
|
+
* Turn what the language tools print into located findings.
|
|
11
|
+
*
|
|
12
|
+
* The three tools disagree on how to say where a problem is, so each gets a small
|
|
13
|
+
* pure parser here and the checks only decide what to do with the result. Without
|
|
14
|
+
* these an agent was told "lint errors found" and nothing else.
|
|
15
|
+
*/
|
|
16
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
17
|
+
const MAX_SNIPPET = 160;
|
|
18
|
+
const TSC_DIAGNOSTIC = /^(.+?)\((\d+),\d+\): error (TS\d+): (.*)$/;
|
|
19
|
+
const clip = (text) => text.length > MAX_SNIPPET ? `${text.slice(0, MAX_SNIPPET - 1)}…` : text;
|
|
20
|
+
/** `src/a.ts(3,5): error TS2322: Type …` lines of `tsc --pretty false`. */
|
|
21
|
+
function parseTscDiagnostics(output) {
|
|
22
|
+
const found = [];
|
|
23
|
+
for (const line of output.split('\n')) {
|
|
24
|
+
const m = TSC_DIAGNOSTIC.exec(line.trim());
|
|
25
|
+
if (m) {
|
|
26
|
+
found.push({
|
|
27
|
+
file: m[1].replace(/\\/g, '/'),
|
|
28
|
+
line: Number(m[2]),
|
|
29
|
+
rule: m[3],
|
|
30
|
+
snippet: clip(m[4]),
|
|
31
|
+
severity: 'error',
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return found;
|
|
36
|
+
}
|
|
37
|
+
/** The array `eslint --format json` writes. Anything else yields no findings. */
|
|
38
|
+
function parseEslintJson(raw, cwd) {
|
|
39
|
+
let report;
|
|
40
|
+
try {
|
|
41
|
+
report = JSON.parse(raw);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
if (!Array.isArray(report))
|
|
47
|
+
return [];
|
|
48
|
+
return report.flatMap(file => (file.messages ?? []).map(m => ({
|
|
49
|
+
file: node_path_1.default.relative(cwd, file.filePath).replace(/\\/g, '/'),
|
|
50
|
+
...(m.line !== undefined && { line: m.line }),
|
|
51
|
+
rule: m.ruleId ?? 'parse-error',
|
|
52
|
+
snippet: clip(m.message),
|
|
53
|
+
severity: m.severity === 2 ? 'error' : 'warn',
|
|
54
|
+
})));
|
|
55
|
+
}
|
|
56
|
+
/** One path per line, as `prettier --list-different` prints them. */
|
|
57
|
+
function parseListDifferent(stdout) {
|
|
58
|
+
return stdout
|
|
59
|
+
.split('\n')
|
|
60
|
+
.map(l => l.trim())
|
|
61
|
+
.filter(Boolean)
|
|
62
|
+
.map(file => ({
|
|
63
|
+
file: file.replace(/\\/g, '/'),
|
|
64
|
+
rule: 'prettier',
|
|
65
|
+
snippet: 'not formatted',
|
|
66
|
+
severity: 'warn',
|
|
67
|
+
}));
|
|
68
|
+
}
|
|
@@ -9,8 +9,10 @@ const node_path_1 = __importDefault(require("node:path"));
|
|
|
9
9
|
const index_js_1 = require("../../../config/index.js");
|
|
10
10
|
const execa_js_1 = require("../../../utils/execa.js");
|
|
11
11
|
const index_js_2 = require("../../../utils/index.js");
|
|
12
|
+
const finding_store_js_1 = require("../../finding-store.js");
|
|
12
13
|
const targets_js_1 = require("../../targets.js");
|
|
13
14
|
const types_js_1 = require("../../types.js");
|
|
15
|
+
const locate_js_1 = require("./locate.js");
|
|
14
16
|
function loadUserPrettierConfig(cwd) {
|
|
15
17
|
const configPath = node_path_1.default.join(cwd, 'pr-checkmate.json');
|
|
16
18
|
if (!node_fs_1.default.existsSync(configPath))
|
|
@@ -59,7 +61,9 @@ exports.prettierCheck = {
|
|
|
59
61
|
ignorePath = tempIgnore = createTempIgnoreFile(ctx.cwd);
|
|
60
62
|
const hasLocal = (0, index_js_1.hasLocalConfig)(['.prettierrc', '.prettierrc.json', '.prettierrc.js', 'prettier.config.js'], ctx.cwd);
|
|
61
63
|
// write=true → format in place; write=false → report-only (`--check`, never mutates files).
|
|
62
|
-
|
|
64
|
+
// `--list-different` names the unformatted files on stdout, which is what lets a
|
|
65
|
+
// finding point at them; `--check` only says how many there are.
|
|
66
|
+
const mode = ctx.write ? '--write' : '--list-different';
|
|
63
67
|
let tempConfig = null;
|
|
64
68
|
let args;
|
|
65
69
|
if (hasLocal) {
|
|
@@ -73,11 +77,20 @@ exports.prettierCheck = {
|
|
|
73
77
|
try {
|
|
74
78
|
const result = await (0, execa_js_1.execa)(process.execPath, [prettierBin, ...args], {
|
|
75
79
|
cwd: ctx.cwd,
|
|
76
|
-
stdio: 'inherit',
|
|
80
|
+
...(ctx.write && { stdio: 'inherit' }),
|
|
77
81
|
reject: false,
|
|
78
82
|
});
|
|
79
83
|
if (result.exitCode === 0)
|
|
80
84
|
return (0, types_js_1.pass)();
|
|
85
|
+
if (!ctx.write) {
|
|
86
|
+
const unformatted = (0, locate_js_1.parseListDifferent)(String(result.stdout ?? ''));
|
|
87
|
+
unformatted.forEach(f => {
|
|
88
|
+
index_js_2.logger.warn(`[Prettier]: not formatted: ${f.file}`);
|
|
89
|
+
(0, finding_store_js_1.addFinding)(f);
|
|
90
|
+
});
|
|
91
|
+
if (result.stderr)
|
|
92
|
+
index_js_2.logger.warn(String(result.stderr));
|
|
93
|
+
}
|
|
81
94
|
// In --check mode a non-zero exit means "some files are not formatted".
|
|
82
95
|
return (0, types_js_1.warn)(ctx.write ? 'prettier failed' : 'unformatted files (run with write to fix)');
|
|
83
96
|
}
|
|
@@ -8,8 +8,10 @@ const node_fs_1 = __importDefault(require("node:fs"));
|
|
|
8
8
|
const node_path_1 = __importDefault(require("node:path"));
|
|
9
9
|
const execa_js_1 = require("../../../utils/execa.js");
|
|
10
10
|
const index_js_1 = require("../../../utils/index.js");
|
|
11
|
+
const finding_store_js_1 = require("../../finding-store.js");
|
|
11
12
|
const reuse_js_1 = require("../../install/reuse.js");
|
|
12
13
|
const types_js_1 = require("../../types.js");
|
|
14
|
+
const locate_js_1 = require("./locate.js");
|
|
13
15
|
/**
|
|
14
16
|
* Prefer the consumer project's own TypeScript so its tsconfig is compiled by the
|
|
15
17
|
* version it was written for; fall back to the compiler bundled with pr-checkmate.
|
|
@@ -45,7 +47,7 @@ exports.typecheckCheck = {
|
|
|
45
47
|
return (0, types_js_1.skip)('project dependencies not installed (no node_modules) — run `npm ci` before typecheck');
|
|
46
48
|
}
|
|
47
49
|
const tscBin = resolveTsc(ctx);
|
|
48
|
-
const result = await (0, execa_js_1.execa)(process.execPath, [tscBin, '--noEmit'], {
|
|
50
|
+
const result = await (0, execa_js_1.execa)(process.execPath, [tscBin, '--noEmit', '--pretty', 'false'], {
|
|
49
51
|
cwd: ctx.cwd,
|
|
50
52
|
reject: false,
|
|
51
53
|
});
|
|
@@ -62,6 +64,7 @@ exports.typecheckCheck = {
|
|
|
62
64
|
index_js_1.logger.warn(`[TypeScript]: ⚠️ tsc exited ${result.exitCode ?? 'abnormally'} without reporting a type error — treating as a tooling failure, not a type error`);
|
|
63
65
|
return (0, types_js_1.warn)(`tsc failed to run (exit ${result.exitCode ?? 'killed'})`);
|
|
64
66
|
}
|
|
67
|
+
(0, locate_js_1.parseTscDiagnostics)(output).forEach(finding_store_js_1.addFinding);
|
|
65
68
|
return (0, types_js_1.fail)(`${errorCount} type error(s)`);
|
|
66
69
|
},
|
|
67
70
|
};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.prSizeCheck = void 0;
|
|
3
|
+
exports.prSizeCheck = exports.DEFAULT_MAX_LINES = exports.DEFAULT_MAX_FILES = void 0;
|
|
4
4
|
const execa_js_1 = require("../../../utils/execa.js");
|
|
5
5
|
const index_js_1 = require("../../../utils/index.js");
|
|
6
6
|
const types_js_1 = require("../../types.js");
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
exports.DEFAULT_MAX_FILES = 50;
|
|
8
|
+
exports.DEFAULT_MAX_LINES = 1000;
|
|
9
9
|
exports.prSizeCheck = {
|
|
10
10
|
name: 'PR Size',
|
|
11
11
|
phase: 'informational',
|
|
@@ -14,8 +14,8 @@ exports.prSizeCheck = {
|
|
|
14
14
|
return ctx.config.prSize?.enabled !== false;
|
|
15
15
|
},
|
|
16
16
|
async run(ctx) {
|
|
17
|
-
const maxFiles = ctx.config.prSize?.maxFiles ?? DEFAULT_MAX_FILES;
|
|
18
|
-
const maxLines = ctx.config.prSize?.maxLines ?? DEFAULT_MAX_LINES;
|
|
17
|
+
const maxFiles = ctx.config.prSize?.maxFiles ?? exports.DEFAULT_MAX_FILES;
|
|
18
|
+
const maxLines = ctx.config.prSize?.maxLines ?? exports.DEFAULT_MAX_LINES;
|
|
19
19
|
try {
|
|
20
20
|
const { stdout } = await (0, execa_js_1.execa)('git', ['diff', '--shortstat', ctx.baseSha, ctx.headSha ?? 'HEAD'], {
|
|
21
21
|
cwd: ctx.cwd,
|