pr-checkmate 1.21.11 → 1.21.12
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/dist/cli.js
CHANGED
package/dist/config/loader.js
CHANGED
|
@@ -61,6 +61,11 @@ function mergeWithDefaults(userConfig) {
|
|
|
61
61
|
...userConfig.cpp,
|
|
62
62
|
clangFormat: { ...defaults.cpp?.clangFormat, ...userConfig.cpp?.clangFormat },
|
|
63
63
|
},
|
|
64
|
+
python: {
|
|
65
|
+
...defaults.python,
|
|
66
|
+
...userConfig.python,
|
|
67
|
+
ruff: { ...defaults.python?.ruff, ...userConfig.python?.ruff },
|
|
68
|
+
},
|
|
64
69
|
diffSmell: { ...defaults.diffSmell, ...userConfig.diffSmell },
|
|
65
70
|
prBody: { ...defaults.prBody, ...userConfig.prBody },
|
|
66
71
|
duplicate: { ...defaults.duplicate, ...userConfig.duplicate },
|
|
@@ -133,6 +138,7 @@ function getDefaultConfig() {
|
|
|
133
138
|
},
|
|
134
139
|
typecheck: { enabled: true },
|
|
135
140
|
cpp: { enabled: true, clangFormat: { style: 'LLVM' } },
|
|
141
|
+
python: { enabled: true, ruff: { lint: true, format: true } },
|
|
136
142
|
diffSmell: { enabled: true },
|
|
137
143
|
prBody: { enabled: true, minLength: 20, requireTicket: true },
|
|
138
144
|
duplicate: { enabled: true, minLines: 5, minTokens: 50 },
|
|
@@ -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.pythonFormatCheck = void 0;
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const utils_1 = require("../../utils");
|
|
10
|
+
const targets_1 = require("../targets");
|
|
11
|
+
const types_1 = require("../types");
|
|
12
|
+
const PY_GLOBS = ['*.py'];
|
|
13
|
+
/**
|
|
14
|
+
* Formats changed Python files with Ruff's formatter (WASM, no Python/pip needed).
|
|
15
|
+
* Read-only in check mode (`warn` when files differ from formatted output); rewrites
|
|
16
|
+
* files in place when `write` so CI can auto-commit — mirrors the Prettier/C++ checks.
|
|
17
|
+
*/
|
|
18
|
+
exports.pythonFormatCheck = {
|
|
19
|
+
name: 'Ruff Format',
|
|
20
|
+
phase: 'format',
|
|
21
|
+
applies(ctx) {
|
|
22
|
+
return (ctx.config.python?.enabled !== false &&
|
|
23
|
+
ctx.config.python?.ruff?.format !== false &&
|
|
24
|
+
ctx.languages.includes('python'));
|
|
25
|
+
},
|
|
26
|
+
async run(ctx) {
|
|
27
|
+
const targets = await (0, targets_1.resolveTargetFiles)(ctx, PY_GLOBS);
|
|
28
|
+
if (targets === null)
|
|
29
|
+
return (0, types_1.skip)('git unavailable');
|
|
30
|
+
if (targets.length === 0)
|
|
31
|
+
return (0, types_1.pass)('no Python files changed');
|
|
32
|
+
const { Workspace, PositionEncoding } = require('@astral-sh/ruff-wasm-nodejs');
|
|
33
|
+
const workspace = new Workspace(Workspace.defaultSettings(), PositionEncoding.Utf16);
|
|
34
|
+
let unformatted = 0;
|
|
35
|
+
for (const rel of targets) {
|
|
36
|
+
const abs = node_path_1.default.join(ctx.cwd, rel);
|
|
37
|
+
let content;
|
|
38
|
+
try {
|
|
39
|
+
content = node_fs_1.default.readFileSync(abs, 'utf-8');
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
continue; // renamed/removed between diff and read — skip
|
|
43
|
+
}
|
|
44
|
+
let formatted;
|
|
45
|
+
try {
|
|
46
|
+
formatted = workspace.format(content);
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
// A syntax error makes Ruff throw — that's a lint concern, not a format failure.
|
|
50
|
+
utils_1.logger.warn(`[Ruff Format]: ⚠️ Could not format ${rel} (syntax error?)\n${err}`);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (formatted === content)
|
|
54
|
+
continue;
|
|
55
|
+
unformatted += 1;
|
|
56
|
+
if (ctx.write)
|
|
57
|
+
node_fs_1.default.writeFileSync(abs, formatted);
|
|
58
|
+
}
|
|
59
|
+
if (unformatted === 0)
|
|
60
|
+
return (0, types_1.pass)();
|
|
61
|
+
if (!ctx.write) {
|
|
62
|
+
utils_1.logger.warn(`[Ruff Format]: ⚠️ ${unformatted} Python file(s) need formatting`);
|
|
63
|
+
return (0, types_1.warn)(`${unformatted} file(s) need formatting (run with write to fix)`);
|
|
64
|
+
}
|
|
65
|
+
utils_1.logger.info(`[Ruff Format]: ✅ Reformatted ${unformatted} Python file(s)`);
|
|
66
|
+
return (0, types_1.pass)(`reformatted ${unformatted} file(s)`);
|
|
67
|
+
},
|
|
68
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
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.pythonLintCheck = void 0;
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const utils_1 = require("../../utils");
|
|
10
|
+
const targets_1 = require("../targets");
|
|
11
|
+
const types_1 = require("../types");
|
|
12
|
+
const PY_GLOBS = ['*.py'];
|
|
13
|
+
/**
|
|
14
|
+
* Lints changed Python files with Ruff, bundled as a self-contained WASM module
|
|
15
|
+
* (`@astral-sh/ruff-wasm-nodejs`) so it runs on a bare Node image with no Python
|
|
16
|
+
* or pip install. Informational by design — reports issues without failing the
|
|
17
|
+
* build, matching how formatting checks roll out; promote to blocking per repo
|
|
18
|
+
* once teams have adopted it. Uses Ruff's default rule set (pyflakes + pycodestyle).
|
|
19
|
+
*/
|
|
20
|
+
exports.pythonLintCheck = {
|
|
21
|
+
name: 'Ruff Lint',
|
|
22
|
+
phase: 'informational',
|
|
23
|
+
applies(ctx) {
|
|
24
|
+
return (ctx.config.python?.enabled !== false &&
|
|
25
|
+
ctx.config.python?.ruff?.lint !== false &&
|
|
26
|
+
ctx.languages.includes('python'));
|
|
27
|
+
},
|
|
28
|
+
async run(ctx) {
|
|
29
|
+
const targets = await (0, targets_1.resolveTargetFiles)(ctx, PY_GLOBS);
|
|
30
|
+
if (targets === null)
|
|
31
|
+
return (0, types_1.skip)('git unavailable');
|
|
32
|
+
if (targets.length === 0)
|
|
33
|
+
return (0, types_1.pass)('no Python files changed');
|
|
34
|
+
const { Workspace, PositionEncoding } = require('@astral-sh/ruff-wasm-nodejs');
|
|
35
|
+
const workspace = new Workspace(Workspace.defaultSettings(), PositionEncoding.Utf16);
|
|
36
|
+
let totalIssues = 0;
|
|
37
|
+
let filesWithIssues = 0;
|
|
38
|
+
for (const rel of targets) {
|
|
39
|
+
let content;
|
|
40
|
+
try {
|
|
41
|
+
content = node_fs_1.default.readFileSync(node_path_1.default.join(ctx.cwd, rel), 'utf-8');
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
continue; // renamed/removed between diff and read — skip
|
|
45
|
+
}
|
|
46
|
+
const diagnostics = workspace.check(content);
|
|
47
|
+
if (diagnostics.length === 0)
|
|
48
|
+
continue;
|
|
49
|
+
filesWithIssues += 1;
|
|
50
|
+
totalIssues += diagnostics.length;
|
|
51
|
+
utils_1.logger.warn(`[Ruff Lint]: ⚠️ ${rel}: ${diagnostics.length} issue(s)`);
|
|
52
|
+
diagnostics
|
|
53
|
+
.slice(0, 5)
|
|
54
|
+
.forEach((d) => utils_1.logger.warn(` ${rel}:${d.start_location.row} ${d.code ?? '?'} ${d.message}`));
|
|
55
|
+
if (diagnostics.length > 5)
|
|
56
|
+
utils_1.logger.warn(` ... and ${diagnostics.length - 5} more`);
|
|
57
|
+
}
|
|
58
|
+
if (totalIssues === 0)
|
|
59
|
+
return (0, types_1.pass)();
|
|
60
|
+
return (0, types_1.warn)(`${totalIssues} issue(s) in ${filesWithIssues} file(s)`);
|
|
61
|
+
},
|
|
62
|
+
};
|
package/dist/core/registry.js
CHANGED
|
@@ -21,9 +21,11 @@ const pr_title_lint_1 = require("./checks/pr-title-lint");
|
|
|
21
21
|
const outdated_deps_1 = require("./checks/outdated-deps");
|
|
22
22
|
const markdown_lint_1 = require("./checks/markdown-lint");
|
|
23
23
|
const spellcheck_1 = require("./checks/spellcheck");
|
|
24
|
+
const python_lint_1 = require("./checks/python-lint");
|
|
24
25
|
// format — runs last, may write files
|
|
25
26
|
const prettier_1 = require("./checks/prettier");
|
|
26
27
|
const cpp_format_1 = require("./checks/cpp-format");
|
|
28
|
+
const python_format_1 = require("./checks/python-format");
|
|
27
29
|
/**
|
|
28
30
|
* Every check, in declaration order. The orchestrator groups them by `phase`
|
|
29
31
|
* (blocking → informational → format) and runs each phase's checks in parallel.
|
|
@@ -49,7 +51,9 @@ exports.REGISTRY = [
|
|
|
49
51
|
outdated_deps_1.outdatedDepsCheck,
|
|
50
52
|
markdown_lint_1.markdownLintCheck,
|
|
51
53
|
spellcheck_1.spellcheckCheck,
|
|
54
|
+
python_lint_1.pythonLintCheck,
|
|
52
55
|
// format
|
|
53
56
|
prettier_1.prettierCheck,
|
|
54
57
|
cpp_format_1.cppFormatCheck,
|
|
58
|
+
python_format_1.pythonFormatCheck,
|
|
55
59
|
];
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveTargetFiles = resolveTargetFiles;
|
|
4
|
+
const execa_1 = require("execa");
|
|
5
|
+
const utils_1 = require("../utils");
|
|
6
|
+
/**
|
|
7
|
+
* Lists the files a language check should operate on, matched by pathspec `globs`
|
|
8
|
+
* (e.g. `['*.py']`). Uses the PR diff range when available (delta mode), else
|
|
9
|
+
* scans all tracked files. Returns an empty array when nothing matches, and
|
|
10
|
+
* `null` when git itself is unusable (caller should `skip`).
|
|
11
|
+
*
|
|
12
|
+
* Only ACMR files are returned (Added/Copied/Modified/Renamed) — deletions are
|
|
13
|
+
* excluded, so every returned path exists on disk and is safe to read.
|
|
14
|
+
*/
|
|
15
|
+
async function resolveTargetFiles(ctx, globs) {
|
|
16
|
+
try {
|
|
17
|
+
const args = ctx.baseSha
|
|
18
|
+
? [
|
|
19
|
+
'diff',
|
|
20
|
+
'--name-only',
|
|
21
|
+
'--diff-filter=ACMR',
|
|
22
|
+
ctx.baseSha,
|
|
23
|
+
ctx.headSha ?? 'HEAD',
|
|
24
|
+
'--',
|
|
25
|
+
...globs,
|
|
26
|
+
]
|
|
27
|
+
: ['ls-files', ...globs];
|
|
28
|
+
const { stdout } = await (0, execa_1.execa)('git', args, { cwd: ctx.cwd, reject: false });
|
|
29
|
+
const files = stdout
|
|
30
|
+
.split('\n')
|
|
31
|
+
.map(f => f.trim())
|
|
32
|
+
.filter(Boolean);
|
|
33
|
+
utils_1.logger.info(`[targets]: ${ctx.baseSha ? '📂 Delta' : 'Full scan'}: ${files.length} file(s) for ${globs.join(', ')}`);
|
|
34
|
+
return files;
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
utils_1.logger.warn(`[targets]: ⚠️ Could not list files\n${err}`);
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pr-checkmate",
|
|
3
|
-
"version": "1.21.
|
|
3
|
+
"version": "1.21.12",
|
|
4
4
|
"description": "Automated PR quality checks: linting, formatting, dependency analysis, and spellcheck",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"github-actions",
|
|
@@ -59,6 +59,7 @@
|
|
|
59
59
|
"author": "Artur Polishchuk",
|
|
60
60
|
"license": "LicenseRef-Proprietary",
|
|
61
61
|
"dependencies": {
|
|
62
|
+
"@astral-sh/ruff-wasm-nodejs": "^0.15.20",
|
|
62
63
|
"@commitlint/cli": "^21.2.0",
|
|
63
64
|
"@commitlint/config-conventional": "^21.2.0",
|
|
64
65
|
"@cspell/dict-es-es": "^3.0.8",
|