pr-checkmate 1.21.9 → 1.21.11
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/cspell.json +270 -1
- package/dist/cli.js +37 -3
- package/dist/config/constants.js +15 -1
- package/dist/config/loader.js +11 -0
- package/dist/core/checks/circular-deps.js +34 -0
- package/dist/core/checks/commitlint.js +83 -0
- package/dist/core/checks/cpp-format.js +120 -0
- package/dist/core/checks/dependency-check.js +54 -0
- package/dist/core/checks/diff-security.js +162 -0
- package/dist/core/checks/diff-smell.js +65 -0
- package/dist/core/checks/duplicate-check.js +60 -0
- package/dist/core/checks/license-check.js +49 -0
- package/dist/core/checks/lint.js +62 -0
- package/dist/core/checks/markdown-lint.js +115 -0
- package/dist/core/checks/npm-audit.js +50 -0
- package/dist/core/checks/outdated-deps.js +48 -0
- package/dist/core/checks/pr-body.js +49 -0
- package/dist/core/checks/pr-size-check.js +42 -0
- package/dist/core/checks/pr-title-lint.js +37 -0
- package/dist/core/checks/prettier.js +96 -0
- package/dist/core/checks/security.js +50 -0
- package/dist/core/checks/sensitive-file-guard.js +103 -0
- package/dist/core/checks/spellcheck.js +98 -0
- package/dist/core/checks/typecheck.js +48 -0
- package/dist/core/context.js +55 -0
- package/dist/core/index.js +24 -0
- package/dist/core/registry.js +55 -0
- package/dist/{scripts/pr-summary.js → core/reporters.js} +35 -32
- package/dist/core/run-checks.js +66 -0
- package/dist/core/types.js +16 -0
- package/dist/index.js +10 -98
- package/dist/init.js +7 -0
- package/dist/utils/detect-language.js +81 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/result-collector.js +2 -2
- package/eslint.config.mjs +3 -3
- package/package.json +4 -3
- package/src/config/cspell.json +270 -1
- package/src/config/eslint.config.mjs +3 -3
- package/dist/scripts/circular-deps.js +0 -37
- package/dist/scripts/commitlint.js +0 -114
- package/dist/scripts/dependency-check.js +0 -62
- package/dist/scripts/diff-security.js +0 -199
- package/dist/scripts/diff-smell.js +0 -100
- package/dist/scripts/duplicate-check.js +0 -68
- package/dist/scripts/index.js +0 -187
- package/dist/scripts/license-check.js +0 -61
- package/dist/scripts/lint.js +0 -61
- package/dist/scripts/markdown-lint.js +0 -128
- package/dist/scripts/npm-audit.js +0 -65
- package/dist/scripts/outdated-deps.js +0 -52
- package/dist/scripts/pr-body.js +0 -62
- package/dist/scripts/pr-size-check.js +0 -56
- package/dist/scripts/pr-title-lint.js +0 -40
- package/dist/scripts/prettier-autoformat.js +0 -116
- package/dist/scripts/security.js +0 -97
- package/dist/scripts/sensitive-file-guard.js +0 -145
- package/dist/scripts/spellcheck.js +0 -127
- package/dist/scripts/typecheck.js +0 -51
package/cspell.json
CHANGED
|
@@ -274,7 +274,276 @@
|
|
|
274
274
|
"פילינגים",
|
|
275
275
|
"פיניש",
|
|
276
276
|
"קלאסי",
|
|
277
|
-
"רטינול"
|
|
277
|
+
"רטינול",
|
|
278
|
+
"accesscode",
|
|
279
|
+
"adminsdk",
|
|
280
|
+
"akia",
|
|
281
|
+
"AKID",
|
|
282
|
+
"myorg",
|
|
283
|
+
"androidpublisher",
|
|
284
|
+
"apikey",
|
|
285
|
+
"apinick",
|
|
286
|
+
"cnick",
|
|
287
|
+
"ghactor",
|
|
288
|
+
"atts",
|
|
289
|
+
"apkg",
|
|
290
|
+
"BEHAVIOUR",
|
|
291
|
+
"appclinical",
|
|
292
|
+
"graphify",
|
|
293
|
+
"graphifyy",
|
|
294
|
+
"DTENSORFLOW",
|
|
295
|
+
"realtag",
|
|
296
|
+
"appoperator",
|
|
297
|
+
"appreglaur",
|
|
298
|
+
"apprequest",
|
|
299
|
+
"appsales",
|
|
300
|
+
"appstoreconnect",
|
|
301
|
+
"archs",
|
|
302
|
+
"artur",
|
|
303
|
+
"Ashwini",
|
|
304
|
+
"atlassian",
|
|
305
|
+
"auditq",
|
|
306
|
+
"avdmanager",
|
|
307
|
+
"avds",
|
|
308
|
+
"awscli",
|
|
309
|
+
"awscliv",
|
|
310
|
+
"pubspec",
|
|
311
|
+
"nios",
|
|
312
|
+
"awsconfiguration",
|
|
313
|
+
"baprs",
|
|
314
|
+
"bepkg",
|
|
315
|
+
"bioeye",
|
|
316
|
+
"bioeyesdkswift",
|
|
317
|
+
"bitbucket",
|
|
318
|
+
"bosb",
|
|
319
|
+
"braineye",
|
|
320
|
+
"braineyeandroid",
|
|
321
|
+
"braineyecore",
|
|
322
|
+
"braineyesdk",
|
|
323
|
+
"bscanf",
|
|
324
|
+
"buildx",
|
|
325
|
+
"CALVER",
|
|
326
|
+
"cdxgen",
|
|
327
|
+
"Cdxgen",
|
|
328
|
+
"ededed",
|
|
329
|
+
"ccache",
|
|
330
|
+
"centralised",
|
|
331
|
+
"chan",
|
|
332
|
+
"checkifemailexists",
|
|
333
|
+
"checkpointing",
|
|
334
|
+
"claudecode",
|
|
335
|
+
"cmdline",
|
|
336
|
+
"cmdtools",
|
|
337
|
+
"cocoapods",
|
|
338
|
+
"codeartifact",
|
|
339
|
+
"cognitogroup",
|
|
340
|
+
"completesession",
|
|
341
|
+
"completetest",
|
|
342
|
+
"confirmemail",
|
|
343
|
+
"confirmpassword",
|
|
344
|
+
"containerregistry",
|
|
345
|
+
"cpiav",
|
|
346
|
+
"createsession",
|
|
347
|
+
"createtest",
|
|
348
|
+
"ctest",
|
|
349
|
+
"customfield",
|
|
350
|
+
"cyclonedx",
|
|
351
|
+
"DBUILD",
|
|
352
|
+
"DCMAKE",
|
|
353
|
+
"dedup",
|
|
354
|
+
"deintegrate",
|
|
355
|
+
"Deintegrating",
|
|
356
|
+
"deleteuser",
|
|
357
|
+
"devuser",
|
|
358
|
+
"DFETCHCONTENT",
|
|
359
|
+
"disableuser",
|
|
360
|
+
"dockerization",
|
|
361
|
+
"Dorg",
|
|
362
|
+
"DTFLITE",
|
|
363
|
+
"dtypes",
|
|
364
|
+
"DTYPES",
|
|
365
|
+
"dumpsys",
|
|
366
|
+
"eigen",
|
|
367
|
+
"EIGEN",
|
|
368
|
+
"enableuser",
|
|
369
|
+
"eoilist",
|
|
370
|
+
"eyecon",
|
|
371
|
+
"eyetracking",
|
|
372
|
+
"eympact",
|
|
373
|
+
"farmhash",
|
|
374
|
+
"FARMHASH",
|
|
375
|
+
"FETCHCONTENT",
|
|
376
|
+
"ffprobe",
|
|
377
|
+
"fgets",
|
|
378
|
+
"filehistory",
|
|
379
|
+
"fixedname",
|
|
380
|
+
"flatbuffers",
|
|
381
|
+
"FLATBUFFERS",
|
|
382
|
+
"forgotpassword",
|
|
383
|
+
"Fsub",
|
|
384
|
+
"gemmlowp",
|
|
385
|
+
"GEMMLOWP",
|
|
386
|
+
"genkey",
|
|
387
|
+
"germany",
|
|
388
|
+
"getprop",
|
|
389
|
+
"ghsa",
|
|
390
|
+
"gnupg",
|
|
391
|
+
"Gowtham",
|
|
392
|
+
"gradlew",
|
|
393
|
+
"Hamou",
|
|
394
|
+
"Harel",
|
|
395
|
+
"hflip",
|
|
396
|
+
"Hossein",
|
|
397
|
+
"initialised",
|
|
398
|
+
"iphoneos",
|
|
399
|
+
"iphonesimulator",
|
|
400
|
+
"issuetype",
|
|
401
|
+
"johnsmith",
|
|
402
|
+
"jsqr",
|
|
403
|
+
"Jsqr",
|
|
404
|
+
"junitxml",
|
|
405
|
+
"jvmargs",
|
|
406
|
+
"keyalg",
|
|
407
|
+
"keypass",
|
|
408
|
+
"keysize",
|
|
409
|
+
"keytool",
|
|
410
|
+
"Khaing",
|
|
411
|
+
"konsultamd",
|
|
412
|
+
"lelvni",
|
|
413
|
+
"libexec",
|
|
414
|
+
"libopencv",
|
|
415
|
+
"libtensorflow",
|
|
416
|
+
"listapps",
|
|
417
|
+
"localised",
|
|
418
|
+
"logcat",
|
|
419
|
+
"lproj",
|
|
420
|
+
"lxgksa",
|
|
421
|
+
"Meaghan",
|
|
422
|
+
"metadatalist",
|
|
423
|
+
"Metaspace",
|
|
424
|
+
"mhvhic",
|
|
425
|
+
"mydir",
|
|
426
|
+
"myimage",
|
|
427
|
+
"mylib",
|
|
428
|
+
"myprefix",
|
|
429
|
+
"myrepo",
|
|
430
|
+
"mytoken",
|
|
431
|
+
"Naing",
|
|
432
|
+
"Namayeshi",
|
|
433
|
+
"nbraineye",
|
|
434
|
+
"NFIX",
|
|
435
|
+
"noprompt",
|
|
436
|
+
"noslash",
|
|
437
|
+
"oneline",
|
|
438
|
+
"Ooura",
|
|
439
|
+
"otherws",
|
|
440
|
+
"pagelen",
|
|
441
|
+
"Palaniswamy",
|
|
442
|
+
"pbxproj",
|
|
443
|
+
"pcodeartifact",
|
|
444
|
+
"Pickery",
|
|
445
|
+
"pipenv",
|
|
446
|
+
"podfile",
|
|
447
|
+
"poland",
|
|
448
|
+
"posthog",
|
|
449
|
+
"pthreadpool",
|
|
450
|
+
"pullrequests",
|
|
451
|
+
"pyproject",
|
|
452
|
+
"qscale",
|
|
453
|
+
"Ramesh",
|
|
454
|
+
"rawvideo",
|
|
455
|
+
"resendcode",
|
|
456
|
+
"samd",
|
|
457
|
+
"Sandino",
|
|
458
|
+
"Saurabh",
|
|
459
|
+
"sbom",
|
|
460
|
+
"sboms",
|
|
461
|
+
"scanf",
|
|
462
|
+
"sdkmanager",
|
|
463
|
+
"sess",
|
|
464
|
+
"setpassword",
|
|
465
|
+
"somerepo",
|
|
466
|
+
"Shani",
|
|
467
|
+
"simctl",
|
|
468
|
+
"stagingeu",
|
|
469
|
+
"storepass",
|
|
470
|
+
"retcode",
|
|
471
|
+
"Tukey",
|
|
472
|
+
"VIRTUALENVS",
|
|
473
|
+
"Δangle",
|
|
474
|
+
"strncpy",
|
|
475
|
+
"subjectid",
|
|
476
|
+
"subscriptionlist",
|
|
477
|
+
"swiftshader",
|
|
478
|
+
"Syncer",
|
|
479
|
+
"Talaiee",
|
|
480
|
+
"Tayebeh",
|
|
481
|
+
"tcwf",
|
|
482
|
+
"temurin",
|
|
483
|
+
"tensorflow",
|
|
484
|
+
"testsuite",
|
|
485
|
+
"testsuites",
|
|
486
|
+
"testuser",
|
|
487
|
+
"tfcommit",
|
|
488
|
+
"tflite",
|
|
489
|
+
"tocken",
|
|
490
|
+
"Turakhia",
|
|
491
|
+
"Tzionit",
|
|
492
|
+
"ubgjg",
|
|
493
|
+
"udid",
|
|
494
|
+
"unparseable",
|
|
495
|
+
"unrecognised",
|
|
496
|
+
"updateemail",
|
|
497
|
+
"userfound",
|
|
498
|
+
"myregistry",
|
|
499
|
+
"userid",
|
|
500
|
+
"userinfo",
|
|
501
|
+
"usermetadata",
|
|
502
|
+
"userproperty",
|
|
503
|
+
"uuids",
|
|
504
|
+
"vfrzk",
|
|
505
|
+
"vuhftgasouc",
|
|
506
|
+
"vulns",
|
|
507
|
+
"workdir",
|
|
508
|
+
"xcframework",
|
|
509
|
+
"xcodeproj",
|
|
510
|
+
"xcresult",
|
|
511
|
+
"xcresulttool",
|
|
512
|
+
"xcrun",
|
|
513
|
+
"xcstrings",
|
|
514
|
+
"xcworkspace",
|
|
515
|
+
"XNNPACK",
|
|
516
|
+
"DYLD",
|
|
517
|
+
"qreader",
|
|
518
|
+
"xoxb",
|
|
519
|
+
"bbox",
|
|
520
|
+
"Degs",
|
|
521
|
+
"Kanade",
|
|
522
|
+
"neighbours",
|
|
523
|
+
"unsharp",
|
|
524
|
+
"imgproc",
|
|
525
|
+
"highgui",
|
|
526
|
+
"imgcodecs",
|
|
527
|
+
"videoio",
|
|
528
|
+
"gapi",
|
|
529
|
+
"objdetect",
|
|
530
|
+
"behaviour",
|
|
531
|
+
"softfloat",
|
|
532
|
+
"CUDA",
|
|
533
|
+
"OPENCL",
|
|
534
|
+
"OPENNI",
|
|
535
|
+
"GSTREAMER",
|
|
536
|
+
"DWITH",
|
|
537
|
+
"DINSTALL",
|
|
538
|
+
"DPARALLEL",
|
|
539
|
+
"libavcodec",
|
|
540
|
+
"libavformat",
|
|
541
|
+
"libavutil",
|
|
542
|
+
"libswscale",
|
|
543
|
+
"Makefiles",
|
|
544
|
+
"dpdm",
|
|
545
|
+
"commitlintrc",
|
|
546
|
+
"shortstat"
|
|
278
547
|
],
|
|
279
548
|
"ignorePaths": [
|
|
280
549
|
"**/node_modules/**",
|
package/dist/cli.js
CHANGED
|
@@ -1,9 +1,33 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
const
|
|
4
|
+
const core_1 = require("./core");
|
|
5
5
|
const init_1 = require("./init");
|
|
6
6
|
const utils_1 = require("./utils");
|
|
7
|
+
/** Maps short CLI command names to their registry check names. */
|
|
8
|
+
const COMMAND_TO_CHECK = {
|
|
9
|
+
lint: 'ESLint',
|
|
10
|
+
deps: 'Dependencies',
|
|
11
|
+
dependencies: 'Dependencies',
|
|
12
|
+
'npm-audit': 'NPM Audit',
|
|
13
|
+
prettier: 'Prettier',
|
|
14
|
+
spellcheck: 'Spellcheck',
|
|
15
|
+
security: 'Security Scan',
|
|
16
|
+
circular: 'Circular Deps',
|
|
17
|
+
license: 'License Check',
|
|
18
|
+
'pr-size': 'PR Size',
|
|
19
|
+
'pr-title': 'PR Title',
|
|
20
|
+
outdated: 'Outdated Deps',
|
|
21
|
+
markdown: 'Markdown',
|
|
22
|
+
typecheck: 'TypeScript',
|
|
23
|
+
'diff-smell': 'Diff Smells',
|
|
24
|
+
'diff-security': 'Diff Security',
|
|
25
|
+
'sensitive-files': 'Sensitive Files',
|
|
26
|
+
'pr-body': 'PR Body',
|
|
27
|
+
duplicate: 'Duplicate Code',
|
|
28
|
+
commitlint: 'Commitlint',
|
|
29
|
+
'cpp-format': 'C++ Format',
|
|
30
|
+
};
|
|
7
31
|
const cmd = process.argv[2];
|
|
8
32
|
void (async () => {
|
|
9
33
|
try {
|
|
@@ -11,8 +35,18 @@ void (async () => {
|
|
|
11
35
|
await (0, init_1.init)();
|
|
12
36
|
process.exit(0);
|
|
13
37
|
}
|
|
14
|
-
|
|
15
|
-
|
|
38
|
+
// Full run: post a GitHub summary comment and allow formatters to write.
|
|
39
|
+
if (!cmd || cmd === 'all') {
|
|
40
|
+
const report = await (0, core_1.runChecks)({ reporter: 'github', write: true });
|
|
41
|
+
process.exit(report.ok ? 0 : 1);
|
|
42
|
+
}
|
|
43
|
+
const checkName = COMMAND_TO_CHECK[cmd];
|
|
44
|
+
if (!checkName) {
|
|
45
|
+
utils_1.logger.warn(`[cli]: Unknown command "${cmd}". Available: all, init, ${Object.keys(COMMAND_TO_CHECK).join(', ')}`);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
const report = await (0, core_1.runChecks)({ only: [checkName], reporter: 'stdout', write: true });
|
|
49
|
+
process.exit(report.ok ? 0 : 1);
|
|
16
50
|
}
|
|
17
51
|
catch (err) {
|
|
18
52
|
utils_1.logger.error(`❌ CLI failed: ${err}`);
|
package/dist/config/constants.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.CONFIG_FILE = exports.documentation = exports.DEFAULT_COMMANDS = exports.SOURCE_EXTENSIONS = exports.CONFIG_FILE_PATTERNS = exports.IGNORE_PATTERNS = void 0;
|
|
6
|
+
exports.CONFIG_FILE = exports.documentation = exports.DEFAULT_ALLOW_PACKAGES = exports.DEFAULT_COMMANDS = exports.SOURCE_EXTENSIONS = exports.CONFIG_FILE_PATTERNS = exports.IGNORE_PATTERNS = void 0;
|
|
7
7
|
const node_path_1 = __importDefault(require("node:path"));
|
|
8
8
|
/**
|
|
9
9
|
* Common patterns to ignore in all checks
|
|
@@ -89,6 +89,20 @@ exports.DEFAULT_COMMANDS = {
|
|
|
89
89
|
'npx pr-checkmate pr-body': 'Validate PR description',
|
|
90
90
|
'npx pr-checkmate duplicate': 'Detect duplicate code blocks (jscpd)',
|
|
91
91
|
};
|
|
92
|
+
/**
|
|
93
|
+
* Packages allow-listed for the license check by default.
|
|
94
|
+
*
|
|
95
|
+
* These are pr-checkmate's own bundled cspell dictionaries. Some ship under
|
|
96
|
+
* copyleft licenses (GPL/LGPL), but they are pr-checkmate dependencies — not
|
|
97
|
+
* the consumer's — so clients should never have to allow-list them manually.
|
|
98
|
+
*/
|
|
99
|
+
exports.DEFAULT_ALLOW_PACKAGES = [
|
|
100
|
+
'@cspell/dict-es-es',
|
|
101
|
+
'@cspell/dict-fr-fr',
|
|
102
|
+
'@cspell/dict-he',
|
|
103
|
+
'@cspell/dict-ru_ru',
|
|
104
|
+
'@cspell/dict-uk-ua',
|
|
105
|
+
];
|
|
92
106
|
exports.documentation = {
|
|
93
107
|
eslint: 'https://eslint.org/docs/latest/rules/',
|
|
94
108
|
typescriptEslint: 'https://typescript-eslint.io/rules/',
|
package/dist/config/loader.js
CHANGED
|
@@ -9,6 +9,7 @@ exports.getDefaultConfig = getDefaultConfig;
|
|
|
9
9
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
10
10
|
const node_path_1 = __importDefault(require("node:path"));
|
|
11
11
|
const utils_1 = require("../utils");
|
|
12
|
+
const constants_1 = require("./constants");
|
|
12
13
|
/**
|
|
13
14
|
* Load project configuration from pr-checkmate.json
|
|
14
15
|
* Falls back to defaults if not found
|
|
@@ -55,6 +56,11 @@ function mergeWithDefaults(userConfig) {
|
|
|
55
56
|
},
|
|
56
57
|
security: userConfig.security || defaults.security,
|
|
57
58
|
typecheck: { ...defaults.typecheck, ...userConfig.typecheck },
|
|
59
|
+
cpp: {
|
|
60
|
+
...defaults.cpp,
|
|
61
|
+
...userConfig.cpp,
|
|
62
|
+
clangFormat: { ...defaults.cpp?.clangFormat, ...userConfig.cpp?.clangFormat },
|
|
63
|
+
},
|
|
58
64
|
diffSmell: { ...defaults.diffSmell, ...userConfig.diffSmell },
|
|
59
65
|
prBody: { ...defaults.prBody, ...userConfig.prBody },
|
|
60
66
|
duplicate: { ...defaults.duplicate, ...userConfig.duplicate },
|
|
@@ -126,6 +132,7 @@ function getDefaultConfig() {
|
|
|
126
132
|
'npm-audit': { enabled: true, auditLevel: 'moderate' },
|
|
127
133
|
},
|
|
128
134
|
typecheck: { enabled: true },
|
|
135
|
+
cpp: { enabled: true, clangFormat: { style: 'LLVM' } },
|
|
129
136
|
diffSmell: { enabled: true },
|
|
130
137
|
prBody: { enabled: true, minLength: 20, requireTicket: true },
|
|
131
138
|
duplicate: { enabled: true, minLines: 5, minTokens: 50 },
|
|
@@ -145,6 +152,10 @@ function getDefaultConfig() {
|
|
|
145
152
|
'build',
|
|
146
153
|
],
|
|
147
154
|
},
|
|
155
|
+
licenseCheck: {
|
|
156
|
+
allowPackages: [...constants_1.DEFAULT_ALLOW_PACKAGES],
|
|
157
|
+
allowLicenses: [],
|
|
158
|
+
},
|
|
148
159
|
};
|
|
149
160
|
}
|
|
150
161
|
/**
|
|
@@ -0,0 +1,34 @@
|
|
|
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.circularDepsCheck = void 0;
|
|
7
|
+
const execa_1 = require("execa");
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const utils_1 = require("../../utils");
|
|
10
|
+
const config_1 = require("../../config");
|
|
11
|
+
const types_1 = require("../types");
|
|
12
|
+
exports.circularDepsCheck = {
|
|
13
|
+
name: 'Circular Deps',
|
|
14
|
+
phase: 'blocking',
|
|
15
|
+
applies(ctx) {
|
|
16
|
+
return ctx.languages.includes('typescript') || ctx.languages.includes('javascript');
|
|
17
|
+
},
|
|
18
|
+
async run(ctx) {
|
|
19
|
+
const pattern = node_path_1.default.join(ctx.cwd, (0, config_1.getSourcePath)(), '**/*.{ts,tsx,js,jsx}');
|
|
20
|
+
const result = await (0, execa_1.execa)('npx', ['dpdm', '--no-warning', '--no-tree', '--circular', pattern], {
|
|
21
|
+
cwd: ctx.cwd,
|
|
22
|
+
reject: false,
|
|
23
|
+
});
|
|
24
|
+
const output = (result.stdout + result.stderr).trim();
|
|
25
|
+
// dpdm's success message also contains "circular" — match only its error shapes:
|
|
26
|
+
// the "Circular Dependencies:" header or a numbered entry "[1] src/a.ts -> src/b.ts".
|
|
27
|
+
const hasCircular = /circular dependencies:/i.test(output) || /^\s*\[\d+\]/m.test(output);
|
|
28
|
+
if (hasCircular) {
|
|
29
|
+
utils_1.logger.error(`[Circular Deps]: ❌ circular dependencies detected:\n${output}`);
|
|
30
|
+
return (0, types_1.fail)('circular dependencies found');
|
|
31
|
+
}
|
|
32
|
+
return (0, types_1.pass)();
|
|
33
|
+
},
|
|
34
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
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.commitlintCheck = void 0;
|
|
7
|
+
const execa_1 = require("execa");
|
|
8
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
9
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
11
|
+
const utils_1 = require("../../utils");
|
|
12
|
+
const config_1 = require("../../config");
|
|
13
|
+
const types_1 = require("../types");
|
|
14
|
+
const LOCAL_CONFIG_FILES = [
|
|
15
|
+
'commitlint.config.cjs',
|
|
16
|
+
'commitlint.config.js',
|
|
17
|
+
'commitlint.config.mjs',
|
|
18
|
+
'commitlint.config.ts',
|
|
19
|
+
'.commitlintrc',
|
|
20
|
+
'.commitlintrc.json',
|
|
21
|
+
'.commitlintrc.yaml',
|
|
22
|
+
'.commitlintrc.yml',
|
|
23
|
+
'.commitlintrc.js',
|
|
24
|
+
'.commitlintrc.cjs',
|
|
25
|
+
];
|
|
26
|
+
const DEFAULT_TYPES = [
|
|
27
|
+
'feat',
|
|
28
|
+
'fix',
|
|
29
|
+
'docs',
|
|
30
|
+
'chore',
|
|
31
|
+
'ci',
|
|
32
|
+
'refactor',
|
|
33
|
+
'perf',
|
|
34
|
+
'style',
|
|
35
|
+
'revert',
|
|
36
|
+
'test',
|
|
37
|
+
'build',
|
|
38
|
+
];
|
|
39
|
+
function buildTempConfig(types) {
|
|
40
|
+
const config = {
|
|
41
|
+
// Resolve via Node module resolution — robust to npm hoisting in the client.
|
|
42
|
+
extends: [require.resolve('@commitlint/config-conventional')],
|
|
43
|
+
rules: { 'type-enum': [2, 'always', types] },
|
|
44
|
+
};
|
|
45
|
+
const tmpFile = node_path_1.default.join(node_os_1.default.tmpdir(), `.commitlint-${process.pid}.json`);
|
|
46
|
+
node_fs_1.default.writeFileSync(tmpFile, JSON.stringify(config, null, 2));
|
|
47
|
+
return tmpFile;
|
|
48
|
+
}
|
|
49
|
+
exports.commitlintCheck = {
|
|
50
|
+
name: 'Commitlint',
|
|
51
|
+
phase: 'informational',
|
|
52
|
+
applies(ctx) {
|
|
53
|
+
return ctx.config.commitlint?.enabled !== false;
|
|
54
|
+
},
|
|
55
|
+
async run(ctx) {
|
|
56
|
+
const commitlintBin = (0, utils_1.resolvePackageBin)('@commitlint/cli', 'cli.js');
|
|
57
|
+
const hasLocal = (0, config_1.hasLocalConfig)(LOCAL_CONFIG_FILES);
|
|
58
|
+
let tempConfigFile = null;
|
|
59
|
+
const from = ctx.baseSha ?? 'HEAD~1';
|
|
60
|
+
const to = ctx.headSha ?? 'HEAD';
|
|
61
|
+
const args = ['--from', from, '--to', to, '--verbose'];
|
|
62
|
+
try {
|
|
63
|
+
if (hasLocal) {
|
|
64
|
+
utils_1.logger.info('[Commitlint]: Using local config');
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
const types = ctx.config.commitlint?.types ?? DEFAULT_TYPES;
|
|
68
|
+
tempConfigFile = buildTempConfig(types);
|
|
69
|
+
args.push('--config', tempConfigFile);
|
|
70
|
+
}
|
|
71
|
+
const result = await (0, execa_1.execa)(process.execPath, [commitlintBin, ...args], {
|
|
72
|
+
cwd: ctx.cwd,
|
|
73
|
+
reject: false,
|
|
74
|
+
stdio: 'inherit',
|
|
75
|
+
});
|
|
76
|
+
return result.exitCode === 0 ? (0, types_1.pass)() : (0, types_1.warn)('commit message violations found');
|
|
77
|
+
}
|
|
78
|
+
finally {
|
|
79
|
+
if (tempConfigFile && node_fs_1.default.existsSync(tempConfigFile))
|
|
80
|
+
node_fs_1.default.unlinkSync(tempConfigFile);
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
};
|
|
@@ -0,0 +1,120 @@
|
|
|
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.cppFormatCheck = void 0;
|
|
7
|
+
const execa_1 = require("execa");
|
|
8
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
9
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
+
const utils_1 = require("../../utils");
|
|
11
|
+
const types_1 = require("../types");
|
|
12
|
+
/**
|
|
13
|
+
* C/C++ source & header extensions clang-format understands. `.h` is intentionally
|
|
14
|
+
* included even though it is ambiguous with C — clang-format handles both.
|
|
15
|
+
*/
|
|
16
|
+
const CPP_GLOBS = ['*.c', '*.cc', '*.cpp', '*.cxx', '*.c++', '*.h', '*.hh', '*.hpp', '*.hxx'];
|
|
17
|
+
const DEFAULT_STYLE = 'LLVM';
|
|
18
|
+
/**
|
|
19
|
+
* Resolves the clang-format style argument. Prefers the project's own `.clang-format`
|
|
20
|
+
* (via `--style=file`) so pr-checkmate never overrides a repo's established rules;
|
|
21
|
+
* falls back to the configured style, then LLVM.
|
|
22
|
+
*/
|
|
23
|
+
function resolveStyleArg(ctx) {
|
|
24
|
+
if (node_fs_1.default.existsSync(node_path_1.default.join(ctx.cwd, '.clang-format'))) {
|
|
25
|
+
return '--style=file';
|
|
26
|
+
}
|
|
27
|
+
const style = ctx.config.cpp?.clangFormat?.style ?? DEFAULT_STYLE;
|
|
28
|
+
return `--style=${style}`;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Returns the C/C++ files to check. Uses the PR diff range when available
|
|
32
|
+
* (delta mode), else scans all tracked C/C++ files. Returns an empty array
|
|
33
|
+
* when there is nothing to check, `null` when git is unusable.
|
|
34
|
+
*/
|
|
35
|
+
async function resolveTargets(ctx) {
|
|
36
|
+
try {
|
|
37
|
+
if (ctx.baseSha) {
|
|
38
|
+
const { stdout } = await (0, execa_1.execa)('git', [
|
|
39
|
+
'diff',
|
|
40
|
+
'--name-only',
|
|
41
|
+
'--diff-filter=ACMR',
|
|
42
|
+
ctx.baseSha,
|
|
43
|
+
ctx.headSha ?? 'HEAD',
|
|
44
|
+
'--',
|
|
45
|
+
...CPP_GLOBS,
|
|
46
|
+
], { cwd: ctx.cwd, reject: false });
|
|
47
|
+
const files = stdout
|
|
48
|
+
.split('\n')
|
|
49
|
+
.map(f => f.trim())
|
|
50
|
+
.filter(Boolean);
|
|
51
|
+
utils_1.logger.info(`[C++ Format]: 📂 Delta mode: ${files.length} changed C/C++ file(s)`);
|
|
52
|
+
return files;
|
|
53
|
+
}
|
|
54
|
+
const { stdout } = await (0, execa_1.execa)('git', ['ls-files', ...CPP_GLOBS], {
|
|
55
|
+
cwd: ctx.cwd,
|
|
56
|
+
reject: false,
|
|
57
|
+
});
|
|
58
|
+
const files = stdout
|
|
59
|
+
.split('\n')
|
|
60
|
+
.map(f => f.trim())
|
|
61
|
+
.filter(Boolean);
|
|
62
|
+
utils_1.logger.info(`[C++ Format]: Full scan: ${files.length} tracked C/C++ file(s)`);
|
|
63
|
+
return files;
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
utils_1.logger.warn(`[C++ Format]: ⚠️ Could not list C/C++ files\n${err}`);
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/** Counts distinct files flagged in clang-format's `--dry-run --Werror` diagnostics. */
|
|
71
|
+
function countOffendingFiles(stderr) {
|
|
72
|
+
const files = new Set();
|
|
73
|
+
for (const line of stderr.split('\n')) {
|
|
74
|
+
const match = line.match(/^(.+?):\d+:\d+: (?:error|warning):/);
|
|
75
|
+
if (match)
|
|
76
|
+
files.add(match[1]);
|
|
77
|
+
}
|
|
78
|
+
return files.size;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Formats C/C++ code with clang-format, bundled as a self-contained WASM CLI
|
|
82
|
+
* (`@wasm-fmt/clang-format`) so it runs on a bare Node image with no system
|
|
83
|
+
* clang install. Read-only in check mode; rewrites files in place when `write`.
|
|
84
|
+
*/
|
|
85
|
+
exports.cppFormatCheck = {
|
|
86
|
+
name: 'C++ Format',
|
|
87
|
+
phase: 'format',
|
|
88
|
+
applies(ctx) {
|
|
89
|
+
return ctx.config.cpp?.enabled !== false && ctx.languages.includes('cpp');
|
|
90
|
+
},
|
|
91
|
+
async run(ctx) {
|
|
92
|
+
const targets = await resolveTargets(ctx);
|
|
93
|
+
if (targets === null)
|
|
94
|
+
return (0, types_1.skip)('git unavailable');
|
|
95
|
+
if (targets.length === 0)
|
|
96
|
+
return (0, types_1.pass)('no C/C++ files changed');
|
|
97
|
+
const cfBin = (0, utils_1.resolvePackageBin)('@wasm-fmt/clang-format', 'clang-format-cli.cjs');
|
|
98
|
+
const styleArg = resolveStyleArg(ctx);
|
|
99
|
+
// Detect unformatted files without mutating anything.
|
|
100
|
+
const dry = await (0, execa_1.execa)(process.execPath, [cfBin, '--dry-run', '--Werror', styleArg, ...targets], { cwd: ctx.cwd, reject: false });
|
|
101
|
+
if (dry.exitCode === 0)
|
|
102
|
+
return (0, types_1.pass)();
|
|
103
|
+
const offending = countOffendingFiles(dry.stderr) || targets.length;
|
|
104
|
+
if (!ctx.write) {
|
|
105
|
+
utils_1.logger.warn(`[C++ Format]: ⚠️ ${offending} C/C++ file(s) need formatting`);
|
|
106
|
+
return (0, types_1.warn)(`${offending} file(s) need formatting (run with write to fix)`);
|
|
107
|
+
}
|
|
108
|
+
// write mode: rewrite the offending files in place so CI can auto-commit them.
|
|
109
|
+
const fix = await (0, execa_1.execa)(process.execPath, [cfBin, '-i', styleArg, ...targets], {
|
|
110
|
+
cwd: ctx.cwd,
|
|
111
|
+
reject: false,
|
|
112
|
+
});
|
|
113
|
+
if (fix.exitCode !== 0) {
|
|
114
|
+
utils_1.logger.warn(`[C++ Format]: ⚠️ clang-format -i failed\n${fix.stderr}`);
|
|
115
|
+
return (0, types_1.warn)('clang-format failed to rewrite files');
|
|
116
|
+
}
|
|
117
|
+
utils_1.logger.info(`[C++ Format]: ✅ Reformatted ${offending} C/C++ file(s)`);
|
|
118
|
+
return (0, types_1.pass)(`reformatted ${offending} file(s)`);
|
|
119
|
+
},
|
|
120
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
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.dependencyCheck = void 0;
|
|
7
|
+
const execa_1 = require("execa");
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
10
|
+
const utils_1 = require("../../utils");
|
|
11
|
+
const types_1 = require("../types");
|
|
12
|
+
exports.dependencyCheck = {
|
|
13
|
+
name: 'Dependencies',
|
|
14
|
+
phase: 'blocking',
|
|
15
|
+
applies(ctx) {
|
|
16
|
+
return node_fs_1.default.existsSync(node_path_1.default.join(ctx.cwd, 'package.json'));
|
|
17
|
+
},
|
|
18
|
+
async run(ctx) {
|
|
19
|
+
let stdout;
|
|
20
|
+
try {
|
|
21
|
+
({ stdout } = await (0, execa_1.execa)('npx', ['depcheck', '--json'], { cwd: ctx.cwd }));
|
|
22
|
+
}
|
|
23
|
+
catch (err) {
|
|
24
|
+
// depcheck exits non-zero when it finds issues but still emits JSON on stdout.
|
|
25
|
+
if (err instanceof execa_1.ExecaError && err.stdout) {
|
|
26
|
+
stdout = err.stdout;
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
throw err;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const start = stdout.indexOf('{');
|
|
33
|
+
const end = stdout.lastIndexOf('}');
|
|
34
|
+
const json = start !== -1 && end !== -1 ? stdout.slice(start, end + 1) : stdout;
|
|
35
|
+
let result;
|
|
36
|
+
try {
|
|
37
|
+
result = JSON.parse(json);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return (0, types_1.fail)('depcheck produced invalid JSON');
|
|
41
|
+
}
|
|
42
|
+
const unused = result.dependencies ?? [];
|
|
43
|
+
const missing = Object.keys(result.missing ?? {});
|
|
44
|
+
if (missing.length > 0) {
|
|
45
|
+
missing.forEach(dep => utils_1.logger.error(`[Dependencies]: ❌ missing ${dep} (used in: ${result.missing[dep]?.join(', ')})`));
|
|
46
|
+
return (0, types_1.fail)(`missing: ${missing.join(', ')}`);
|
|
47
|
+
}
|
|
48
|
+
if (unused.length > 0) {
|
|
49
|
+
unused.forEach(dep => utils_1.logger.warn(`[Dependencies]: ⚠️ unused ${dep}`));
|
|
50
|
+
return (0, types_1.warn)(`${unused.length} unused dependency(ies)`);
|
|
51
|
+
}
|
|
52
|
+
return (0, types_1.pass)();
|
|
53
|
+
},
|
|
54
|
+
};
|