pr-checkmate 3.2.3 → 4.0.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/.eslintrc.js +3 -9
- package/LICENSE +8 -3
- package/README.md +54 -5
- package/dist/cjs/agent/hook.d.ts +37 -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 +20 -0
- package/dist/cjs/agent/setup.js +229 -0
- package/dist/cjs/cli.js +101 -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 +11 -1
- package/dist/cjs/core/checks/dependencies/grype-scan.js +18 -0
- package/dist/cjs/core/checks/findings.js +33 -7
- 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 +12 -0
- package/dist/cjs/core/checks/languages/ruff-shared.js +6 -1
- package/dist/cjs/core/checks/pr/commitlint.js +4 -2
- 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/quality/package-size.d.ts +23 -1
- package/dist/cjs/core/checks/quality/package-size.js +20 -4
- package/dist/cjs/core/checks/quality/spellcheck.js +8 -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/install/doctor.js +12 -0
- package/dist/cjs/core/install/grype-db.d.ts +25 -0
- package/dist/cjs/core/install/grype-db.js +45 -0
- package/dist/cjs/core/install/profiles.d.ts +1 -1
- package/dist/cjs/core/install/profiles.js +1 -1
- package/dist/cjs/core/install/reuse.js +6 -3
- 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 +13 -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.d.ts +0 -8
- package/dist/cjs/init.js +12 -2
- 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 +33 -0
- package/dist/cjs/mcp/dependency.js +407 -0
- package/dist/cjs/mcp/protocol.d.ts +97 -0
- package/dist/cjs/mcp/protocol.js +181 -0
- package/dist/cjs/mcp/review.d.ts +63 -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 +633 -0
- package/dist/cjs/utils/execa.d.ts +7 -0
- package/dist/cjs/utils/execa.js +41 -1
- package/dist/cjs/utils/own-deps.js +8 -1
- package/dist/cjs/utils/resolve-bin.d.ts +2 -0
- package/dist/cjs/utils/resolve-bin.js +5 -0
- 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 +37 -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 +20 -0
- package/dist/esm/agent/setup.js +220 -0
- package/dist/esm/cli.js +100 -2
- 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 +11 -1
- package/dist/esm/core/checks/dependencies/dependency-check.js +2 -2
- package/dist/esm/core/checks/dependencies/grype-scan.js +18 -0
- package/dist/esm/core/checks/dependencies/license-check.js +2 -2
- package/dist/esm/core/checks/dependencies/npm-audit.js +2 -2
- package/dist/esm/core/checks/findings.js +33 -7
- package/dist/esm/core/checks/git/diff-smell.js +1 -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/cpp-format.js +1 -1
- package/dist/esm/core/checks/languages/lint.js +12 -3
- package/dist/esm/core/checks/languages/prettier.js +3 -3
- package/dist/esm/core/checks/languages/python-format.js +2 -2
- package/dist/esm/core/checks/languages/python-lint.js +2 -2
- package/dist/esm/core/checks/languages/ruff-shared.js +6 -1
- package/dist/esm/core/checks/languages/typecheck.js +1 -1
- package/dist/esm/core/checks/pr/commitlint.js +4 -2
- package/dist/esm/core/checks/pr/pr-body.js +1 -1
- package/dist/esm/core/checks/pr/pr-size-check.d.ts +2 -0
- package/dist/esm/core/checks/pr/pr-size-check.js +3 -3
- package/dist/esm/core/checks/pr/pr-title-lint.js +1 -1
- package/dist/esm/core/checks/quality/dead-code.js +1 -1
- package/dist/esm/core/checks/quality/duplicate-check.js +1 -1
- package/dist/esm/core/checks/quality/markdown-lint.js +1 -1
- package/dist/esm/core/checks/quality/package-size.d.ts +23 -1
- package/dist/esm/core/checks/quality/package-size.js +19 -4
- package/dist/esm/core/checks/quality/spellcheck.js +11 -8
- 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 +21 -1
- package/dist/esm/core/checks/security/security.d.ts +0 -13
- package/dist/esm/core/checks/security/security.js +28 -1
- 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/install/doctor.js +12 -0
- package/dist/esm/core/install/grype-db.d.ts +25 -0
- package/dist/esm/core/install/grype-db.js +41 -0
- package/dist/esm/core/install/prepare.js +1 -1
- package/dist/esm/core/install/profiles.d.ts +1 -1
- package/dist/esm/core/install/profiles.js +1 -1
- package/dist/esm/core/install/reuse.js +6 -3
- package/dist/esm/core/registry.js +2 -0
- package/dist/esm/core/reporters.js +9 -0
- package/dist/esm/core/run-checks.js +17 -7
- package/dist/esm/core/sarif.d.ts +13 -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.d.ts +0 -8
- package/dist/esm/init.js +15 -4
- 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 +33 -0
- package/dist/esm/mcp/dependency.js +402 -0
- package/dist/esm/mcp/protocol.d.ts +97 -0
- package/dist/esm/mcp/protocol.js +175 -0
- package/dist/esm/mcp/review.d.ts +63 -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 +626 -0
- package/dist/esm/utils/execa.d.ts +7 -0
- package/dist/esm/utils/execa.js +40 -1
- package/dist/esm/utils/own-deps.js +8 -1
- package/dist/esm/utils/resolve-bin.d.ts +2 -0
- package/dist/esm/utils/resolve-bin.js +4 -0
- package/dist/esm/utils/result-collector.d.ts +17 -1
- package/dist/esm/utils/result-collector.js +7 -2
- package/eslint-default-rules.json +2 -8
- package/eslint.config.mjs +37 -8
- package/install-manifests/commitlint/package-lock.json +5 -4
- package/install-manifests/core/package-lock.json +5 -3
- package/install-manifests/js/package-lock.json +250 -571
- package/install-manifests/js/package.json +10 -8
- package/install-manifests/markdown/package-lock.json +16 -13
- package/install-manifests/markdown/package.json +3 -0
- package/install-manifests/spellcheck/package-lock.json +3 -3
- package/package.json +24 -16
- package/pr-checkmate.schema.json +35 -0
- package/src/config/.eslintrc.js +3 -9
- package/src/config/eslint-default-rules.json +2 -8
- package/src/config/pr-checkmate.schema.json +35 -0
package/dist/esm/cli.js
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { evaluateHook } from './agent/hook.js';
|
|
5
|
+
import { reportSetup, setupAgent } from './agent/setup.js';
|
|
6
|
+
import { loadProjectConfig } from './config/loader.js';
|
|
7
|
+
import { AGENT_COMMANDS, COMMAND_TO_CHECK, FLAGS, STANDALONE_COMMANDS, TOOLING_COMMANDS, } from './core/commands.js';
|
|
3
8
|
import { runChecks } from './core/index.js';
|
|
4
9
|
import { installProfiles, pruneCache, runDoctor } from './core/install/doctor.js';
|
|
10
|
+
import { ensureGrypeDb } from './core/install/grype-db.js';
|
|
11
|
+
import { toSarif } from './core/sarif.js';
|
|
5
12
|
import { init } from './init.js';
|
|
6
|
-
import {
|
|
13
|
+
import { serve } from './mcp/serve.js';
|
|
14
|
+
import { createServerDefinition } from './mcp/tools.js';
|
|
15
|
+
import { isolateInheritedStdio } from './utils/execa.js';
|
|
16
|
+
import { getUpdateNotice, logger } from './utils/index.js';
|
|
17
|
+
import { packageVersion } from './utils/package-version.js';
|
|
7
18
|
const argv = process.argv.slice(2);
|
|
8
19
|
/**
|
|
9
20
|
* Flags are separated from positional arguments so they compose with every command
|
|
@@ -50,6 +61,28 @@ function rejectConflictingScope() {
|
|
|
50
61
|
logger.error('[cli]: "--all" applies only to "install". Did you mean "--full"?');
|
|
51
62
|
process.exit(1);
|
|
52
63
|
}
|
|
64
|
+
// `mcp` speaks a protocol on stdout and `hook` is invoked by a harness with a
|
|
65
|
+
// fixed payload: neither has anything to configure from the command line, and a
|
|
66
|
+
// flag that quietly did nothing would look like it had.
|
|
67
|
+
if ((cmd === 'mcp' || cmd === 'hook') && flags.length > 0) {
|
|
68
|
+
logger.error(`[cli]: "${cmd}" takes no flags, got: ${flags.join(', ')}.`);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
// Only a command that runs checks has findings to export; `all`, `precommit` and a
|
|
72
|
+
// named check do, every other standalone command does not.
|
|
73
|
+
const runsChecks = !cmd || cmd === 'all' || cmd === 'precommit' || !STANDALONE_COMMANDS.includes(cmd);
|
|
74
|
+
if (flags.includes('--sarif') && !runsChecks) {
|
|
75
|
+
logger.error('[cli]: "--sarif" applies only to commands that run checks.');
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
if (flags.includes('--no-hook') && cmd !== 'agent-setup') {
|
|
79
|
+
logger.error('[cli]: "--no-hook" applies only to "agent-setup".');
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
if (flags.includes('--full') && AGENT_COMMANDS.includes(cmd ?? '')) {
|
|
83
|
+
logger.error(`[cli]: "${cmd}" reviews nothing on its own, so "--full" has no meaning.`);
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
53
86
|
if (flags.includes('--full') && TOOLING_COMMANDS.includes(cmd ?? '')) {
|
|
54
87
|
logger.error(`[cli]: "${cmd}" manages tooling and reviews nothing, so "--full" has no meaning.`);
|
|
55
88
|
process.exit(1);
|
|
@@ -71,6 +104,55 @@ function rejectExtraArgs() {
|
|
|
71
104
|
logger.error(`[cli]: "${cmd}" takes no other arguments, got: ${args.slice(1).join(', ')}. Run them as a separate command.`);
|
|
72
105
|
process.exit(1);
|
|
73
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* Write the report as SARIF when `--sarif` was passed. Written before the exit code
|
|
109
|
+
* is decided, so a failing run still leaves the file a dashboard needs to show why.
|
|
110
|
+
*/
|
|
111
|
+
function writeSarifIfRequested(report) {
|
|
112
|
+
if (!flags.includes('--sarif'))
|
|
113
|
+
return;
|
|
114
|
+
const file = path.resolve(process.cwd(), 'pr-checkmate.sarif');
|
|
115
|
+
fs.writeFileSync(file, `${JSON.stringify(toSarif(report, packageVersion()), null, 2)}\n`);
|
|
116
|
+
logger.info(`[sarif]: wrote ${file}`);
|
|
117
|
+
}
|
|
118
|
+
/** Everything the harness wrote to stdin, as text; empty when stdin is closed or a terminal. */
|
|
119
|
+
async function readStdin() {
|
|
120
|
+
if (process.stdin.isTTY)
|
|
121
|
+
return '';
|
|
122
|
+
const chunks = [];
|
|
123
|
+
for await (const chunk of process.stdin)
|
|
124
|
+
chunks.push(Buffer.from(chunk));
|
|
125
|
+
return Buffer.concat(chunks).toString('utf-8');
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* The commands that serve AI agents. Returns whether `cmd` was one of them (and has
|
|
129
|
+
* therefore already exited); kept out of the main dispatcher so that one stays
|
|
130
|
+
* readable as the list of commands grows.
|
|
131
|
+
*
|
|
132
|
+
* `mcp` must stay ahead of anything that could print: from here until stdin closes,
|
|
133
|
+
* stdout carries protocol messages and nothing else.
|
|
134
|
+
*/
|
|
135
|
+
async function runAgentCommand() {
|
|
136
|
+
if (cmd === 'mcp') {
|
|
137
|
+
await serve(createServerDefinition());
|
|
138
|
+
process.exit(0);
|
|
139
|
+
}
|
|
140
|
+
if (cmd === 'hook') {
|
|
141
|
+
// Discarded rather than shown: the harness reads stdout and stderr of a hook
|
|
142
|
+
// as instructions to the model, so only the verdict may reach either.
|
|
143
|
+
isolateInheritedStdio('discard');
|
|
144
|
+
logger.setSink({ out: () => undefined, err: () => undefined });
|
|
145
|
+
const verdict = await evaluateHook(await readStdin(), process.cwd());
|
|
146
|
+
if (verdict.stderr)
|
|
147
|
+
process.stderr.write(`${verdict.stderr}\n`);
|
|
148
|
+
process.exit(verdict.exitCode);
|
|
149
|
+
}
|
|
150
|
+
if (cmd === 'agent-setup') {
|
|
151
|
+
reportSetup(setupAgent(process.cwd(), { hook: !flags.includes('--no-hook') }));
|
|
152
|
+
process.exit(0);
|
|
153
|
+
}
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
74
156
|
void (async () => {
|
|
75
157
|
try {
|
|
76
158
|
rejectUnknownFlags();
|
|
@@ -80,6 +162,8 @@ void (async () => {
|
|
|
80
162
|
await init();
|
|
81
163
|
process.exit(0);
|
|
82
164
|
}
|
|
165
|
+
if (await runAgentCommand())
|
|
166
|
+
return;
|
|
83
167
|
// Tooling management. These run no checks — they prepare, inspect or clean
|
|
84
168
|
// up what the checks need, so they never touch the diff or the reporter.
|
|
85
169
|
if (cmd === 'install') {
|
|
@@ -88,6 +172,17 @@ void (async () => {
|
|
|
88
172
|
for (const result of failed) {
|
|
89
173
|
logger.error(`[install]: ❌ ${result.profiles.join(', ') || 'tooling'}: ${result.reason}`);
|
|
90
174
|
}
|
|
175
|
+
// Warm grype's vulnerability database too, with no time cap: the download is
|
|
176
|
+
// hundreds of MB and the scan itself would only be allowed a couple of minutes.
|
|
177
|
+
// Best-effort — an absent grype or a failed download is a warning, not a
|
|
178
|
+
// failed install, since Grype Scan skips cleanly without it.
|
|
179
|
+
if (loadProjectConfig().grypeScan?.enabled !== false) {
|
|
180
|
+
const why = await ensureGrypeDb(process.cwd());
|
|
181
|
+
if (why && why !== 'ENOENT')
|
|
182
|
+
logger.warn(`[install]: ⚠️ grype: ${why}`);
|
|
183
|
+
else if (!why)
|
|
184
|
+
logger.info('[install]: grype vulnerability database is up to date');
|
|
185
|
+
}
|
|
91
186
|
process.exit(failed.length > 0 ? 1 : 0);
|
|
92
187
|
}
|
|
93
188
|
if (cmd === 'doctor') {
|
|
@@ -110,12 +205,14 @@ void (async () => {
|
|
|
110
205
|
logger.info('');
|
|
111
206
|
notice.forEach(line => logger.info(line));
|
|
112
207
|
}
|
|
208
|
+
writeSarifIfRequested(report);
|
|
113
209
|
process.exit(report.ok ? 0 : 1);
|
|
114
210
|
}
|
|
115
211
|
// Local pre-commit: check the staged changes (index vs HEAD), print to
|
|
116
212
|
// stdout, never modify files. Exits non-zero on failure to block the commit.
|
|
117
213
|
if (cmd === 'precommit') {
|
|
118
214
|
const report = await runChecks({ staged: true, reporter: 'stdout', write: false });
|
|
215
|
+
writeSarifIfRequested(report);
|
|
119
216
|
process.exit(report.ok ? 0 : 1);
|
|
120
217
|
}
|
|
121
218
|
// Every remaining argument is a check name, so `pr-checkmate lint spellcheck`
|
|
@@ -128,6 +225,7 @@ void (async () => {
|
|
|
128
225
|
}
|
|
129
226
|
const checkNames = [...new Set(args.map(arg => COMMAND_TO_CHECK[arg]))];
|
|
130
227
|
const report = await runChecks({ only: checkNames, reporter: 'stdout', write: true, full });
|
|
228
|
+
writeSarifIfRequested(report);
|
|
131
229
|
process.exit(report.ok ? 0 : 1);
|
|
132
230
|
}
|
|
133
231
|
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.
|
|
@@ -225,6 +225,7 @@ export const SOURCE_EXTENSIONS = {
|
|
|
225
225
|
json: ['.json'],
|
|
226
226
|
all: ['.ts', '.tsx', '.js', '.jsx', '.json', '.md', '.mdx'],
|
|
227
227
|
};
|
|
228
|
+
export const AGENT_SETUP_COMMAND = 'npx pr-checkmate agent-setup';
|
|
228
229
|
export const DEFAULT_COMMANDS = {
|
|
229
230
|
'npx pr-checkmate init': 'Create pr-checkmate.json and the CI workflow (first-run setup)',
|
|
230
231
|
'npx pr-checkmate all': 'Run all checks over the changes — the PR diff in CI, your branch changes locally',
|
|
@@ -239,6 +240,7 @@ export const DEFAULT_COMMANDS = {
|
|
|
239
240
|
'npx pr-checkmate diff-smell': 'Scan git diff for code smells',
|
|
240
241
|
'npx pr-checkmate pr-body': 'Validate PR description',
|
|
241
242
|
'npx pr-checkmate duplicate': 'Detect duplicate code blocks (jscpd)',
|
|
243
|
+
[AGENT_SETUP_COMMAND]: 'Connect your AI agent: MCP server, rules for it and edit hooks (writes .mcp.json)',
|
|
242
244
|
};
|
|
243
245
|
/**
|
|
244
246
|
* Packages allow-listed for the license check by default.
|
|
@@ -320,6 +322,8 @@ export const KNOWN_CONFIG_KEYS = new Set([
|
|
|
320
322
|
'diffSecurity',
|
|
321
323
|
'workflowSecurity',
|
|
322
324
|
'dockerfileSecurity',
|
|
325
|
+
'agentInstructions',
|
|
326
|
+
'mcp',
|
|
323
327
|
'sensitiveFileGuard',
|
|
324
328
|
'migrationSafety',
|
|
325
329
|
// 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,
|
|
@@ -318,7 +328,7 @@
|
|
|
318
328
|
"//ignores": "APPENDED to the built-in list: node_modules, dist, coverage, graphify-out, .claude and CHANGELOG.md.",
|
|
319
329
|
"ignores": []
|
|
320
330
|
},
|
|
321
|
-
"//7b-install": "WHICH TOOLING IS FETCHED. The
|
|
331
|
+
"//7b-install": "WHICH TOOLING IS FETCHED. The tooling totals ~233 MB and no repository needs all of it. \"adaptive\" (default) fetches only what this repository turns out to need; \"eager\" fetches everything up front, which is what a closed network or a prebuilt image wants and is also the exact previous behaviour; \"manual\" fetches nothing because the tools are already on the runner. A missing tool never fails silently: the affected checks skip with a reason in the report, or fail the run outright with onMissingTool: \"error\".",
|
|
322
332
|
"install": {
|
|
323
333
|
"strategy": "adaptive",
|
|
324
334
|
"//profiles": "Extra profiles beyond what detection concluded, for generated code or a monorepo layout detection cannot see through. Names: core, js, python, cpp, spellcheck, markdown, commitlint, duplicate.",
|
|
@@ -2,8 +2,8 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { hasLocalConfig } from '../../../config/index.js';
|
|
4
4
|
import { execa } from '../../../utils/execa.js';
|
|
5
|
-
import {
|
|
6
|
-
import { isNpmProject, pass,
|
|
5
|
+
import { cleanupTempFile, logger, resolvePackageBin } from '../../../utils/index.js';
|
|
6
|
+
import { fail, isNpmProject, pass, skip, warn } from '../../types.js';
|
|
7
7
|
/**
|
|
8
8
|
* Config files knip discovers on its own. When a client ships one, it is the source
|
|
9
9
|
* of truth and pr-checkmate passes no config of its own — the same rule spellcheck
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { execa } from '../../../utils/execa.js';
|
|
2
2
|
import { collectDirectDeps, isCiEnvironment, logger, normalizeName, } from '../../../utils/index.js';
|
|
3
|
+
import { ensureGrypeDb } from '../../install/grype-db.js';
|
|
3
4
|
import { pass, skip, warn } from '../../types.js';
|
|
4
5
|
import { notInstalled } from '../tool-install.js';
|
|
5
6
|
const SEVERITY_ORDER = ['negligible', 'low', 'medium', 'high', 'critical'];
|
|
@@ -17,6 +18,14 @@ const TYPE_TO_ECOSYSTEM = {
|
|
|
17
18
|
* looks like a hung commit — an advisory check must never be able to do that.
|
|
18
19
|
*/
|
|
19
20
|
const DEFAULT_TIMEOUT_MS = 120000;
|
|
21
|
+
/**
|
|
22
|
+
* Cap for refreshing grype's vulnerability database. A stale database (grype
|
|
23
|
+
* refuses one older than 5 days) is re-downloaded, hundreds of MB, which takes
|
|
24
|
+
* longer than the scan's own cap. Kept below the per-check timeout so the check
|
|
25
|
+
* itself reports the outcome instead of being cut off by the runner. `npx
|
|
26
|
+
* pr-checkmate install` refreshes it without any cap, ahead of time.
|
|
27
|
+
*/
|
|
28
|
+
const DB_UPDATE_TIMEOUT_MS = 240000;
|
|
20
29
|
function severityRank(severity) {
|
|
21
30
|
return SEVERITY_ORDER.indexOf((severity ?? '').toLowerCase());
|
|
22
31
|
}
|
|
@@ -72,10 +81,19 @@ export const grypeScanCheck = {
|
|
|
72
81
|
// third-party code is not something the client could fix anyway (see below).
|
|
73
82
|
const excludes = (ctx.config.ignoreDirs ?? []).flatMap(dir => ['--exclude', `**/${dir}/**`]);
|
|
74
83
|
const timeout = settings.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
84
|
+
const unusable = await ensureGrypeDb(ctx.cwd, DB_UPDATE_TIMEOUT_MS);
|
|
85
|
+
if (unusable === 'ENOENT')
|
|
86
|
+
return notInstalled('grype');
|
|
87
|
+
if (unusable) {
|
|
88
|
+
logger.warn(`[Grype Scan]: ⚠️ ${unusable} — skipping`);
|
|
89
|
+
return skip(unusable);
|
|
90
|
+
}
|
|
91
|
+
// The database was just checked, so the scan must not try to fetch one itself.
|
|
75
92
|
const result = await execa('grype', ['dir:.', '-o', 'json', '-q', ...excludes], {
|
|
76
93
|
cwd: ctx.cwd,
|
|
77
94
|
reject: false,
|
|
78
95
|
timeout,
|
|
96
|
+
env: { GRYPE_DB_AUTO_UPDATE: 'false' },
|
|
79
97
|
});
|
|
80
98
|
// execa v9 with reject:false resolves (not throws) with `code: 'ENOENT'`
|
|
81
99
|
// when the binary is absent — detect the missing scanner on the result.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { execa } from '../../../utils/execa.js';
|
|
2
|
-
import { logger, resolvePackageBin
|
|
3
|
-
import { isNpmProject, pass,
|
|
2
|
+
import { getOwnDependencyClosure, logger, resolvePackageBin } from '../../../utils/index.js';
|
|
3
|
+
import { fail, isNpmProject, pass, skip } from '../../types.js';
|
|
4
4
|
const FORBIDDEN_LICENSES = ['GPL-2.0', 'GPL-3.0', 'AGPL-3.0', 'LGPL-2.0', 'LGPL-2.1', 'LGPL-3.0'];
|
|
5
5
|
/** Strip the trailing "@version" to get the bare package name (scoped-safe). */
|
|
6
6
|
function bareName(pkgKey) {
|
|
@@ -4,8 +4,8 @@ const __dirname = __dirnameFn(__fileURLToPath(import.meta.url));
|
|
|
4
4
|
import fs from 'node:fs';
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
import { execa } from '../../../utils/execa.js';
|
|
7
|
-
import {
|
|
8
|
-
import { isNpmProject, pass,
|
|
7
|
+
import { findOwnPackageRoot, logger } from '../../../utils/index.js';
|
|
8
|
+
import { fail, isNpmProject, pass, skip } from '../../types.js';
|
|
9
9
|
const SEVERITY_ORDER = ['info', 'low', 'moderate', 'high', 'critical'];
|
|
10
10
|
function isSeverityAtLeast(severity, level) {
|
|
11
11
|
const sevIdx = SEVERITY_ORDER.indexOf(severity);
|
|
@@ -2,6 +2,7 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { execa } from '../../utils/execa.js';
|
|
4
4
|
import { logger, matchesAnyGlob } from '../../utils/index.js';
|
|
5
|
+
import { fileFindings, noteLocation } from '../finding-store.js';
|
|
5
6
|
import { listTrackedFiles } from '../targets.js';
|
|
6
7
|
import { fail, pass, warn } from '../types.js';
|
|
7
8
|
/**
|
|
@@ -370,6 +371,8 @@ export const DEFAULT_DATAFLOW_WINDOW = 10;
|
|
|
370
371
|
const CROSS_LINE_SQL_LABEL = 'Interpolated value reaches a query (SQL injection risk, cross-line)';
|
|
371
372
|
/** `@@ ... @@` — a new hunk, and the boundary taint never crosses. */
|
|
372
373
|
const DIFF_HUNK_HEADER = /^@@ /;
|
|
374
|
+
/** The first line of the new side of a hunk: `@@ -3,2 +10,4 @@` → 10. */
|
|
375
|
+
const HUNK_NEW_START = /^@@ -\d+(?:,\d+)? \+(\d+)/;
|
|
373
376
|
/**
|
|
374
377
|
* Whether this line hands `name` to a query sink.
|
|
375
378
|
*
|
|
@@ -443,6 +446,9 @@ export function scanDiffPatterns(stdout, patterns, opts = {}) {
|
|
|
443
446
|
const mode = opts.comments ?? 'language';
|
|
444
447
|
const ignorePaths = opts.ignorePaths ?? [];
|
|
445
448
|
let syntax = 'both';
|
|
449
|
+
// Where the walk is, for findings that need to say so.
|
|
450
|
+
let file = '';
|
|
451
|
+
let newLine = 0;
|
|
446
452
|
// Block comments span lines, so the state belongs to the walk, not to one line.
|
|
447
453
|
let inBlockComment = false;
|
|
448
454
|
// Whole files can be excluded by glob; the flag persists until the next header.
|
|
@@ -453,6 +459,7 @@ export function scanDiffPatterns(stdout, patterns, opts = {}) {
|
|
|
453
459
|
for (const line of stdout.split('\n')) {
|
|
454
460
|
const header = DIFF_FILE_HEADER.exec(line);
|
|
455
461
|
if (header) {
|
|
462
|
+
file = header[1];
|
|
456
463
|
syntax = mode === 'language' ? commentSyntaxFor(header[1]) : 'both';
|
|
457
464
|
inBlockComment = false;
|
|
458
465
|
skippingFile = ignorePaths.length > 0 && matchesAnyGlob(header[1], ignorePaths);
|
|
@@ -461,9 +468,15 @@ export function scanDiffPatterns(stdout, patterns, opts = {}) {
|
|
|
461
468
|
}
|
|
462
469
|
// Checked before the `+` filter below, which a hunk header does not satisfy.
|
|
463
470
|
if (DIFF_HUNK_HEADER.test(line)) {
|
|
471
|
+
newLine = Number(HUNK_NEW_START.exec(line)?.[1] ?? 0);
|
|
464
472
|
taint.reset();
|
|
465
473
|
continue;
|
|
466
474
|
}
|
|
475
|
+
// Counted before any skip: an ignored or excluded line still occupies a line
|
|
476
|
+
// number in the file, and the ones after it must not shift.
|
|
477
|
+
const lineNo = newLine;
|
|
478
|
+
if (line.startsWith('+'))
|
|
479
|
+
newLine++;
|
|
467
480
|
if (skippingFile)
|
|
468
481
|
continue;
|
|
469
482
|
// Only added lines carry block-comment state: a `-` line is code being
|
|
@@ -475,18 +488,28 @@ export function scanDiffPatterns(stdout, patterns, opts = {}) {
|
|
|
475
488
|
const prepared = uncomment(line, { mode, syntax, inBlockComment });
|
|
476
489
|
inBlockComment = prepared.inBlockComment;
|
|
477
490
|
const asCode = stripStringLiterals(prepared.text);
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
const bucket = severity === 'error' ? errors : warnings;
|
|
481
|
-
(bucket[label] ?? (bucket[label] = [])).push(line.slice(1).trim().substring(0, SAMPLE_MAX_LENGTH));
|
|
482
|
-
}
|
|
483
|
-
}
|
|
491
|
+
const where = { file, line: lineNo };
|
|
492
|
+
recordMatches(patterns, { text: prepared.text, asCode }, line, where, { errors, warnings });
|
|
484
493
|
if (taint.inspect(prepared.text, asCode)) {
|
|
485
|
-
(warnings
|
|
494
|
+
record(warnings, CROSS_LINE_SQL_LABEL, line, where);
|
|
486
495
|
}
|
|
487
496
|
}
|
|
488
497
|
return { errors, warnings };
|
|
489
498
|
}
|
|
499
|
+
/** File one added line under `label` and remember where it was, for findings that say so. */
|
|
500
|
+
function record(bucket, label, line, where) {
|
|
501
|
+
const sample = line.slice(1).trim().substring(0, SAMPLE_MAX_LENGTH);
|
|
502
|
+
(bucket[label] ?? (bucket[label] = [])).push(sample);
|
|
503
|
+
noteLocation(label, sample, where.file, where.line);
|
|
504
|
+
}
|
|
505
|
+
/** File every pattern that matches one prepared line under its own label and severity. */
|
|
506
|
+
function recordMatches(patterns, view, line, where, into) {
|
|
507
|
+
for (const { pattern, label, severity, view: wants } of patterns) {
|
|
508
|
+
if (pattern.test(wants === 'code' ? view.asCode : view.text)) {
|
|
509
|
+
record(severity === 'error' ? into.errors : into.warnings, label, line, where);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
}
|
|
490
513
|
/**
|
|
491
514
|
* Reduce one added line to what should be matched against: block comments
|
|
492
515
|
* blanked (carrying the open state on), then the trailing line comment removed.
|
|
@@ -577,6 +600,7 @@ export function bucketTotal(bucket) {
|
|
|
577
600
|
*/
|
|
578
601
|
export function reportCountedFindings(findings, ctx, opts) {
|
|
579
602
|
const sev = severityEmitter(opts.severity, opts.fallback ?? 'warn');
|
|
603
|
+
fileFindings(findings, sev.icon === '❌' ? 'error' : 'warn');
|
|
580
604
|
logFindings(findings, {
|
|
581
605
|
prefix: opts.prefix,
|
|
582
606
|
sampleLimit: opts.sampleLimit ?? ctx.config.log?.sampleLimit ?? 3,
|
|
@@ -681,6 +705,8 @@ export function reportSeverityBuckets(errorsIn, warningsIn, opts) {
|
|
|
681
705
|
const ignore = opts.ignore ?? [];
|
|
682
706
|
const filed = applyOverrides(dropIgnored(errorsIn, ignore), dropIgnored(warningsIn, ignore), opts.severityOverrides ?? {});
|
|
683
707
|
const { errors, warnings } = filed;
|
|
708
|
+
fileFindings(errors, 'error');
|
|
709
|
+
fileFindings(warnings, 'warn');
|
|
684
710
|
const totalErrors = bucketTotal(errors);
|
|
685
711
|
const totalWarnings = bucketTotal(warnings);
|
|
686
712
|
if (totalErrors === 0 && totalWarnings === 0)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { logger } from '../../../utils/index.js';
|
|
2
2
|
import { pass, skip } from '../../types.js';
|
|
3
|
-
import { bucketTotal, emptyBuckets, mergeSeverityBuckets, reportCountedFindings,
|
|
3
|
+
import { bucketTotal, emptyBuckets, mergeSeverityBuckets, reportCountedFindings, reviewLines, scanDiffPatterns, } from '../findings.js';
|
|
4
4
|
/**
|
|
5
5
|
* Smells that are only smells in live code.
|
|
6
6
|
*
|
|
@@ -5,7 +5,7 @@ import { logger, matchesAnyGlob } from '../../../utils/index.js';
|
|
|
5
5
|
import { resolveTargetFiles } from '../../targets.js';
|
|
6
6
|
import { pass, skip } from '../../types.js';
|
|
7
7
|
import { severityEmitter } from '../findings.js';
|
|
8
|
-
const DEFAULT_MAX_KB = 500;
|
|
8
|
+
export const DEFAULT_MAX_KB = 500;
|
|
9
9
|
/**
|
|
10
10
|
* Lockfiles are legitimately large and always generated, never accidental — so
|
|
11
11
|
* they're exempt from the size/binary guard by default (a 600 KB
|
|
@@ -4,7 +4,7 @@ import { getSourcePaths } from '../../../config/index.js';
|
|
|
4
4
|
import { execa } from '../../../utils/execa.js';
|
|
5
5
|
import { logger, resolvePackageBin } from '../../../utils/index.js';
|
|
6
6
|
import { resolveScopedTargetFiles } from '../../targets.js';
|
|
7
|
-
import { pass,
|
|
7
|
+
import { pass, skip, warn } from '../../types.js';
|
|
8
8
|
/**
|
|
9
9
|
* C/C++ source & header extensions clang-format understands. `.h` is intentionally
|
|
10
10
|
* included even though it is ambiguous with C — clang-format handles both.
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { getPackageConfigPath, getSourcePaths, hasLocalConfig, IGNORE_PATTERNS, } from '../../../config/index.js';
|
|
2
3
|
import { execa } from '../../../utils/execa.js';
|
|
3
|
-
import { logger, resolvePackageBin } from '../../../utils/index.js';
|
|
4
|
+
import { logger, registeredToolPaths, resolvePackageBin } from '../../../utils/index.js';
|
|
4
5
|
import { clientToolPaths } from '../../install/reuse.js';
|
|
5
6
|
import { resolveScopedTargetFiles } from '../../targets.js';
|
|
6
|
-
import { pass,
|
|
7
|
+
import { fail, pass, skip, warn } from '../../types.js';
|
|
7
8
|
/**
|
|
8
9
|
* Resolve the ESLint binary under the client's `install.reuseLocal` policy.
|
|
9
10
|
*
|
|
@@ -73,10 +74,18 @@ export const lintCheck = {
|
|
|
73
74
|
logger.info(`[ESLint]: Using pr-checkmate config (ESLint v${version}, ${configFile})`);
|
|
74
75
|
}
|
|
75
76
|
IGNORE_PATTERNS.forEach(p => args.push('--ignore-pattern', p));
|
|
77
|
+
// Our bundled config loads its plugins through these roots, and an ESLint
|
|
78
|
+
// older than 9 resolves legacy-config plugins relative to the path below — in
|
|
79
|
+
// both cases the plugins are in a fetched profile rather than beside us.
|
|
80
|
+
const toolRoots = registeredToolPaths();
|
|
81
|
+
if (!hasLocal && version < 9 && toolRoots.length > 0) {
|
|
82
|
+
args.push('--resolve-plugins-relative-to', toolRoots[0]);
|
|
83
|
+
}
|
|
76
84
|
const result = await execa(process.execPath, [eslintBin, ...args], {
|
|
77
85
|
cwd: ctx.cwd,
|
|
78
86
|
stdio: 'inherit',
|
|
79
87
|
reject: false,
|
|
88
|
+
env: { PR_CHECKMATE_TOOL_ROOTS: toolRoots.join(path.delimiter) },
|
|
80
89
|
});
|
|
81
90
|
if (result.exitCode === 0)
|
|
82
91
|
return pass();
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { getPackageConfigPath, hasLocalConfig, IGNORE_PATTERNS,
|
|
3
|
+
import { getPackageConfigPath, getSourcePaths, hasLocalConfig, IGNORE_PATTERNS, } from '../../../config/index.js';
|
|
4
4
|
import { execa } from '../../../utils/execa.js';
|
|
5
|
-
import {
|
|
5
|
+
import { cleanupTempFile, logger, resolvePackageBin } from '../../../utils/index.js';
|
|
6
6
|
import { resolveScopedTargetFiles } from '../../targets.js';
|
|
7
|
-
import { pass,
|
|
7
|
+
import { pass, skip, warn } from '../../types.js';
|
|
8
8
|
function loadUserPrettierConfig(cwd) {
|
|
9
9
|
const configPath = path.join(cwd, 'pr-checkmate.json');
|
|
10
10
|
if (!fs.existsSync(configPath))
|
|
@@ -2,8 +2,8 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { logger } from '../../../utils/index.js';
|
|
4
4
|
import { resolveTargetFiles } from '../../targets.js';
|
|
5
|
-
import { pass,
|
|
6
|
-
import {
|
|
5
|
+
import { pass, skip, warn } from '../../types.js';
|
|
6
|
+
import { createRuffWorkspace, PY_GLOBS } from './ruff-shared.js';
|
|
7
7
|
/**
|
|
8
8
|
* Formats changed Python files with Ruff's formatter (WASM, no Python/pip needed).
|
|
9
9
|
* Read-only in check mode (`warn` when files differ from formatted output); rewrites
|
|
@@ -2,8 +2,8 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { logger } from '../../../utils/index.js';
|
|
4
4
|
import { resolveTargetFiles } from '../../targets.js';
|
|
5
|
-
import { pass,
|
|
6
|
-
import {
|
|
5
|
+
import { pass, skip, warn } from '../../types.js';
|
|
6
|
+
import { createRuffWorkspace, PY_GLOBS } from './ruff-shared.js';
|
|
7
7
|
/**
|
|
8
8
|
* Lints changed Python files with Ruff, bundled as a self-contained WASM module
|
|
9
9
|
* (`@astral-sh/ruff-wasm-nodejs`) so it runs on a bare Node image with no Python
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createRequire as __createRequire } from 'node:module';
|
|
2
2
|
const require = __createRequire(import.meta.url);
|
|
3
|
+
import { resolvePackageBin } from '../../../utils/index.js';
|
|
3
4
|
export const PY_GLOBS = ['*.py'];
|
|
4
5
|
/**
|
|
5
6
|
* The rules we lint Python with — pinned here rather than left to Ruff's defaults.
|
|
@@ -26,7 +27,11 @@ const SELECTED_RULES = ['E4', 'E7', 'E9', 'F'];
|
|
|
26
27
|
* singleton) so the WASM module loads only when a check runs.
|
|
27
28
|
*/
|
|
28
29
|
export function createRuffWorkspace() {
|
|
29
|
-
|
|
30
|
+
// Resolved rather than required by name: the package lives in the `python`
|
|
31
|
+
// profile, which is fetched into the cache directory when this repository
|
|
32
|
+
// needs it, and a bare specifier only searches our own tree.
|
|
33
|
+
const entry = resolvePackageBin('@astral-sh/ruff-wasm-nodejs', 'ruff_wasm.js');
|
|
34
|
+
const { Workspace, PositionEncoding } = require(entry);
|
|
30
35
|
const settings = {
|
|
31
36
|
...Workspace.defaultSettings(),
|
|
32
37
|
lint: { select: SELECTED_RULES },
|
|
@@ -3,7 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
import { execa } from '../../../utils/execa.js';
|
|
4
4
|
import { logger, resolvePackageBin } from '../../../utils/index.js';
|
|
5
5
|
import { clientToolPaths } from '../../install/reuse.js';
|
|
6
|
-
import { pass,
|
|
6
|
+
import { fail, pass, skip, warn } from '../../types.js';
|
|
7
7
|
/**
|
|
8
8
|
* Prefer the consumer project's own TypeScript so its tsconfig is compiled by the
|
|
9
9
|
* version it was written for; fall back to the compiler bundled with pr-checkmate.
|
|
@@ -34,8 +34,10 @@ const DEFAULT_IGNORES = ['^Release\\b', '^chore\\(release\\):'];
|
|
|
34
34
|
function buildTempConfig(types, ignores) {
|
|
35
35
|
// Emit a CJS module so `ignores` can hold functions — commitlint requires
|
|
36
36
|
// predicate functions there, which a JSON config cannot express.
|
|
37
|
-
//
|
|
38
|
-
|
|
37
|
+
// Resolved through the same roots as the binary, not by bare specifier: both
|
|
38
|
+
// live in the `commitlint` profile, which is fetched into the cache directory,
|
|
39
|
+
// and `require.resolve` alone only searches our own tree.
|
|
40
|
+
const configConventional = resolvePackageBin('@commitlint/config-conventional', 'lib/index.js');
|
|
39
41
|
const content = `const patterns = ${JSON.stringify(ignores)}.map((p) => new RegExp(p, 'i'));
|
|
40
42
|
module.exports = {
|
|
41
43
|
extends: [${JSON.stringify(configConventional)}],
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import { logger } from '../../../utils/index.js';
|
|
3
|
-
import { pass,
|
|
3
|
+
import { pass, skip, warn } from '../../types.js';
|
|
4
4
|
// Matches "Closes #123", "Fixes #45", "Resolves #7" or JIRA-style "PROJ-456".
|
|
5
5
|
const TICKET_PATTERN = /(?:closes?|fixes?|resolves?)\s+#\d+|[A-Z][A-Z0-9]+-\d+/i;
|
|
6
6
|
export const prBodyCheck = {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { execa } from '../../../utils/execa.js';
|
|
2
2
|
import { logger } from '../../../utils/index.js';
|
|
3
|
-
import { pass,
|
|
4
|
-
const DEFAULT_MAX_FILES = 50;
|
|
5
|
-
const DEFAULT_MAX_LINES = 1000;
|
|
3
|
+
import { pass, skip, warn } from '../../types.js';
|
|
4
|
+
export const DEFAULT_MAX_FILES = 50;
|
|
5
|
+
export const DEFAULT_MAX_LINES = 1000;
|
|
6
6
|
export const prSizeCheck = {
|
|
7
7
|
name: 'PR Size',
|
|
8
8
|
phase: 'informational',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import { logger } from '../../../utils/index.js';
|
|
3
|
-
import { pass,
|
|
3
|
+
import { pass, skip, warn } from '../../types.js';
|
|
4
4
|
const CONVENTIONAL_PREFIX = /^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\(.+\))?!?: .+/;
|
|
5
5
|
export const prTitleLintCheck = {
|
|
6
6
|
name: 'PR Title',
|
|
@@ -4,7 +4,7 @@ import { DEAD_CODE_IGNORE_DEFAULTS } from '../../../config/constants.js';
|
|
|
4
4
|
import { execa } from '../../../utils/execa.js';
|
|
5
5
|
import { logger, resolvePackageBin } from '../../../utils/index.js';
|
|
6
6
|
import { clientToolPaths } from '../../install/reuse.js';
|
|
7
|
-
import { pass,
|
|
7
|
+
import { pass, skip, warn } from '../../types.js';
|
|
8
8
|
/**
|
|
9
9
|
* Resolve ts-unused-exports, preferring the consumer project's own copy (so a
|
|
10
10
|
* repo pinned to a different major keeps its own behaviour) and falling back to
|
|
@@ -6,7 +6,7 @@ import { getSourcePaths } from '../../../config/index.js';
|
|
|
6
6
|
import { execa } from '../../../utils/execa.js';
|
|
7
7
|
import { logger, resolvePackageBin } from '../../../utils/index.js';
|
|
8
8
|
import { resolveScopedTargetFiles } from '../../targets.js';
|
|
9
|
-
import { pass,
|
|
9
|
+
import { pass, skip, warn } from '../../types.js';
|
|
10
10
|
import { logSample } from '../findings.js';
|
|
11
11
|
/**
|
|
12
12
|
* The files to hand jscpd, per language.
|
|
@@ -4,7 +4,7 @@ import { pathToFileURL } from 'node:url';
|
|
|
4
4
|
import { GENERATED_DOC_FILES, getPackageConfigPath, hasLocalConfig, } from '../../../config/index.js';
|
|
5
5
|
import { logger, resolvePackageBin } from '../../../utils/index.js';
|
|
6
6
|
import { resolveTargetFiles } from '../../targets.js';
|
|
7
|
-
import { pass,
|
|
7
|
+
import { pass, skip, warn } from '../../types.js';
|
|
8
8
|
function loadUserMarkdownConfig(cwd) {
|
|
9
9
|
const configPath = path.join(cwd, 'pr-checkmate.json');
|
|
10
10
|
if (!fs.existsSync(configPath))
|