pr-checkmate 1.22.8 → 1.23.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/README.md +101 -333
- package/cspell.json +9 -1
- package/dist/cli.js +32 -2
- package/dist/config/constants.d.ts +9 -0
- package/dist/config/constants.js +68 -1
- package/dist/core/checks/dependencies/banned-imports.d.ts +2 -0
- package/dist/core/checks/dependencies/banned-imports.js +71 -0
- package/dist/core/checks/{circular-deps.d.ts → dependencies/circular-deps.d.ts} +1 -1
- package/dist/core/checks/{circular-deps.js → dependencies/circular-deps.js} +5 -5
- package/dist/core/checks/{dependency-check.d.ts → dependencies/dependency-check.d.ts} +1 -1
- package/dist/core/checks/{dependency-check.js → dependencies/dependency-check.js} +3 -3
- package/dist/core/checks/{license-check.d.ts → dependencies/license-check.d.ts} +1 -1
- package/dist/core/checks/{license-check.js → dependencies/license-check.js} +3 -3
- package/dist/core/checks/{npm-audit.d.ts → dependencies/npm-audit.d.ts} +1 -1
- package/dist/core/checks/{npm-audit.js → dependencies/npm-audit.js} +3 -3
- package/dist/core/checks/{outdated-deps.d.ts → dependencies/outdated-deps.d.ts} +1 -1
- package/dist/core/checks/{outdated-deps.js → dependencies/outdated-deps.js} +3 -3
- package/dist/core/checks/dependencies/vuln-scan.d.ts +9 -0
- package/dist/core/checks/dependencies/vuln-scan.js +41 -0
- package/dist/core/checks/findings.d.ts +37 -1
- package/dist/core/checks/findings.js +80 -3
- package/dist/core/checks/git/custom-rules.d.ts +2 -0
- package/dist/core/checks/git/custom-rules.js +102 -0
- package/dist/core/checks/{diff-smell.d.ts → git/diff-smell.d.ts} +1 -1
- package/dist/core/checks/{diff-smell.js → git/diff-smell.js} +3 -3
- package/dist/core/checks/git/large-files.d.ts +2 -0
- package/dist/core/checks/git/large-files.js +83 -0
- package/dist/core/checks/git/leftover-debug.d.ts +2 -0
- package/dist/core/checks/git/leftover-debug.js +77 -0
- package/dist/core/checks/git/lockfile-drift.d.ts +2 -0
- package/dist/core/checks/git/lockfile-drift.js +114 -0
- package/dist/core/checks/git/merge-conflict.d.ts +2 -0
- package/dist/core/checks/git/merge-conflict.js +109 -0
- package/dist/core/checks/git/missing-tests.d.ts +2 -0
- package/dist/core/checks/git/missing-tests.js +92 -0
- package/dist/core/checks/git/todo-fixme.d.ts +7 -0
- package/dist/core/checks/git/todo-fixme.js +74 -0
- package/dist/core/checks/{cpp-format.d.ts → languages/cpp-format.d.ts} +1 -1
- package/dist/core/checks/{cpp-format.js → languages/cpp-format.js} +3 -3
- package/dist/core/checks/languages/csharp-format.d.ts +13 -0
- package/dist/core/checks/languages/csharp-format.js +52 -0
- package/dist/core/checks/languages/go-format.d.ts +8 -0
- package/dist/core/checks/languages/go-format.js +48 -0
- package/dist/core/checks/languages/go-lint.d.ts +8 -0
- package/dist/core/checks/languages/go-lint.js +48 -0
- package/dist/core/checks/{kotlin-lint.d.ts → languages/kotlin-lint.d.ts} +1 -1
- package/dist/core/checks/{kotlin-lint.js → languages/kotlin-lint.js} +13 -17
- package/dist/core/checks/{lint.d.ts → languages/lint.d.ts} +1 -1
- package/dist/core/checks/{lint.js → languages/lint.js} +5 -5
- package/dist/core/checks/languages/php-format.d.ts +9 -0
- package/dist/core/checks/languages/php-format.js +49 -0
- package/dist/core/checks/{prettier.d.ts → languages/prettier.d.ts} +1 -1
- package/dist/core/checks/{prettier.js → languages/prettier.js} +5 -5
- package/dist/core/checks/{python-format.d.ts → languages/python-format.d.ts} +1 -1
- package/dist/core/checks/{python-format.js → languages/python-format.js} +3 -3
- package/dist/core/checks/{python-lint.d.ts → languages/python-lint.d.ts} +1 -1
- package/dist/core/checks/{python-lint.js → languages/python-lint.js} +3 -3
- package/dist/core/checks/languages/python-typecheck.d.ts +9 -0
- package/dist/core/checks/languages/python-typecheck.js +66 -0
- package/dist/core/checks/languages/ruby-lint.d.ts +8 -0
- package/dist/core/checks/languages/ruby-lint.js +50 -0
- package/dist/core/checks/languages/rust-format.d.ts +9 -0
- package/dist/core/checks/languages/rust-format.js +45 -0
- package/dist/core/checks/languages/rust-lint.d.ts +8 -0
- package/dist/core/checks/languages/rust-lint.js +50 -0
- package/dist/core/checks/{swift-lint.d.ts → languages/swift-lint.d.ts} +1 -1
- package/dist/core/checks/{swift-lint.js → languages/swift-lint.js} +13 -17
- package/dist/core/checks/{typecheck.d.ts → languages/typecheck.d.ts} +1 -1
- package/dist/core/checks/{typecheck.js → languages/typecheck.js} +3 -3
- package/dist/core/checks/{commitlint.d.ts → pr/commitlint.d.ts} +1 -1
- package/dist/core/checks/{commitlint.js → pr/commitlint.js} +4 -4
- package/dist/core/checks/{pr-body.d.ts → pr/pr-body.d.ts} +1 -1
- package/dist/core/checks/{pr-body.js → pr/pr-body.js} +2 -2
- package/dist/core/checks/{pr-size-check.d.ts → pr/pr-size-check.d.ts} +1 -1
- package/dist/core/checks/{pr-size-check.js → pr/pr-size-check.js} +13 -11
- package/dist/core/checks/{pr-title-lint.d.ts → pr/pr-title-lint.d.ts} +1 -1
- package/dist/core/checks/{pr-title-lint.js → pr/pr-title-lint.js} +2 -2
- package/dist/core/checks/quality/case-collision.d.ts +8 -0
- package/dist/core/checks/quality/case-collision.js +40 -0
- package/dist/core/checks/quality/config-validation.d.ts +8 -0
- package/dist/core/checks/quality/config-validation.js +63 -0
- package/dist/core/checks/quality/coverage.d.ts +2 -0
- package/dist/core/checks/quality/coverage.js +63 -0
- package/dist/core/checks/{dead-code.d.ts → quality/dead-code.d.ts} +1 -1
- package/dist/core/checks/{dead-code.js → quality/dead-code.js} +4 -4
- package/dist/core/checks/{duplicate-check.d.ts → quality/duplicate-check.d.ts} +1 -1
- package/dist/core/checks/{duplicate-check.js → quality/duplicate-check.js} +6 -6
- package/dist/core/checks/quality/license-header.d.ts +7 -0
- package/dist/core/checks/quality/license-header.js +74 -0
- package/dist/core/checks/{markdown-lint.d.ts → quality/markdown-lint.d.ts} +1 -1
- package/dist/core/checks/{markdown-lint.js → quality/markdown-lint.js} +5 -5
- package/dist/core/checks/{spellcheck.d.ts → quality/spellcheck.d.ts} +1 -1
- package/dist/core/checks/{spellcheck.js → quality/spellcheck.js} +5 -5
- package/dist/core/checks/{diff-security.d.ts → security/diff-security.d.ts} +1 -1
- package/dist/core/checks/security/diff-security.js +262 -0
- package/dist/core/checks/security/dockerfile-security.d.ts +2 -0
- package/dist/core/checks/security/dockerfile-security.js +92 -0
- package/dist/core/checks/security/migration-safety.d.ts +2 -0
- package/dist/core/checks/security/migration-safety.js +110 -0
- package/dist/core/checks/{security.d.ts → security/security.d.ts} +1 -1
- package/dist/core/checks/{security.js → security/security.js} +4 -4
- package/dist/core/checks/{sensitive-file-guard.d.ts → security/sensitive-file-guard.d.ts} +1 -1
- package/dist/core/checks/{sensitive-file-guard.js → security/sensitive-file-guard.js} +4 -4
- package/dist/core/checks/security/workflow-security.d.ts +2 -0
- package/dist/core/checks/security/workflow-security.js +75 -0
- package/dist/core/context.js +14 -0
- package/dist/core/registry.js +73 -28
- package/dist/core/reporters.d.ts +4 -3
- package/dist/core/reporters.js +104 -4
- package/dist/core/run-checks.js +21 -3
- package/dist/core/targets.d.ts +4 -1
- package/dist/core/targets.js +16 -3
- package/dist/core/types.d.ts +13 -1
- package/dist/init.js +63 -0
- package/dist/interfaces/index.d.ts +99 -0
- package/dist/scripts/copy-configs.js +1 -0
- package/dist/utils/detect-language.d.ts +1 -1
- package/dist/utils/detect-language.js +32 -3
- package/dist/utils/glob.d.ts +10 -0
- package/dist/utils/glob.js +25 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/package.json +2 -1
- package/pr-checkmate.schema.json +166 -0
- package/src/config/cspell.json +9 -1
- package/src/config/pr-checkmate.schema.json +166 -0
- package/dist/core/checks/diff-security.js +0 -145
- /package/dist/core/checks/{ruff-shared.d.ts → languages/ruff-shared.d.ts} +0 -0
- /package/dist/core/checks/{ruff-shared.js → languages/ruff-shared.js} +0 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.goLintCheck = void 0;
|
|
4
|
+
const utils_1 = require("../../../utils");
|
|
5
|
+
const execa_1 = require("../../../utils/execa");
|
|
6
|
+
const targets_1 = require("../../targets");
|
|
7
|
+
const types_1 = require("../../types");
|
|
8
|
+
const GO_GLOBS = ['*.go'];
|
|
9
|
+
/**
|
|
10
|
+
* Vets changed Go code with `go vet ./...`. Runner-dependency: requires the Go
|
|
11
|
+
* toolchain on the runner (like SwiftLint/ktlint), and gracefully skips when
|
|
12
|
+
* `go` is absent. `go vet` operates per-package, so it runs over the module
|
|
13
|
+
* rather than a file list; the delta check only gates whether it runs at all.
|
|
14
|
+
*/
|
|
15
|
+
exports.goLintCheck = {
|
|
16
|
+
name: 'Go Vet',
|
|
17
|
+
phase: 'informational',
|
|
18
|
+
applies(ctx) {
|
|
19
|
+
return ctx.config.go?.enabled !== false && ctx.languages.includes('go');
|
|
20
|
+
},
|
|
21
|
+
async run(ctx) {
|
|
22
|
+
const targets = await (0, targets_1.resolveTargetFiles)(ctx, GO_GLOBS);
|
|
23
|
+
if (targets === null)
|
|
24
|
+
return (0, types_1.skip)('git unavailable');
|
|
25
|
+
if (targets.length === 0)
|
|
26
|
+
return (0, types_1.pass)('no Go files changed');
|
|
27
|
+
const result = await (0, execa_1.execa)('go', ['vet', './...'], { cwd: ctx.cwd, reject: false });
|
|
28
|
+
// execa v9 with reject:false does NOT throw on a missing binary — it resolves
|
|
29
|
+
// with `code: 'ENOENT'`. Detect that on the result, not via try/catch.
|
|
30
|
+
if (result.code === 'ENOENT')
|
|
31
|
+
return (0, types_1.skip)('go not installed');
|
|
32
|
+
if (result.exitCode === 0) {
|
|
33
|
+
utils_1.logger.info('[Go Vet]: ✅ No issues found');
|
|
34
|
+
return (0, types_1.pass)();
|
|
35
|
+
}
|
|
36
|
+
// go vet writes diagnostics (and build errors) to stderr.
|
|
37
|
+
const lines = [result.stderr, result.stdout]
|
|
38
|
+
.filter(Boolean)
|
|
39
|
+
.join('\n')
|
|
40
|
+
.split('\n')
|
|
41
|
+
.filter(Boolean);
|
|
42
|
+
utils_1.logger.warn(`[Go Vet]: ⚠️ ${lines.length} line(s) of diagnostics:`);
|
|
43
|
+
lines.slice(0, 10).forEach(l => utils_1.logger.warn(` ${l}`));
|
|
44
|
+
if (lines.length > 10)
|
|
45
|
+
utils_1.logger.warn(` ... and ${lines.length - 10} more`);
|
|
46
|
+
return (0, types_1.warn)(`${lines.length} diagnostic line(s)`);
|
|
47
|
+
},
|
|
48
|
+
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Check } from '
|
|
1
|
+
import { Check } from '../../types';
|
|
2
2
|
export declare const kotlinLintCheck: Check;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.kotlinLintCheck = void 0;
|
|
4
|
-
const utils_1 = require("
|
|
5
|
-
const execa_1 = require("
|
|
6
|
-
const targets_1 = require("
|
|
7
|
-
const types_1 = require("
|
|
4
|
+
const utils_1 = require("../../../utils");
|
|
5
|
+
const execa_1 = require("../../../utils/execa");
|
|
6
|
+
const targets_1 = require("../../targets");
|
|
7
|
+
const types_1 = require("../../types");
|
|
8
8
|
const KOTLIN_GLOBS = ['*.kt', '*.kts'];
|
|
9
9
|
exports.kotlinLintCheck = {
|
|
10
10
|
name: 'ktlint',
|
|
@@ -18,19 +18,15 @@ exports.kotlinLintCheck = {
|
|
|
18
18
|
return (0, types_1.skip)('git unavailable');
|
|
19
19
|
if (targets.length === 0)
|
|
20
20
|
return (0, types_1.pass)('no Kotlin files changed');
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return (0, types_1.skip)('ktlint not installed');
|
|
31
|
-
}
|
|
32
|
-
throw err;
|
|
33
|
-
}
|
|
21
|
+
const result = await (0, execa_1.execa)('ktlint', ['--reporter=plain', ...targets], {
|
|
22
|
+
cwd: ctx.cwd,
|
|
23
|
+
reject: false,
|
|
24
|
+
});
|
|
25
|
+
// execa v9 with reject:false does NOT throw on a missing binary — it resolves
|
|
26
|
+
// with `code: 'ENOENT'`. Detect the absent toolchain on the result, not via a
|
|
27
|
+
// try/catch (which never fires here and would let a missing linter pass silently).
|
|
28
|
+
if (result.code === 'ENOENT')
|
|
29
|
+
return (0, types_1.skip)('ktlint not installed');
|
|
34
30
|
const output = [result.stdout, result.stderr].filter(Boolean).join('\n').trim();
|
|
35
31
|
if (result.exitCode === 0 || !output) {
|
|
36
32
|
utils_1.logger.info('[ktlint]: ✅ No issues found');
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Check } from '
|
|
1
|
+
import { Check } from '../../types';
|
|
2
2
|
export declare const lintCheck: Check;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.lintCheck = void 0;
|
|
4
|
-
const config_1 = require("
|
|
5
|
-
const utils_1 = require("
|
|
6
|
-
const execa_1 = require("
|
|
7
|
-
const targets_1 = require("
|
|
8
|
-
const types_1 = require("
|
|
4
|
+
const config_1 = require("../../../config");
|
|
5
|
+
const utils_1 = require("../../../utils");
|
|
6
|
+
const execa_1 = require("../../../utils/execa");
|
|
7
|
+
const targets_1 = require("../../targets");
|
|
8
|
+
const types_1 = require("../../types");
|
|
9
9
|
/**
|
|
10
10
|
* Resolve the ESLint binary, preferring the consumer project's own copy so a
|
|
11
11
|
* repo pinned to ESLint 8 (+ legacy `.eslintrc.*`) keeps working. Falls back to
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Check } from '../../types';
|
|
2
|
+
/**
|
|
3
|
+
* Formats changed PHP files with PHP-CS-Fixer. Runner-dependency: needs
|
|
4
|
+
* `php-cs-fixer` on the runner, gracefully skipping when absent. Read-only in
|
|
5
|
+
* check mode (`--dry-run`); rewrites in place when `write`, so CI can
|
|
6
|
+
* auto-commit — mirrors the other format checks. Uses the repo's own
|
|
7
|
+
* `.php-cs-fixer.php` config when present.
|
|
8
|
+
*/
|
|
9
|
+
export declare const phpFormatCheck: Check;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.phpFormatCheck = void 0;
|
|
4
|
+
const utils_1 = require("../../../utils");
|
|
5
|
+
const execa_1 = require("../../../utils/execa");
|
|
6
|
+
const targets_1 = require("../../targets");
|
|
7
|
+
const types_1 = require("../../types");
|
|
8
|
+
const PHP_GLOBS = ['*.php'];
|
|
9
|
+
/**
|
|
10
|
+
* Formats changed PHP files with PHP-CS-Fixer. Runner-dependency: needs
|
|
11
|
+
* `php-cs-fixer` on the runner, gracefully skipping when absent. Read-only in
|
|
12
|
+
* check mode (`--dry-run`); rewrites in place when `write`, so CI can
|
|
13
|
+
* auto-commit — mirrors the other format checks. Uses the repo's own
|
|
14
|
+
* `.php-cs-fixer.php` config when present.
|
|
15
|
+
*/
|
|
16
|
+
exports.phpFormatCheck = {
|
|
17
|
+
name: 'PHP CS Fixer',
|
|
18
|
+
phase: 'format',
|
|
19
|
+
applies(ctx) {
|
|
20
|
+
return ctx.config.php?.enabled !== false && ctx.languages.includes('php');
|
|
21
|
+
},
|
|
22
|
+
async run(ctx) {
|
|
23
|
+
const targets = await (0, targets_1.resolveTargetFiles)(ctx, PHP_GLOBS);
|
|
24
|
+
if (targets === null)
|
|
25
|
+
return (0, types_1.skip)('git unavailable');
|
|
26
|
+
if (targets.length === 0)
|
|
27
|
+
return (0, types_1.pass)('no PHP files changed');
|
|
28
|
+
const baseArgs = ['fix', '--using-cache=no', ...targets];
|
|
29
|
+
const dry = await (0, execa_1.execa)('php-cs-fixer', [...baseArgs, '--dry-run'], {
|
|
30
|
+
cwd: ctx.cwd,
|
|
31
|
+
reject: false,
|
|
32
|
+
});
|
|
33
|
+
if (dry.code === 'ENOENT')
|
|
34
|
+
return (0, types_1.skip)('php-cs-fixer not installed');
|
|
35
|
+
if (dry.exitCode === 0)
|
|
36
|
+
return (0, types_1.pass)();
|
|
37
|
+
if (!ctx.write) {
|
|
38
|
+
utils_1.logger.warn('[PHP CS Fixer]: ⚠️ PHP code needs formatting');
|
|
39
|
+
return (0, types_1.warn)('PHP code needs formatting (run with write to fix)');
|
|
40
|
+
}
|
|
41
|
+
const fix = await (0, execa_1.execa)('php-cs-fixer', baseArgs, { cwd: ctx.cwd, reject: false });
|
|
42
|
+
if (fix.exitCode !== 0) {
|
|
43
|
+
utils_1.logger.warn(`[PHP CS Fixer]: ⚠️ php-cs-fixer failed\n${fix.stderr}`);
|
|
44
|
+
return (0, types_1.warn)('php-cs-fixer failed to rewrite files');
|
|
45
|
+
}
|
|
46
|
+
utils_1.logger.info('[PHP CS Fixer]: ✅ Reformatted PHP code');
|
|
47
|
+
return (0, types_1.pass)('reformatted PHP code');
|
|
48
|
+
},
|
|
49
|
+
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Check } from '
|
|
1
|
+
import { Check } from '../../types';
|
|
2
2
|
export declare const prettierCheck: Check;
|
|
@@ -6,11 +6,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.prettierCheck = void 0;
|
|
7
7
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
8
|
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
-
const config_1 = require("
|
|
10
|
-
const utils_1 = require("
|
|
11
|
-
const execa_1 = require("
|
|
12
|
-
const targets_1 = require("
|
|
13
|
-
const types_1 = require("
|
|
9
|
+
const config_1 = require("../../../config");
|
|
10
|
+
const utils_1 = require("../../../utils");
|
|
11
|
+
const execa_1 = require("../../../utils/execa");
|
|
12
|
+
const targets_1 = require("../../targets");
|
|
13
|
+
const types_1 = require("../../types");
|
|
14
14
|
function loadUserPrettierConfig(cwd) {
|
|
15
15
|
const configPath = node_path_1.default.join(cwd, 'pr-checkmate.json');
|
|
16
16
|
if (!node_fs_1.default.existsSync(configPath))
|
|
@@ -6,9 +6,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.pythonFormatCheck = void 0;
|
|
7
7
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
8
|
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
-
const utils_1 = require("
|
|
10
|
-
const targets_1 = require("
|
|
11
|
-
const types_1 = require("
|
|
9
|
+
const utils_1 = require("../../../utils");
|
|
10
|
+
const targets_1 = require("../../targets");
|
|
11
|
+
const types_1 = require("../../types");
|
|
12
12
|
const ruff_shared_1 = require("./ruff-shared");
|
|
13
13
|
/**
|
|
14
14
|
* Formats changed Python files with Ruff's formatter (WASM, no Python/pip needed).
|
|
@@ -6,9 +6,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.pythonLintCheck = void 0;
|
|
7
7
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
8
|
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
-
const utils_1 = require("
|
|
10
|
-
const targets_1 = require("
|
|
11
|
-
const types_1 = require("
|
|
9
|
+
const utils_1 = require("../../../utils");
|
|
10
|
+
const targets_1 = require("../../targets");
|
|
11
|
+
const types_1 = require("../../types");
|
|
12
12
|
const ruff_shared_1 = require("./ruff-shared");
|
|
13
13
|
/**
|
|
14
14
|
* Lints changed Python files with Ruff, bundled as a self-contained WASM module
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Check } from '../../types';
|
|
2
|
+
/**
|
|
3
|
+
* Type-checks changed Python files — the Python analogue of the TypeScript
|
|
4
|
+
* `tsc --noEmit` check. Runner-dependency (needs mypy or pyright on the runner),
|
|
5
|
+
* gracefully skipping when neither is present. Informational by design (like
|
|
6
|
+
* Ruff lint) so teams can adopt gradually; force a checker with
|
|
7
|
+
* `python.typeChecker`, else mypy is tried first, then pyright.
|
|
8
|
+
*/
|
|
9
|
+
export declare const pythonTypecheckCheck: Check;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.pythonTypecheckCheck = void 0;
|
|
4
|
+
const utils_1 = require("../../../utils");
|
|
5
|
+
const execa_1 = require("../../../utils/execa");
|
|
6
|
+
const targets_1 = require("../../targets");
|
|
7
|
+
const types_1 = require("../../types");
|
|
8
|
+
const ruff_shared_1 = require("./ruff-shared");
|
|
9
|
+
/** Run a type checker over the target files, or return null if the binary is absent. */
|
|
10
|
+
async function runChecker(ctx, bin, targets) {
|
|
11
|
+
const result = await (0, execa_1.execa)(bin, targets, { cwd: ctx.cwd, reject: false });
|
|
12
|
+
if (result.code === 'ENOENT')
|
|
13
|
+
return null;
|
|
14
|
+
return { exitCode: result.exitCode, stdout: result.stdout, stderr: result.stderr };
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Type-checks changed Python files — the Python analogue of the TypeScript
|
|
18
|
+
* `tsc --noEmit` check. Runner-dependency (needs mypy or pyright on the runner),
|
|
19
|
+
* gracefully skipping when neither is present. Informational by design (like
|
|
20
|
+
* Ruff lint) so teams can adopt gradually; force a checker with
|
|
21
|
+
* `python.typeChecker`, else mypy is tried first, then pyright.
|
|
22
|
+
*/
|
|
23
|
+
exports.pythonTypecheckCheck = {
|
|
24
|
+
name: 'Python Types',
|
|
25
|
+
phase: 'informational',
|
|
26
|
+
applies(ctx) {
|
|
27
|
+
return (ctx.config.python?.enabled !== false &&
|
|
28
|
+
ctx.config.python?.typecheck !== false &&
|
|
29
|
+
ctx.languages.includes('python'));
|
|
30
|
+
},
|
|
31
|
+
async run(ctx) {
|
|
32
|
+
const targets = await (0, targets_1.resolveTargetFiles)(ctx, ruff_shared_1.PY_GLOBS);
|
|
33
|
+
if (targets === null)
|
|
34
|
+
return (0, types_1.skip)('git unavailable');
|
|
35
|
+
if (targets.length === 0)
|
|
36
|
+
return (0, types_1.pass)('no Python files changed');
|
|
37
|
+
const forced = ctx.config.python?.typeChecker;
|
|
38
|
+
const order = forced ? [forced] : ['mypy', 'pyright'];
|
|
39
|
+
let result = null;
|
|
40
|
+
let used = '';
|
|
41
|
+
for (const bin of order) {
|
|
42
|
+
result = await runChecker(ctx, bin, targets);
|
|
43
|
+
if (result) {
|
|
44
|
+
used = bin;
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (!result)
|
|
49
|
+
return (0, types_1.skip)(`${forced ?? 'mypy/pyright'} not installed`);
|
|
50
|
+
if (result.exitCode === 0) {
|
|
51
|
+
utils_1.logger.info(`[Python Types]: ✅ No type errors (${used})`);
|
|
52
|
+
return (0, types_1.pass)();
|
|
53
|
+
}
|
|
54
|
+
const lines = [result.stdout, result.stderr]
|
|
55
|
+
.filter(Boolean)
|
|
56
|
+
.join('\n')
|
|
57
|
+
.split('\n')
|
|
58
|
+
.filter(l => /\berror\b|\bwarning\b/i.test(l));
|
|
59
|
+
utils_1.logger.warn(`[Python Types]: ⚠️ type issues (${used}):`);
|
|
60
|
+
lines.slice(0, 10).forEach(l => utils_1.logger.warn(` ${l}`));
|
|
61
|
+
if (lines.length > 10)
|
|
62
|
+
utils_1.logger.warn(` ... and ${lines.length - 10} more`);
|
|
63
|
+
const summary = lines.find(l => /found\s+\d+\s+error/i.test(l)) ?? `${lines.length} type issue(s)`;
|
|
64
|
+
return (0, types_1.warn)(summary.trim());
|
|
65
|
+
},
|
|
66
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Check } from '../../types';
|
|
2
|
+
/**
|
|
3
|
+
* Lints changed Ruby files with RuboCop (which covers both style and layout, so
|
|
4
|
+
* it doubles as the formatting signal). Runner-dependency: needs `rubocop` on
|
|
5
|
+
* the runner, gracefully skipping when absent. Uses the repo's own `.rubocop.yml`
|
|
6
|
+
* when present.
|
|
7
|
+
*/
|
|
8
|
+
export declare const rubyLintCheck: Check;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.rubyLintCheck = void 0;
|
|
4
|
+
const utils_1 = require("../../../utils");
|
|
5
|
+
const execa_1 = require("../../../utils/execa");
|
|
6
|
+
const targets_1 = require("../../targets");
|
|
7
|
+
const types_1 = require("../../types");
|
|
8
|
+
const RUBY_GLOBS = ['*.rb', '*.rake'];
|
|
9
|
+
/**
|
|
10
|
+
* Lints changed Ruby files with RuboCop (which covers both style and layout, so
|
|
11
|
+
* it doubles as the formatting signal). Runner-dependency: needs `rubocop` on
|
|
12
|
+
* the runner, gracefully skipping when absent. Uses the repo's own `.rubocop.yml`
|
|
13
|
+
* when present.
|
|
14
|
+
*/
|
|
15
|
+
exports.rubyLintCheck = {
|
|
16
|
+
name: 'RuboCop',
|
|
17
|
+
phase: 'informational',
|
|
18
|
+
applies(ctx) {
|
|
19
|
+
return ctx.config.ruby?.enabled !== false && ctx.languages.includes('ruby');
|
|
20
|
+
},
|
|
21
|
+
async run(ctx) {
|
|
22
|
+
const targets = await (0, targets_1.resolveTargetFiles)(ctx, RUBY_GLOBS);
|
|
23
|
+
if (targets === null)
|
|
24
|
+
return (0, types_1.skip)('git unavailable');
|
|
25
|
+
if (targets.length === 0)
|
|
26
|
+
return (0, types_1.pass)('no Ruby files changed');
|
|
27
|
+
const result = await (0, execa_1.execa)('rubocop', ['--format', 'simple', ...targets], {
|
|
28
|
+
cwd: ctx.cwd,
|
|
29
|
+
reject: false,
|
|
30
|
+
});
|
|
31
|
+
if (result.code === 'ENOENT')
|
|
32
|
+
return (0, types_1.skip)('rubocop not installed');
|
|
33
|
+
if (result.exitCode === 0) {
|
|
34
|
+
utils_1.logger.info('[RuboCop]: ✅ No offenses found');
|
|
35
|
+
return (0, types_1.pass)();
|
|
36
|
+
}
|
|
37
|
+
const lines = [result.stdout, result.stderr]
|
|
38
|
+
.filter(Boolean)
|
|
39
|
+
.join('\n')
|
|
40
|
+
.split('\n')
|
|
41
|
+
.filter(l => l.trim());
|
|
42
|
+
utils_1.logger.warn('[RuboCop]: ⚠️ offenses detected:');
|
|
43
|
+
lines.slice(0, 10).forEach(l => utils_1.logger.warn(` ${l}`));
|
|
44
|
+
if (lines.length > 10)
|
|
45
|
+
utils_1.logger.warn(` ... and ${lines.length - 10} more line(s)`);
|
|
46
|
+
// RuboCop's summary line looks like "N offenses detected".
|
|
47
|
+
const summary = lines.find(l => /offense/i.test(l)) ?? `${lines.length} line(s) of output`;
|
|
48
|
+
return (0, types_1.warn)(summary.trim());
|
|
49
|
+
},
|
|
50
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Check } from '../../types';
|
|
2
|
+
/**
|
|
3
|
+
* Formats changed Rust code with `cargo fmt` (which respects the crate's edition
|
|
4
|
+
* from Cargo.toml, unlike a bare `rustfmt`). Runner-dependency, gracefully
|
|
5
|
+
* skipping when `cargo` is absent. Read-only in check mode (`cargo fmt --check`);
|
|
6
|
+
* rewrites in place with `cargo fmt` when `write`, so CI can auto-commit —
|
|
7
|
+
* mirrors the Prettier/C++/Go format checks.
|
|
8
|
+
*/
|
|
9
|
+
export declare const rustFormatCheck: Check;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.rustFormatCheck = void 0;
|
|
4
|
+
const utils_1 = require("../../../utils");
|
|
5
|
+
const execa_1 = require("../../../utils/execa");
|
|
6
|
+
const targets_1 = require("../../targets");
|
|
7
|
+
const types_1 = require("../../types");
|
|
8
|
+
const RUST_GLOBS = ['*.rs'];
|
|
9
|
+
/**
|
|
10
|
+
* Formats changed Rust code with `cargo fmt` (which respects the crate's edition
|
|
11
|
+
* from Cargo.toml, unlike a bare `rustfmt`). Runner-dependency, gracefully
|
|
12
|
+
* skipping when `cargo` is absent. Read-only in check mode (`cargo fmt --check`);
|
|
13
|
+
* rewrites in place with `cargo fmt` when `write`, so CI can auto-commit —
|
|
14
|
+
* mirrors the Prettier/C++/Go format checks.
|
|
15
|
+
*/
|
|
16
|
+
exports.rustFormatCheck = {
|
|
17
|
+
name: 'Rustfmt',
|
|
18
|
+
phase: 'format',
|
|
19
|
+
applies(ctx) {
|
|
20
|
+
return ctx.config.rust?.enabled !== false && ctx.languages.includes('rust');
|
|
21
|
+
},
|
|
22
|
+
async run(ctx) {
|
|
23
|
+
const targets = await (0, targets_1.resolveTargetFiles)(ctx, RUST_GLOBS);
|
|
24
|
+
if (targets === null)
|
|
25
|
+
return (0, types_1.skip)('git unavailable');
|
|
26
|
+
if (targets.length === 0)
|
|
27
|
+
return (0, types_1.pass)('no Rust files changed');
|
|
28
|
+
const check = await (0, execa_1.execa)('cargo', ['fmt', '--check'], { cwd: ctx.cwd, reject: false });
|
|
29
|
+
if (check.code === 'ENOENT')
|
|
30
|
+
return (0, types_1.skip)('cargo not installed');
|
|
31
|
+
if (check.exitCode === 0)
|
|
32
|
+
return (0, types_1.pass)();
|
|
33
|
+
if (!ctx.write) {
|
|
34
|
+
utils_1.logger.warn('[Rustfmt]: ⚠️ Rust code is not formatted (cargo fmt --check failed)');
|
|
35
|
+
return (0, types_1.warn)('Rust code needs formatting (run with write to fix)');
|
|
36
|
+
}
|
|
37
|
+
const fix = await (0, execa_1.execa)('cargo', ['fmt'], { cwd: ctx.cwd, reject: false });
|
|
38
|
+
if (fix.exitCode !== 0) {
|
|
39
|
+
utils_1.logger.warn(`[Rustfmt]: ⚠️ cargo fmt failed\n${fix.stderr}`);
|
|
40
|
+
return (0, types_1.warn)('cargo fmt failed to rewrite files');
|
|
41
|
+
}
|
|
42
|
+
utils_1.logger.info('[Rustfmt]: ✅ Reformatted Rust code');
|
|
43
|
+
return (0, types_1.pass)('reformatted Rust code');
|
|
44
|
+
},
|
|
45
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Check } from '../../types';
|
|
2
|
+
/**
|
|
3
|
+
* Lints changed Rust code with Clippy (`cargo clippy -- -D warnings`).
|
|
4
|
+
* Runner-dependency: needs the Rust toolchain + clippy on the runner, and
|
|
5
|
+
* gracefully skips when absent. Clippy compiles the whole crate, so it runs over
|
|
6
|
+
* the crate rather than a file list; the delta check only gates whether it runs.
|
|
7
|
+
*/
|
|
8
|
+
export declare const rustLintCheck: Check;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.rustLintCheck = void 0;
|
|
4
|
+
const utils_1 = require("../../../utils");
|
|
5
|
+
const execa_1 = require("../../../utils/execa");
|
|
6
|
+
const targets_1 = require("../../targets");
|
|
7
|
+
const types_1 = require("../../types");
|
|
8
|
+
const RUST_GLOBS = ['*.rs'];
|
|
9
|
+
/**
|
|
10
|
+
* Lints changed Rust code with Clippy (`cargo clippy -- -D warnings`).
|
|
11
|
+
* Runner-dependency: needs the Rust toolchain + clippy on the runner, and
|
|
12
|
+
* gracefully skips when absent. Clippy compiles the whole crate, so it runs over
|
|
13
|
+
* the crate rather than a file list; the delta check only gates whether it runs.
|
|
14
|
+
*/
|
|
15
|
+
exports.rustLintCheck = {
|
|
16
|
+
name: 'Clippy',
|
|
17
|
+
phase: 'informational',
|
|
18
|
+
applies(ctx) {
|
|
19
|
+
return ctx.config.rust?.enabled !== false && ctx.languages.includes('rust');
|
|
20
|
+
},
|
|
21
|
+
async run(ctx) {
|
|
22
|
+
const targets = await (0, targets_1.resolveTargetFiles)(ctx, RUST_GLOBS);
|
|
23
|
+
if (targets === null)
|
|
24
|
+
return (0, types_1.skip)('git unavailable');
|
|
25
|
+
if (targets.length === 0)
|
|
26
|
+
return (0, types_1.pass)('no Rust files changed');
|
|
27
|
+
const result = await (0, execa_1.execa)('cargo', ['clippy', '--quiet', '--', '-D', 'warnings'], {
|
|
28
|
+
cwd: ctx.cwd,
|
|
29
|
+
reject: false,
|
|
30
|
+
});
|
|
31
|
+
// execa v9 with reject:false resolves (not throws) with `code: 'ENOENT'` when
|
|
32
|
+
// the binary is absent — detect the missing toolchain on the result.
|
|
33
|
+
if (result.code === 'ENOENT')
|
|
34
|
+
return (0, types_1.skip)('cargo/clippy not installed');
|
|
35
|
+
if (result.exitCode === 0) {
|
|
36
|
+
utils_1.logger.info('[Clippy]: ✅ No issues found');
|
|
37
|
+
return (0, types_1.pass)();
|
|
38
|
+
}
|
|
39
|
+
// Clippy writes diagnostics to stderr.
|
|
40
|
+
const lines = result.stderr
|
|
41
|
+
.split('\n')
|
|
42
|
+
.filter(l => /\b(warning|error)\b/.test(l))
|
|
43
|
+
.filter(Boolean);
|
|
44
|
+
utils_1.logger.warn(`[Clippy]: ⚠️ ${lines.length} diagnostic line(s):`);
|
|
45
|
+
lines.slice(0, 10).forEach(l => utils_1.logger.warn(` ${l}`));
|
|
46
|
+
if (lines.length > 10)
|
|
47
|
+
utils_1.logger.warn(` ... and ${lines.length - 10} more`);
|
|
48
|
+
return (0, types_1.warn)(`${lines.length || 'unknown'} clippy diagnostic(s)`);
|
|
49
|
+
},
|
|
50
|
+
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Check } from '
|
|
1
|
+
import { Check } from '../../types';
|
|
2
2
|
export declare const swiftLintCheck: Check;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.swiftLintCheck = void 0;
|
|
4
|
-
const utils_1 = require("
|
|
5
|
-
const execa_1 = require("
|
|
6
|
-
const targets_1 = require("
|
|
7
|
-
const types_1 = require("
|
|
4
|
+
const utils_1 = require("../../../utils");
|
|
5
|
+
const execa_1 = require("../../../utils/execa");
|
|
6
|
+
const targets_1 = require("../../targets");
|
|
7
|
+
const types_1 = require("../../types");
|
|
8
8
|
const SWIFT_GLOBS = ['*.swift'];
|
|
9
9
|
exports.swiftLintCheck = {
|
|
10
10
|
name: 'SwiftLint',
|
|
@@ -18,19 +18,15 @@ exports.swiftLintCheck = {
|
|
|
18
18
|
return (0, types_1.skip)('git unavailable');
|
|
19
19
|
if (targets.length === 0)
|
|
20
20
|
return (0, types_1.pass)('no Swift files changed');
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return (0, types_1.skip)('swiftlint not installed');
|
|
31
|
-
}
|
|
32
|
-
throw err;
|
|
33
|
-
}
|
|
21
|
+
const result = await (0, execa_1.execa)('swiftlint', ['lint', '--quiet', '--path', ctx.cwd], {
|
|
22
|
+
cwd: ctx.cwd,
|
|
23
|
+
reject: false,
|
|
24
|
+
});
|
|
25
|
+
// execa v9 with reject:false does NOT throw on a missing binary — it resolves
|
|
26
|
+
// with `code: 'ENOENT'`. Detect the absent toolchain on the result, not via a
|
|
27
|
+
// try/catch (which never fires here and would let a missing linter pass silently).
|
|
28
|
+
if (result.code === 'ENOENT')
|
|
29
|
+
return (0, types_1.skip)('swiftlint not installed');
|
|
34
30
|
const output = [result.stdout, result.stderr].filter(Boolean).join('\n').trim();
|
|
35
31
|
if (result.exitCode === 0 || !output) {
|
|
36
32
|
utils_1.logger.info('[SwiftLint]: ✅ No issues found');
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Check } from '
|
|
1
|
+
import { Check } from '../../types';
|
|
2
2
|
export declare const typecheckCheck: Check;
|
|
@@ -6,9 +6,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.typecheckCheck = void 0;
|
|
7
7
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
8
|
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
-
const utils_1 = require("
|
|
10
|
-
const execa_1 = require("
|
|
11
|
-
const types_1 = require("
|
|
9
|
+
const utils_1 = require("../../../utils");
|
|
10
|
+
const execa_1 = require("../../../utils/execa");
|
|
11
|
+
const types_1 = require("../../types");
|
|
12
12
|
/**
|
|
13
13
|
* Prefer the consumer project's own TypeScript so its tsconfig is compiled by the
|
|
14
14
|
* version it was written for; fall back to the compiler bundled with pr-checkmate.
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Check } from '
|
|
1
|
+
import { Check } from '../../types';
|
|
2
2
|
export declare const commitlintCheck: Check;
|
|
@@ -7,10 +7,10 @@ exports.commitlintCheck = void 0;
|
|
|
7
7
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
8
|
const node_os_1 = __importDefault(require("node:os"));
|
|
9
9
|
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
-
const config_1 = require("
|
|
11
|
-
const utils_1 = require("
|
|
12
|
-
const execa_1 = require("
|
|
13
|
-
const types_1 = require("
|
|
10
|
+
const config_1 = require("../../../config");
|
|
11
|
+
const utils_1 = require("../../../utils");
|
|
12
|
+
const execa_1 = require("../../../utils/execa");
|
|
13
|
+
const types_1 = require("../../types");
|
|
14
14
|
const LOCAL_CONFIG_FILES = [
|
|
15
15
|
'commitlint.config.cjs',
|
|
16
16
|
'commitlint.config.js',
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Check } from '
|
|
1
|
+
import { Check } from '../../types';
|
|
2
2
|
export declare const prBodyCheck: Check;
|
|
@@ -5,8 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.prBodyCheck = void 0;
|
|
7
7
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
-
const utils_1 = require("
|
|
9
|
-
const types_1 = require("
|
|
8
|
+
const utils_1 = require("../../../utils");
|
|
9
|
+
const types_1 = require("../../types");
|
|
10
10
|
// Matches "Closes #123", "Fixes #45", "Resolves #7" or JIRA-style "PROJ-456".
|
|
11
11
|
const TICKET_PATTERN = /(?:closes?|fixes?|resolves?)\s+#\d+|[A-Z][A-Z0-9]+-\d+/i;
|
|
12
12
|
exports.prBodyCheck = {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Check } from '
|
|
1
|
+
import { Check } from '../../types';
|
|
2
2
|
export declare const prSizeCheck: Check;
|