pr-checkmate 2.1.0 → 3.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.
Files changed (109) hide show
  1. package/README.md +63 -267
  2. package/cspell.json +155 -133
  3. package/dist/config/constants.d.ts +13 -1
  4. package/dist/config/constants.js +20 -7
  5. package/dist/config/index.d.ts +23 -11
  6. package/dist/config/index.js +27 -33
  7. package/dist/core/checks/dependencies/banned-imports.js +4 -1
  8. package/dist/core/checks/dependencies/circular-deps.js +14 -5
  9. package/dist/core/checks/dependencies/dependency-check.js +2 -2
  10. package/dist/core/checks/dependencies/grype-scan.js +2 -1
  11. package/dist/core/checks/dependencies/license-check.js +1 -6
  12. package/dist/core/checks/dependencies/npm-audit.js +1 -1
  13. package/dist/core/checks/dependencies/outdated-deps.js +1 -6
  14. package/dist/core/checks/dependencies/vuln-scan.js +2 -1
  15. package/dist/core/checks/findings.d.ts +46 -2
  16. package/dist/core/checks/findings.js +140 -2
  17. package/dist/core/checks/git/custom-rules.js +2 -0
  18. package/dist/core/checks/git/diff-smell.js +6 -1
  19. package/dist/core/checks/git/leftover-debug.js +8 -2
  20. package/dist/core/checks/git/lockfile-drift.js +6 -1
  21. package/dist/core/checks/git/merge-conflict.js +1 -1
  22. package/dist/core/checks/git/missing-tests.js +1 -1
  23. package/dist/core/checks/git/todo-fixme.js +10 -2
  24. package/dist/core/checks/languages/cpp-format.js +19 -42
  25. package/dist/core/checks/languages/external-checks.d.ts +77 -0
  26. package/dist/core/checks/languages/external-checks.js +200 -0
  27. package/dist/core/checks/languages/external-runner.d.ts +119 -0
  28. package/dist/core/checks/languages/external-runner.js +143 -0
  29. package/dist/core/checks/languages/go-format.d.ts +4 -4
  30. package/dist/core/checks/languages/go-format.js +151 -30
  31. package/dist/core/checks/languages/lint.js +19 -19
  32. package/dist/core/checks/languages/prettier.js +2 -7
  33. package/dist/core/checks/languages/python-typecheck.d.ts +10 -5
  34. package/dist/core/checks/languages/python-typecheck.js +161 -37
  35. package/dist/core/checks/languages/typecheck.js +10 -8
  36. package/dist/core/checks/pr/commitlint.js +7 -1
  37. package/dist/core/checks/quality/case-collision.js +6 -4
  38. package/dist/core/checks/quality/config-validation.d.ts +0 -7
  39. package/dist/core/checks/quality/config-validation.js +30 -0
  40. package/dist/core/checks/quality/duplicate-check.js +86 -28
  41. package/dist/core/checks/quality/license-header.js +7 -4
  42. package/dist/core/checks/quality/markdown-lint.js +1 -6
  43. package/dist/core/checks/quality/spellcheck.js +34 -5
  44. package/dist/core/checks/quality/symlinks.js +6 -4
  45. package/dist/core/checks/security/diff-security.js +26 -18
  46. package/dist/core/checks/security/dockerfile-security.js +7 -1
  47. package/dist/core/checks/security/migration-safety.js +32 -4
  48. package/dist/core/checks/security/security.d.ts +56 -0
  49. package/dist/core/checks/security/security.js +135 -11
  50. package/dist/core/checks/security/sensitive-file-guard.js +40 -6
  51. package/dist/core/checks/security/workflow-security.js +7 -1
  52. package/dist/core/checks/tool-install.d.ts +102 -0
  53. package/dist/core/checks/tool-install.js +69 -0
  54. package/dist/core/context.js +9 -1
  55. package/dist/core/install/cache.js +42 -11
  56. package/dist/core/install/doctor.d.ts +2 -2
  57. package/dist/core/install/doctor.js +17 -16
  58. package/dist/core/install/ensure.js +22 -3
  59. package/dist/core/install/lock.d.ts +2 -1
  60. package/dist/core/install/prepare.d.ts +75 -0
  61. package/dist/core/install/prepare.js +151 -0
  62. package/dist/core/install/profiles.d.ts +17 -3
  63. package/dist/core/install/profiles.js +27 -0
  64. package/dist/core/install/reuse.d.ts +41 -0
  65. package/dist/core/install/reuse.js +107 -0
  66. package/dist/core/registry.js +10 -18
  67. package/dist/core/reporters.js +8 -2
  68. package/dist/core/run-checks.js +74 -3
  69. package/dist/core/targets.d.ts +116 -5
  70. package/dist/core/targets.js +101 -16
  71. package/dist/core/types.d.ts +26 -2
  72. package/dist/core/types.js +28 -0
  73. package/dist/init.js +21 -1
  74. package/dist/interfaces/index.d.ts +1 -0
  75. package/dist/utils/detect-language.js +11 -1
  76. package/dist/utils/index.d.ts +1 -0
  77. package/dist/utils/index.js +1 -0
  78. package/dist/utils/package-version.d.ts +4 -0
  79. package/dist/utils/package-version.js +41 -0
  80. package/dist/utils/resolve-bin.d.ts +56 -6
  81. package/dist/utils/resolve-bin.js +136 -26
  82. package/dist/utils/update-notifier.js +5 -7
  83. package/gitleaks.toml +14 -0
  84. package/install-manifests/go/package-lock.json +24 -0
  85. package/install-manifests/go/package.json +8 -0
  86. package/install-manifests/pytypes/package-lock.json +45 -0
  87. package/install-manifests/pytypes/package.json +8 -0
  88. package/package.json +4 -2
  89. package/pr-checkmate.schema.json +8 -2
  90. package/src/config/cspell.json +155 -133
  91. package/src/config/pr-checkmate.schema.json +8 -2
  92. package/dist/core/checks/languages/csharp-format.d.ts +0 -13
  93. package/dist/core/checks/languages/csharp-format.js +0 -52
  94. package/dist/core/checks/languages/go-lint.d.ts +0 -8
  95. package/dist/core/checks/languages/go-lint.js +0 -48
  96. package/dist/core/checks/languages/kotlin-lint.d.ts +0 -2
  97. package/dist/core/checks/languages/kotlin-lint.js +0 -42
  98. package/dist/core/checks/languages/php-format.d.ts +0 -9
  99. package/dist/core/checks/languages/php-format.js +0 -49
  100. package/dist/core/checks/languages/ruby-lint.d.ts +0 -8
  101. package/dist/core/checks/languages/ruby-lint.js +0 -50
  102. package/dist/core/checks/languages/rust-format.d.ts +0 -9
  103. package/dist/core/checks/languages/rust-format.js +0 -45
  104. package/dist/core/checks/languages/rust-lint.d.ts +0 -8
  105. package/dist/core/checks/languages/rust-lint.js +0 -50
  106. package/dist/core/checks/languages/shellcheck.d.ts +0 -8
  107. package/dist/core/checks/languages/shellcheck.js +0 -55
  108. package/dist/core/checks/languages/swift-lint.d.ts +0 -2
  109. package/dist/core/checks/languages/swift-lint.js +0 -46
