pr-checkmate 1.21.11 → 1.21.14
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 +50 -5
- package/dist/cli.js +2 -0
- package/dist/config/loader.js +6 -0
- package/dist/core/checks/dependency-check.js +15 -11
- package/dist/core/checks/license-check.js +18 -4
- package/dist/core/checks/python-format.js +68 -0
- package/dist/core/checks/python-lint.js +62 -0
- package/dist/core/registry.js +4 -0
- package/dist/core/targets.js +40 -0
- package/package.json +4 -1
package/LICENSE
CHANGED
|
@@ -1,9 +1,54 @@
|
|
|
1
|
-
|
|
2
|
-
All
|
|
1
|
+
PROPRIETARY SOFTWARE LICENSE
|
|
2
|
+
All Rights Reserved
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
Copyright (c) 2025-2026 Mybono. All Rights Reserved.
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
================================================================================
|
|
7
|
+
NOTICE
|
|
8
|
+
================================================================================
|
|
7
9
|
|
|
8
|
-
|
|
10
|
+
This software and its source code, documentation, data, models, and all
|
|
11
|
+
associated materials (collectively, the "Software") are the confidential and
|
|
12
|
+
proprietary property of Mybono (the "Owner").
|
|
9
13
|
|
|
14
|
+
NO LICENSE IS GRANTED. This is NOT open-source software. The Software is
|
|
15
|
+
made available for viewing solely to persons explicitly and individually
|
|
16
|
+
authorized in writing by the Owner. The mere presence of this source code in
|
|
17
|
+
any repository, archive, or medium does NOT constitute permission to use it.
|
|
18
|
+
|
|
19
|
+
================================================================================
|
|
20
|
+
RESTRICTIONS
|
|
21
|
+
================================================================================
|
|
22
|
+
|
|
23
|
+
Without the prior express written permission of the Owner, you MAY NOT:
|
|
24
|
+
|
|
25
|
+
1. Copy, reproduce, or duplicate the Software, in whole or in part.
|
|
26
|
+
2. Use or execute the Software for any purpose, commercial or otherwise.
|
|
27
|
+
3. Modify, adapt, translate, or create derivative works from the Software.
|
|
28
|
+
4. Distribute, publish, sublicense, sell, rent, lease, or transfer the
|
|
29
|
+
Software or any portion thereof to any third party.
|
|
30
|
+
5. Fork, mirror, or re-host the Software in any public or private repository.
|
|
31
|
+
6. Reverse engineer, decompile, or disassemble the Software.
|
|
32
|
+
7. Remove, obscure, or alter this notice or any copyright, trademark, or
|
|
33
|
+
other proprietary notices contained in the Software.
|
|
34
|
+
|
|
35
|
+
Any use not expressly permitted in writing by the Owner is strictly
|
|
36
|
+
prohibited and constitutes a violation of applicable copyright and
|
|
37
|
+
intellectual property laws.
|
|
38
|
+
|
|
39
|
+
================================================================================
|
|
40
|
+
NO WARRANTY
|
|
41
|
+
================================================================================
|
|
42
|
+
|
|
43
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
44
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
45
|
+
FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
46
|
+
OWNER BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN
|
|
47
|
+
ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
|
|
48
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
49
|
+
|
|
50
|
+
================================================================================
|
|
51
|
+
CONTACT
|
|
52
|
+
================================================================================
|
|
53
|
+
|
|
54
|
+
For licensing inquiries or permission requests, contact the Owner: Mybono.
|
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 },
|
|
@@ -16,22 +16,26 @@ exports.dependencyCheck = {
|
|
|
16
16
|
return node_fs_1.default.existsSync(node_path_1.default.join(ctx.cwd, 'package.json'));
|
|
17
17
|
},
|
|
18
18
|
async run(ctx) {
|
|
19
|
-
|
|
19
|
+
// Resolve the bundled depcheck binary and run it via the current node — never
|
|
20
|
+
// shell out to `npx`, which fails (exit 127) when depcheck isn't on the path.
|
|
21
|
+
let depcheckBin;
|
|
20
22
|
try {
|
|
21
|
-
|
|
23
|
+
depcheckBin = (0, utils_1.resolvePackageBin)('depcheck', 'bin/depcheck.js');
|
|
22
24
|
}
|
|
23
|
-
catch
|
|
24
|
-
|
|
25
|
-
if (err instanceof execa_1.ExecaError && err.stdout) {
|
|
26
|
-
stdout = err.stdout;
|
|
27
|
-
}
|
|
28
|
-
else {
|
|
29
|
-
throw err;
|
|
30
|
-
}
|
|
25
|
+
catch {
|
|
26
|
+
return (0, types_1.skip)('depcheck unavailable');
|
|
31
27
|
}
|
|
28
|
+
// reject:false — depcheck exits non-zero when it finds issues but still emits JSON on stdout.
|
|
29
|
+
const { stdout } = await (0, execa_1.execa)(process.execPath, [depcheckBin, '--json'], {
|
|
30
|
+
cwd: ctx.cwd,
|
|
31
|
+
reject: false,
|
|
32
|
+
});
|
|
32
33
|
const start = stdout.indexOf('{');
|
|
33
34
|
const end = stdout.lastIndexOf('}');
|
|
34
|
-
|
|
35
|
+
// No JSON at all → the tool couldn't produce a result; skip rather than fail the client's PR.
|
|
36
|
+
if (start === -1 || end === -1)
|
|
37
|
+
return (0, types_1.skip)('depcheck produced no output');
|
|
38
|
+
const json = stdout.slice(start, end + 1);
|
|
35
39
|
let result;
|
|
36
40
|
try {
|
|
37
41
|
result = JSON.parse(json);
|
|
@@ -24,10 +24,24 @@ exports.licenseCheck = {
|
|
|
24
24
|
async run(ctx) {
|
|
25
25
|
const allowPackages = new Set(ctx.config.licenseCheck?.allowPackages ?? []);
|
|
26
26
|
const allowLicenses = ctx.config.licenseCheck?.allowLicenses ?? [];
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
// Resolve the bundled license-checker binary and run it via the current node — never
|
|
28
|
+
// shell out to `npx`, which fails (exit 127) when license-checker isn't on the path.
|
|
29
|
+
let licenseCheckerBin;
|
|
30
|
+
try {
|
|
31
|
+
licenseCheckerBin = (0, utils_1.resolvePackageBin)('license-checker', 'bin/license-checker');
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return (0, types_1.skip)('license-checker unavailable');
|
|
35
|
+
}
|
|
36
|
+
const { stdout, failed } = await (0, execa_1.execa)(process.execPath, [licenseCheckerBin, '--json', '--production'], { cwd: ctx.cwd, reject: false });
|
|
37
|
+
let licenses;
|
|
38
|
+
try {
|
|
39
|
+
licenses = JSON.parse(stdout);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// Tool couldn't produce parseable output; skip rather than fail the client's PR.
|
|
43
|
+
return (0, types_1.skip)(failed ? 'license-checker failed to run' : 'license-checker produced invalid JSON');
|
|
44
|
+
}
|
|
31
45
|
const violations = [];
|
|
32
46
|
for (const [pkg, info] of Object.entries(licenses)) {
|
|
33
47
|
if (allowPackages.has(bareName(pkg)))
|
|
@@ -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.14",
|
|
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",
|
|
@@ -71,6 +72,7 @@
|
|
|
71
72
|
"@typescript-eslint/parser": "^8.47.0",
|
|
72
73
|
"@wasm-fmt/clang-format": "^22.1.8",
|
|
73
74
|
"cspell": "^9.3.2",
|
|
75
|
+
"depcheck": "^1.4.7",
|
|
74
76
|
"eslint": "^9.26.0",
|
|
75
77
|
"eslint-config-prettier": "^10.1.8",
|
|
76
78
|
"eslint-plugin-import": "^2.29.0",
|
|
@@ -79,6 +81,7 @@
|
|
|
79
81
|
"gitleaks-secret-scanner-fixed": "^2.1.3",
|
|
80
82
|
"inquirer": "^13.0.1",
|
|
81
83
|
"jscpd": "^3.5.10",
|
|
84
|
+
"license-checker": "^25.0.1",
|
|
82
85
|
"markdownlint-cli": "^0.49.0",
|
|
83
86
|
"prettier": ">=3.0.0",
|
|
84
87
|
"ts-node": "^10.9.1",
|