pr-checkmate 1.21.0 → 1.21.2

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.
@@ -0,0 +1,119 @@
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.runPrSummary = runPrSummary;
7
+ const utils_1 = require("../utils");
8
+ const result_collector_1 = require("../utils/result-collector");
9
+ const node_fs_1 = __importDefault(require("node:fs"));
10
+ const STATUS_ICON = {
11
+ pass: '✅',
12
+ warn: '⚠️',
13
+ fail: '❌',
14
+ skip: '⏭️',
15
+ };
16
+ // Unique marker so we can find and update the comment on re-runs
17
+ const COMMENT_MARKER = '<!-- pr-checkmate-summary -->';
18
+ function getPrContext() {
19
+ const token = process.env.GITHUB_TOKEN;
20
+ const repository = process.env.GITHUB_REPOSITORY;
21
+ const eventPath = process.env.GITHUB_EVENT_PATH;
22
+ if (!token || !repository || !eventPath || !node_fs_1.default.existsSync(eventPath)) {
23
+ return null;
24
+ }
25
+ try {
26
+ const event = JSON.parse(node_fs_1.default.readFileSync(eventPath, 'utf-8'));
27
+ const prNumber = event?.pull_request?.number;
28
+ if (!prNumber)
29
+ return null;
30
+ const [owner, repo] = repository.split('/');
31
+ return { token, owner, repo, prNumber };
32
+ }
33
+ catch {
34
+ return null;
35
+ }
36
+ }
37
+ function buildMarkdownReport(results) {
38
+ const { passed, warned, failed, skipped } = result_collector_1.resultCollector.getSummary();
39
+ const rows = results
40
+ .map(r => {
41
+ const icon = STATUS_ICON[r.status];
42
+ const details = r.details ? ` — ${r.details}` : '';
43
+ return `| ${r.name} | ${icon} ${r.status}${details} |`;
44
+ })
45
+ .join('\n');
46
+ return [
47
+ COMMENT_MARKER,
48
+ '## 🔍 PR CheckMate Summary',
49
+ '',
50
+ `**${passed} passed** · **${warned} warnings** · **${failed} failed** · **${skipped} skipped**`,
51
+ '',
52
+ '| Check | Result |',
53
+ '|---|---|',
54
+ rows,
55
+ '',
56
+ '_Generated by [PR CheckMate](https://www.npmjs.com/package/pr-checkmate)_',
57
+ ].join('\n');
58
+ }
59
+ async function upsertGithubComment(body) {
60
+ const ctx = getPrContext();
61
+ if (!ctx) {
62
+ utils_1.logger.info('[runPrSummary]: Skipping GitHub comment (not in PR context or GITHUB_TOKEN missing)');
63
+ return;
64
+ }
65
+ const { token, owner, repo, prNumber } = ctx;
66
+ const apiBase = `https://api.github.com/repos/${owner}/${repo}`;
67
+ const headers = {
68
+ Authorization: `Bearer ${token}`,
69
+ 'User-Agent': 'pr-checkmate',
70
+ Accept: 'application/vnd.github.v3+json',
71
+ 'Content-Type': 'application/json',
72
+ };
73
+ try {
74
+ // Look for an existing pr-checkmate comment to update (avoids spam on repeated pushes)
75
+ const listRes = await fetch(`${apiBase}/issues/${prNumber}/comments`, { headers });
76
+ if (listRes.ok) {
77
+ const comments = (await listRes.json());
78
+ const existing = comments.find(c => typeof c.body === 'string' && c.body.includes(COMMENT_MARKER));
79
+ if (existing) {
80
+ await fetch(`${apiBase}/issues/comments/${existing.id}`, {
81
+ method: 'PATCH',
82
+ headers,
83
+ body: JSON.stringify({ body }),
84
+ });
85
+ utils_1.logger.info('[runPrSummary]: ✅ Updated PR CheckMate comment on PR');
86
+ return;
87
+ }
88
+ }
89
+ // No existing comment — post a new one
90
+ await fetch(`${apiBase}/issues/${prNumber}/comments`, {
91
+ method: 'POST',
92
+ headers,
93
+ body: JSON.stringify({ body }),
94
+ });
95
+ utils_1.logger.info('[runPrSummary]: ✅ Posted PR CheckMate summary comment to PR');
96
+ }
97
+ catch (err) {
98
+ utils_1.logger.warn(`[runPrSummary]: ⚠️ Could not post GitHub comment: ${err}`);
99
+ }
100
+ }
101
+ async function runPrSummary() {
102
+ const results = result_collector_1.resultCollector.getResults();
103
+ if (results.length === 0)
104
+ return;
105
+ const { passed, warned, failed, skipped } = result_collector_1.resultCollector.getSummary();
106
+ utils_1.logger.info('');
107
+ utils_1.logger.info('═══════════════════════════════════════════════════════════════');
108
+ utils_1.logger.info(' PR CheckMate Summary');
109
+ utils_1.logger.info('═══════════════════════════════════════════════════════════════');
110
+ for (const r of results) {
111
+ const icon = STATUS_ICON[r.status];
112
+ const details = r.details ? `: ${r.details}` : '';
113
+ utils_1.logger.info(` ${icon} ${r.name}${details}`);
114
+ }
115
+ utils_1.logger.info('───────────────────────────────────────────────────────────────');
116
+ utils_1.logger.info(` ✅ ${passed} passed ⚠️ ${warned} warnings ❌ ${failed} failed ⏭️ ${skipped} skipped`);
117
+ utils_1.logger.info('═══════════════════════════════════════════════════════════════');
118
+ await upsertGithubComment(buildMarkdownReport(results));
119
+ }
@@ -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.runTypecheck = runTypecheck;
7
+ const execa_1 = require("execa");
8
+ const utils_1 = require("../utils");
9
+ const loader_1 = require("../config/loader");
10
+ const node_fs_1 = __importDefault(require("node:fs"));
11
+ const node_path_1 = __importDefault(require("node:path"));
12
+ async function runTypecheck() {
13
+ const cfg = (0, loader_1.loadProjectConfig)();
14
+ if (cfg.typecheck?.enabled === false) {
15
+ utils_1.logger.info('[runTypecheck]: Disabled in pr-checkmate.json, skipping');
16
+ return;
17
+ }
18
+ utils_1.logger.info('[runTypecheck]: Running TypeScript type check...');
19
+ const tsConfigPath = node_path_1.default.join(process.cwd(), 'tsconfig.json');
20
+ if (!node_fs_1.default.existsSync(tsConfigPath)) {
21
+ utils_1.logger.info('[runTypecheck]: No tsconfig.json found, skipping');
22
+ return;
23
+ }
24
+ const result = await (0, execa_1.execa)('npx', ['tsc', '--noEmit'], { reject: false });
25
+ if (result.exitCode !== 0) {
26
+ const output = [result.stdout, result.stderr].filter(Boolean).join('\n');
27
+ const errorCount = (output.match(/error TS\d+/g) ?? []).length;
28
+ utils_1.logger.warn(`[runTypecheck]: ⚠️ TypeScript found ${errorCount} type error(s):`);
29
+ if (output)
30
+ utils_1.logger.warn(output);
31
+ throw new Error(`TypeScript: ${errorCount} type error(s)`);
32
+ }
33
+ utils_1.logger.info('[runTypecheck]: ✅ TypeScript type check passed');
34
+ }
@@ -22,6 +22,7 @@ const node_fs_1 = __importDefault(require("node:fs"));
22
22
  const logger_1 = require("./logger");