@@ -727,7 +727,7 @@
727
727
  "default": 1
728
728
  },
729
729
  "reuseLocal": {
730
- "description": "Reuse a tool already in the client's node_modules: \"compatible\" (default), \"always\", or \"off\".",
730
+ "description": "Reuse a tool already present in the repository's node_modules instead of our copy. \"compatible\" (default) reuses when the major matches ours, or when the tool reads the repository's own config (a legacy .eslintrc can only be read by the ESLint that wrote it, and a tsconfig by its own compiler). \"always\" reuses across a major gap at your own risk. \"off\" always uses our pinned copy, for reproducibility.",
731
731
  "type": "string",
732
732
  "enum": ["compatible", "always", "off"],
733
733
  "default": "compatible"
@@ -761,7 +761,7 @@
761
761
  "default": "~/.cache/pr-checkmate"
762
762
  },
763
763
  "prewarm": {
764
- "description": "Warm the cache in postinstall. false leaves everything to the lazy path.",
764
+ "description": "NOT YET IMPLEMENTED — accepted and ignored. Warming the cache needs a postinstall hook, and there is nothing to warm while every profile's tooling ships inside the package; the hook lands with the dependency extraction (ticket 064, release 2). Documented as inert rather than quietly doing nothing.",
765
765
  "type": "boolean",
766
766
  "default": true
767
767
  },
@@ -1174,6 +1174,12 @@
1174
1174
  }
1175
1175
  }
1176
1176
  }
1177
+ },
1178
+ "dataflowWindow": {
1179
+ "type": "integer",
1180
+ "minimum": 0,
1181
+ "default": 10,
1182
+ "description": "Added lines a value stays tainted for, connecting an interpolated string assigned on one line to a query on a later line. 0 disables the cross-line heuristic. Advisory only — it has no scopes and no reassignment tracking, so it never blocks a run."
1177
1183
  }
1178
1184
  }
1179
1185
  },
@@ -1,13 +0,0 @@
1
- import { Check } from '../../types';
2
- /**
3
- * Formats/verifies C# code with `dotnet format` (which also runs the configured
4
- * analyzers/whitespace/style rules). Runner-dependency: needs the .NET SDK on
5
- * the runner, gracefully skipping when absent. Read-only in check mode
6
- * (`--verify-no-changes`); rewrites in place with `dotnet format` when `write`,
7
- * so CI can auto-commit — mirrors the other format checks.
8
- *
9
- * Note: `dotnet format` operates on the project/solution (needs a prior
10
- * `dotnet restore`), so it runs over the whole project; the delta check only
11
- * gates whether it runs at all.
12
- */
13
- export declare const csharpFormatCheck: Check;
@@ -1,52 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.csharpFormatCheck = 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 CS_GLOBS = ['*.cs'];
9
- /**
10
- * Formats/verifies C# code with `dotnet format` (which also runs the configured
11
- * analyzers/whitespace/style rules). Runner-dependency: needs the .NET SDK on
12
- * the runner, gracefully skipping when absent. Read-only in check mode
13
- * (`--verify-no-changes`); rewrites in place with `dotnet format` when `write`,
14
- * so CI can auto-commit — mirrors the other format checks.
15
- *
16
- * Note: `dotnet format` operates on the project/solution (needs a prior
17
- * `dotnet restore`), so it runs over the whole project; the delta check only
18
- * gates whether it runs at all.
19
- */
20
- exports.csharpFormatCheck = {
21
- name: 'C# Format',
22
- phase: 'format',
23
- applies(ctx) {
24
- return ctx.config.csharp?.enabled !== false && ctx.languages.includes('csharp');
25
- },
26
- async run(ctx) {
27
- const targets = await (0, targets_1.resolveTargetFiles)(ctx, CS_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 C# files changed');
32
- const check = await (0, execa_1.execa)('dotnet', ['format', '--verify-no-changes'], {
33
- cwd: ctx.cwd,
34
- reject: false,
35
- });
36
- if (check.code === 'ENOENT')
37
- return (0, types_1.skip)('dotnet SDK not installed');
38
- if (check.exitCode === 0)
39
- return (0, types_1.pass)();
40
- if (!ctx.write) {
41
- utils_1.logger.warn('[C# Format]: ⚠️ C# code needs formatting (dotnet format --verify-no-changes failed)');
42
- return (0, types_1.warn)('C# code needs formatting (run with write to fix)');
43
- }
44
- const fix = await (0, execa_1.execa)('dotnet', ['format'], { cwd: ctx.cwd, reject: false });
45
- if (fix.exitCode !== 0) {
46
- utils_1.logger.warn(`[C# Format]: ⚠️ dotnet format failed\n${fix.stderr}`);
47
- return (0, types_1.warn)('dotnet format failed to rewrite files');
48
- }
49
- utils_1.logger.info('[C# Format]: ✅ Reformatted C# code');
50
- return (0, types_1.pass)('reformatted C# code');
51
- },
52
- };
@@ -1,8 +0,0 @@
1
- import { Check } from '../../types';
2
- /**
3
- * Vets changed Go code with `go vet ./...`. Runner-dependency: requires the Go
4
- * toolchain on the runner (like SwiftLint/ktlint), and gracefully skips when
5
- * `go` is absent. `go vet` operates per-package, so it runs over the module
6
- * rather than a file list; the delta check only gates whether it runs at all.
7
- */
8
- export declare const goLintCheck: Check;
@@ -1,48 +0,0 @@
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 +0,0 @@
1
- import { Check } from '../../types';
2
- export declare const kotlinLintCheck: Check;
@@ -1,42 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.kotlinLintCheck = 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 KOTLIN_GLOBS = ['*.kt', '*.kts'];
9
- exports.kotlinLintCheck = {
10
- name: 'ktlint',
11
- phase: 'informational',
12
- applies(ctx) {
13
- return ctx.config.kotlin?.enabled !== false && ctx.languages.includes('kotlin');
14
- },
15
- async run(ctx) {
16
- const targets = await (0, targets_1.resolveTargetFiles)(ctx, KOTLIN_GLOBS);
17
- if (targets === null)
18
- return (0, types_1.skip)('git unavailable');
19
- if (targets.length === 0)
20
- return (0, types_1.pass)('no Kotlin files changed');
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');
30
- const output = [result.stdout, result.stderr].filter(Boolean).join('\n').trim();
31
- if (result.exitCode === 0 || !output) {
32
- utils_1.logger.info('[ktlint]: ✅ No issues found');
33
- return (0, types_1.pass)();
34
- }
35
- const lines = output.split('\n').filter(Boolean);
36
- utils_1.logger.warn(`[ktlint]: ⚠️ ${lines.length} issue(s):`);
37
- lines.slice(0, 10).forEach(l => utils_1.logger.warn(` ${l}`));
38
- if (lines.length > 10)
39
- utils_1.logger.warn(` ... and ${lines.length - 10} more`);
40
- return (0, types_1.warn)(`${lines.length} issue(s)`);
41
- },
42
- };
@@ -1,9 +0,0 @@
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;
@@ -1,49 +0,0 @@
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,8 +0,0 @@
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;
@@ -1,50 +0,0 @@
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
- };
@@ -1,9 +0,0 @@
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;
@@ -1,45 +0,0 @@
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
- };
@@ -1,8 +0,0 @@
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;
@@ -1,50 +0,0 @@
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,8 +0,0 @@
1
- import { Check } from '../../types';
2
- /**
3
- * Lints changed shell scripts with ShellCheck. Runner-dependency: needs the
4
- * `shellcheck` binary on the runner (like Go Vet/Clippy/RuboCop), gracefully
5
- * skipping when it's absent. `-f gcc` gives one line per finding, ending in
6
- * an `[SC…]` rule code, which both a human and the count below can read directly.
7
- */
8
- export declare const shellcheckCheck: Check;
@@ -1,55 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.shellcheckCheck = 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 SHELL_GLOBS = ['*.sh', '*.bash', '*.ksh'];
9
- /**
10
- * Lints changed shell scripts with ShellCheck. Runner-dependency: needs the
11
- * `shellcheck` binary on the runner (like Go Vet/Clippy/RuboCop), gracefully
12
- * skipping when it's absent. `-f gcc` gives one line per finding, ending in
13
- * an `[SC…]` rule code, which both a human and the count below can read directly.
14
- */
15
- exports.shellcheckCheck = {
16
- name: 'ShellCheck',
17
- phase: 'informational',
18
- applies(ctx) {
19
- return ctx.config.shellcheck?.enabled !== false;
20
- },
21
- async run(ctx) {
22
- const targets = await (0, targets_1.resolveTargetFiles)(ctx, SHELL_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 shell scripts changed');
27
- const minSeverity = ctx.config.shellcheck?.minSeverity;
28
- const ignore = ctx.config.shellcheck?.ignore ?? [];
29
- const args = ['-f', 'gcc'];
30
- if (minSeverity)
31
- args.push('-S', minSeverity);
32
- if (ignore.length > 0)
33
- args.push('-e', ignore.join(','));
34
- args.push(...targets);
35
- const result = await (0, execa_1.execa)('shellcheck', args, { cwd: ctx.cwd, reject: false });
36
- // execa v9 with reject:false does NOT throw on a missing binary — it resolves
37
- // with `code: 'ENOENT'`. Detect that on the result, not via try/catch.
38
- if (result.code === 'ENOENT')
39
- return (0, types_1.skip)('shellcheck not installed');
40
- if (result.exitCode === 0) {
41
- utils_1.logger.info('[ShellCheck]: ✅ No issues found');
42
- return (0, types_1.pass)();
43
- }
44
- const lines = [result.stdout, result.stderr]
45
- .filter(Boolean)
46
- .join('\n')
47
- .split('\n')
48
- .filter(Boolean);
49
- utils_1.logger.warn(`[ShellCheck]: ⚠️ ${lines.length} issue(s):`);
50
- lines.slice(0, 10).forEach(l => utils_1.logger.warn(` ${l}`));
51
- if (lines.length > 10)
52
- utils_1.logger.warn(` ... and ${lines.length - 10} more`);
53
- return (0, types_1.warn)(`${lines.length} issue(s) in ${targets.length} file(s)`);
54
- },
55
- };
@@ -1,2 +0,0 @@
1
- import { Check } from '../../types';
2
- export declare const swiftLintCheck: Check;
@@ -1,46 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.swiftLintCheck = 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 SWIFT_GLOBS = ['*.swift'];
9
- exports.swiftLintCheck = {
10
- name: 'SwiftLint',
11
- phase: 'informational',
12
- applies(ctx) {
13
- return ctx.config.swift?.enabled !== false && ctx.languages.includes('swift');
14
- },
15
- async run(ctx) {
16
- const targets = await (0, targets_1.resolveTargetFiles)(ctx, SWIFT_GLOBS);
17
- if (targets === null)
18
- return (0, types_1.skip)('git unavailable');
19
- if (targets.length === 0)
20
- return (0, types_1.pass)('no Swift files changed');
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');
30
- const output = [result.stdout, result.stderr].filter(Boolean).join('\n').trim();
31
- if (result.exitCode === 0 || !output) {
32
- utils_1.logger.info('[SwiftLint]: ✅ No issues found');
33
- return (0, types_1.pass)();
34
- }
35
- const lines = output
36
- .split('\n')
37
- .filter(l => l.includes(': warning:') || l.includes(': error:'));
38
- const errorCount = lines.filter(l => l.includes(': error:')).length;
39
- const warnCount = lines.filter(l => l.includes(': warning:')).length;
40
- utils_1.logger.warn(`[SwiftLint]: ⚠️ ${errorCount} error(s), ${warnCount} warning(s):`);
41
- lines.slice(0, 10).forEach(l => utils_1.logger.warn(` ${l}`));
42
- if (lines.length > 10)
43
- utils_1.logger.warn(` ... and ${lines.length - 10} more`);
44
- return (0, types_1.warn)(`${errorCount} error(s), ${warnCount} warning(s)`);
45
- },
46
- };