23
23
  __exportStar(require("./logger"), exports);
24
24
  __exportStar(require("./delta"), exports);
25
+ __exportStar(require("./result-collector"), exports);
25
26
  function cleanupTempFile(filePath) {
26
27
  try {
27
28
  if (node_fs_1.default.existsSync(filePath)) {
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resultCollector = void 0;
4
+ class ResultCollector {
5
+ constructor() {
6
+ this.results = [];
7
+ }
8
+ register(name, status, details) {
9
+ const idx = this.results.findIndex(r => r.name === name);
10
+ const entry = { name, status, ...(details && { details }) };
11
+ if (idx >= 0) {
12
+ this.results[idx] = entry;
13
+ }
14
+ else {
15
+ this.results.push(entry);
16
+ }
17
+ }
18
+ getResults() {
19
+ return [...this.results];
20
+ }
21
+ clear() {
22
+ this.results = [];
23
+ }
24
+ getSummary() {
25
+ return {
26
+ passed: this.results.filter(r => r.status === 'pass').length,
27
+ warned: this.results.filter(r => r.status === 'warn').length,
28
+ failed: this.results.filter(r => r.status === 'fail').length,
29
+ skipped: this.results.filter(r => r.status === 'skip').length,
30
+ };
31
+ }
32
+ }
33
+ exports.resultCollector = new ResultCollector();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pr-checkmate",
3
- "version": "1.21.0",
3
+ "version": "1.21.2",
4
4
  "description": "Automated PR quality checks: linting, formatting, dependency analysis, and spellcheck",
5
5
  "keywords": [
6
6
  "github-actions",
@@ -26,6 +26,7 @@
26
26
  ".eslintignore",
27
27
  "cspell.json",
28
28
  "src/config/pr-checkmate-workflow.yml",
29
+ ".markdownlint.json",
29
30
  "LICENSE"
30
31
  ],
31
32
  "scripts": {
@@ -73,6 +74,8 @@
73
74
  "eslint-plugin-prettier": "^5.5.4",
74
75
  "execa": "^9.6.0",
75
76
  "gitleaks-secret-scanner-fixed": "^2.1.3",
77
+ "jscpd": "^3.5.10",
78
+ "markdownlint-cli": "^0.45.0",
76
79
  "inquirer": "^13.0.1",
77
80
  "prettier": ">=3.0.0",
78
81
  "ts-node": "^10.9.1",
@@ -242,3 +242,112 @@ jobs:
242
242
  - name: Check Outdated Dependencies
243
243
  run: npx pr-checkmate outdated
244
244
  continue-on-error: true
245
+
246
+ markdown-lint:
247
+ name: Markdown Lint
248
+ runs-on: ubuntu-latest
249
+ steps:
250
+ - name: Checkout code
251
+ uses: actions/checkout@v4
252
+ with:
253
+ fetch-depth: 0
254
+
255
+ - name: Setup Node.js
256
+ uses: actions/setup-node@v4
257
+ with:
258
+ node-version: 20
259
+
260
+ - name: Install dependencies
261
+ run: npm ci
262
+
263
+ - name: Run Markdown Lint
264
+ run: npx pr-checkmate markdown
265
+ continue-on-error: true
266
+
267
+ typecheck:
268
+ name: TypeScript Type Check
269
+ runs-on: ubuntu-latest
270
+ steps:
271
+ - name: Checkout code
272
+ uses: actions/checkout@v4
273
+ with:
274
+ ref: ${{ github.head_ref }}
275
+ fetch-depth: 0
276
+
277
+ - name: Setup Node.js
278
+ uses: actions/setup-node@v4
279
+ with:
280
+ node-version: 20
281
+
282
+ - name: Install dependencies
283
+ run: npm ci
284
+
285
+ - name: Run TypeScript Type Check
286
+ run: npx pr-checkmate typecheck
287
+ continue-on-error: true
288
+
289
+ diff-smell:
290
+ name: Diff Code Smell
291
+ runs-on: ubuntu-latest
292
+ steps:
293
+ - name: Checkout code
294
+ uses: actions/checkout@v4
295
+ with:
296
+ ref: ${{ github.head_ref }}
297
+ fetch-depth: 0
298
+
299
+ - name: Setup Node.js
300
+ uses: actions/setup-node@v4
301
+ with:
302
+ node-version: 20
303
+
304
+ - name: Install dependencies
305
+ run: npm ci
306
+
307
+ - name: Scan Diff for Code Smells
308
+ run: npx pr-checkmate diff-smell
309
+ continue-on-error: true
310
+
311
+ pr-body:
312
+ name: PR Description
313
+ runs-on: ubuntu-latest
314
+ steps:
315
+ - name: Checkout code
316
+ uses: actions/checkout@v4
317
+ with:
318
+ ref: ${{ github.head_ref }}
319
+ fetch-depth: 0
320
+
321
+ - name: Setup Node.js
322
+ uses: actions/setup-node@v4
323
+ with:
324
+ node-version: 20
325
+
326
+ - name: Install dependencies
327
+ run: npm ci
328
+
329
+ - name: Check PR Description
330
+ run: npx pr-checkmate pr-body
331
+ continue-on-error: true
332
+
333
+ duplicate-check:
334
+ name: Duplicate Code
335
+ runs-on: ubuntu-latest
336
+ steps:
337
+ - name: Checkout code
338
+ uses: actions/checkout@v4
339
+ with:
340
+ ref: ${{ github.head_ref }}
341
+ fetch-depth: 0
342
+
343
+ - name: Setup Node.js
344
+ uses: actions/setup-node@v4
345
+ with:
346
+ node-version: 20
347
+
348
+ - name: Install dependencies
349
+ run: npm ci
350
+
351
+ - name: Check for Duplicate Code
352
+ run: npx pr-checkmate duplicate
353
+ continue-on-error: true