pr-checkmate 1.23.4 → 1.23.6

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/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
  ![jscpd](https://img.shields.io/badge/jscpd-5-blue)
10
10
  [![API Docs](https://img.shields.io/badge/API_docs-TypeDoc-9600FF?logo=readthedocs&logoColor=white)](https://pr-checkmate-docs.pages.dev)
11
11
 
12
- > PR CheckMate is a universal PR quality checker: one `npm install`, 49 checks across
12
+ > PR CheckMate is a universal PR quality checker: one `npm install`, 50 checks across
13
13
  > 11 languages, running in CI on every pull request. The bundled languages need no
14
14
  > per-language toolchain setup.
15
15
 
@@ -108,7 +108,7 @@ skipped, not failed.
108
108
 
109
109
  ## Checks
110
110
 
111
- 49 checks, grouped by category and run in parallel within each phase:
111
+ 50 checks, grouped by category and run in parallel within each phase:
112
112
 
113
113
  - Universal git-diff (18) — language-agnostic and bundled: merge-conflict markers,
114
114
  secret scan, diff security (JS/TS/Python/Go/Kotlin), GitHub Actions and Dockerfile
@@ -118,7 +118,7 @@ skipped, not failed.
118
118
  - Dependencies (7) — unused/missing, circular, outdated, license compliance, npm audit,
119
119
  a cross-language vuln scan (osv-scanner, opt-in), and a direct-dependency vuln scan
120
120
  (grype, on by default).
121
- - Quality (4) — duplicate code, dead code, multi-language spellcheck, markdown lint.
121
+ - Quality (5) — duplicate code, dead code, multi-language spellcheck, markdown lint, YAML lint.
122
122
  - PR hygiene (4) — size, title, body, and commit-message convention.
123
123
  - Per-language (16) — lint, format, and type-check for the languages listed above.
124
124
 
package/dist/cli.js CHANGED
@@ -19,6 +19,7 @@ const COMMAND_TO_CHECK = {
19
19
  'pr-title': 'PR Title',
20
20
  outdated: 'Outdated Deps',
21
21
  markdown: 'Markdown',
22
+ 'yaml-lint': 'YAML Lint',
22
23
  typecheck: 'TypeScript',
23
24
  'diff-smell': 'Diff Smells',
24
25
  'diff-security': 'Diff Security',
@@ -240,6 +240,7 @@ exports.KNOWN_CONFIG_KEYS = new Set([
240
240
  'spellcheck',
241
241
  'cspell',
242
242
  'markdownlint',
243
+ 'yamlLint',
243
244
  'typecheck',
244
245
  // security
245
246
  'security',
@@ -59,7 +59,12 @@ function mergeWithDefaults(userConfig) {
59
59
  ...(userConfig.spellcheck?.ignorePaths || []),
60
60
  ],
61
61
  },
62
- security: userConfig.security || defaults.security,
62
+ security: {
63
+ ...defaults.security,
64
+ ...userConfig.security,
65
+ gitleaks: { ...defaults.security?.gitleaks, ...userConfig.security?.gitleaks },
66
+ 'npm-audit': { ...defaults.security?.['npm-audit'], ...userConfig.security?.['npm-audit'] },
67
+ },
63
68
  typecheck: { ...defaults.typecheck, ...userConfig.typecheck },
64
69
  cpp: {
65
70
  ...defaults.cpp,
@@ -38,10 +38,17 @@ exports.npmAuditCheck = {
38
38
  },
39
39
  async run(ctx) {
40
40
  const auditLevel = ctx.config.security?.['npm-audit']?.auditLevel ?? 'moderate';
41
+ const ignore = new Set((ctx.config.security?.['npm-audit']?.ignore ?? []).map(name => name.toLowerCase()));
41
42
  const ownName = getOwnPackageName();
42
43
  const { stdout } = await (0, execa_1.execa)('npm', ['audit', '--json'], { cwd: ctx.cwd, reject: false });
43
44
  const report = JSON.parse(stdout);
44
- const directVulns = Object.values(report.vulnerabilities ?? {}).filter(v => v.isDirect && v.name !== ownName && isSeverityAtLeast(v.severity, auditLevel));
45
+ const relevant = Object.values(report.vulnerabilities ?? {}).filter(v => v.isDirect && v.name !== ownName && isSeverityAtLeast(v.severity, auditLevel));
46
+ const [ignored, directVulns] = relevant.reduce(([skip, block], v) => {
47
+ (ignore.has(v.name.toLowerCase()) ? skip : block).push(v);
48
+ return [skip, block];
49
+ }, [[], []]);
50
+ // Ignored advisories stay visible as warnings — an exception is not a silent drop.
51
+ ignored.forEach(v => utils_1.logger.warn(`[NPM Audit]: ⚠ ${v.name} — severity: ${v.severity} (ignored via config)`));
45
52
  if (directVulns.length === 0)
46
53
  return (0, types_1.pass)();
47
54
  directVulns.forEach(v => utils_1.logger.error(`[NPM Audit]: ❌ ${v.name} — severity: ${v.severity}`));
@@ -0,0 +1,13 @@
1
+ import { Check } from '../../types';
2
+ /**
3
+ * Bundled YAML linter (the `yaml` package — no runner dependency, always
4
+ * available like Markdown/Spellcheck). Parses every changed `.yml`/`.yaml` file
5
+ * and reports the parser's own errors and warnings: syntax errors, duplicate map
6
+ * keys, tabs used as indentation, and other structural problems. Multi-document
7
+ * files (`---`-separated) are each parsed. ON by default — runs unless
8
+ * `yamlLint.enabled` is explicitly `false`. Findings are a hard error by default
9
+ * (a YAML problem fails the run); downgrade to advisory with
10
+ * `yamlLint.severity: "warn"`. Clients can exclude paths with `yamlLint.ignore`
11
+ * (glob patterns) in pr-checkmate.json.
12
+ */
13
+ export declare const yamlLintCheck: Check;
@@ -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.yamlLintCheck = void 0;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const yaml_1 = require("yaml");
10
+ const utils_1 = require("../../../utils");
11
+ const targets_1 = require("../../targets");
12
+ const types_1 = require("../../types");
13
+ const findings_1 = require("../findings");
14
+ /** First line of a `yaml` error/warning message, stripped of its source excerpt. */
15
+ function shortMessage(message) {
16
+ return message.split('\n')[0].trim();
17
+ }
18
+ /**
19
+ * Bundled YAML linter (the `yaml` package — no runner dependency, always
20
+ * available like Markdown/Spellcheck). Parses every changed `.yml`/`.yaml` file
21
+ * and reports the parser's own errors and warnings: syntax errors, duplicate map
22
+ * keys, tabs used as indentation, and other structural problems. Multi-document
23
+ * files (`---`-separated) are each parsed. ON by default — runs unless
24
+ * `yamlLint.enabled` is explicitly `false`. Findings are a hard error by default
25
+ * (a YAML problem fails the run); downgrade to advisory with
26
+ * `yamlLint.severity: "warn"`. Clients can exclude paths with `yamlLint.ignore`
27
+ * (glob patterns) in pr-checkmate.json.
28
+ */
29
+ exports.yamlLintCheck = {
30
+ name: 'YAML Lint',
31
+ phase: 'blocking',
32
+ applies(ctx) {
33
+ return ctx.config.yamlLint?.enabled !== false;
34
+ },
35
+ async run(ctx) {
36
+ const allTargets = await (0, targets_1.resolveTargetFiles)(ctx, ['*.yml', '*.yaml']);
37
+ if (allTargets === null)
38
+ return (0, types_1.skip)('git unavailable');
39
+ if (allTargets.length === 0)
40
+ return (0, types_1.skip)('no YAML files');
41
+ const ignore = ctx.config.yamlLint?.ignore ?? [];
42
+ const targets = ignore.length > 0 ? allTargets.filter(f => !(0, utils_1.matchesAnyGlob)(f, ignore)) : allTargets;
43
+ if (targets.length === 0)
44
+ return (0, types_1.skip)('all YAML files ignored');
45
+ const issues = [];
46
+ for (const file of targets) {
47
+ let source;
48
+ try {
49
+ source = node_fs_1.default.readFileSync(node_path_1.default.join(ctx.cwd, file), 'utf-8');
50
+ }
51
+ catch (err) {
52
+ utils_1.logger.warn(`[YAML Lint]: ⚠️ Could not read ${file}: ${err}`);
53
+ continue;
54
+ }
55
+ // parseAllDocuments never throws — it collects problems on each document,
56
+ // so a single malformed file cannot abort the whole check.
57
+ const docs = (0, yaml_1.parseAllDocuments)(source, { prettyErrors: true });
58
+ for (const doc of docs) {
59
+ for (const problem of [...doc.errors, ...doc.warnings]) {
60
+ const pos = problem.linePos?.[0];
61
+ issues.push({
62
+ file,
63
+ line: pos?.line,
64
+ col: pos?.col,
65
+ message: shortMessage(problem.message),
66
+ });
67
+ }
68
+ }
69
+ }
70
+ if (issues.length === 0)
71
+ return (0, types_1.pass)();
72
+ // YAML problems are a hard error by default; a repo may downgrade to `warn`
73
+ // via `yamlLint.severity`.
74
+ const sev = (0, findings_1.severityEmitter)(ctx.config.yamlLint?.severity, 'error');
75
+ issues.slice(0, 10).forEach(i => {
76
+ const where = i.line ? `:${i.line}${i.col ? `:${i.col}` : ''}` : '';
77
+ sev.log(`[YAML Lint]: ${sev.icon} ${i.file}${where} — ${i.message}`);
78
+ });
79
+ if (issues.length > 10)
80
+ sev.log(`[YAML Lint]: ... and ${issues.length - 10} more`);
81
+ return sev.emit(`${issues.length} YAML issue(s) found`);
82
+ },
83
+ };
@@ -45,6 +45,7 @@ const duplicate_check_1 = require("./checks/quality/duplicate-check");
45
45
  const license_header_1 = require("./checks/quality/license-header");
46
46
  const markdown_lint_1 = require("./checks/quality/markdown-lint");
47
47
  const spellcheck_1 = require("./checks/quality/spellcheck");
48
+ const yaml_lint_1 = require("./checks/quality/yaml-lint");
48
49
  const diff_security_1 = require("./checks/security/diff-security");
49
50
  const dockerfile_security_1 = require("./checks/security/dockerfile-security");
50
51
  const migration_safety_1 = require("./checks/security/migration-safety");
@@ -93,6 +94,7 @@ exports.REGISTRY = [
93
94
  outdated_deps_1.outdatedDepsCheck,
94
95
  markdown_lint_1.markdownLintCheck,
95
96
  spellcheck_1.spellcheckCheck,
97
+ yaml_lint_1.yamlLintCheck,
96
98
  python_lint_1.pythonLintCheck,
97
99
  python_typecheck_1.pythonTypecheckCheck,
98
100
  dead_code_1.deadCodeCheck,
package/dist/index.d.ts CHANGED
@@ -8,3 +8,5 @@ export { pass, warn, fail, skip, defineCheck } from './core';
8
8
  export type { Check, CheckContext, CheckOutcome, CheckReport, CheckResult, CheckStatus, Language, Phase, ReporterFn, ReporterKind, RunOptions, } from './core';
9
9
  export { getSourcePath, getSourcePaths, getIgnoredDirs, getPackageConfigPath, hasLocalConfig, } from './config';
10
10
  export type { PRCheckMateConfig } from './interfaces';
11
+ export { Logger } from './utils/logger';
12
+ export type { LogLevel } from './utils/logger';
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@
5
5
  * @module pr-checkmate
6
6
  */
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.hasLocalConfig = exports.getPackageConfigPath = exports.getIgnoredDirs = exports.getSourcePaths = exports.getSourcePath = exports.defineCheck = exports.skip = exports.fail = exports.warn = exports.pass = exports.REGISTRY = exports.buildContext = exports.runChecks = void 0;
8
+ exports.Logger = exports.hasLocalConfig = exports.getPackageConfigPath = exports.getIgnoredDirs = exports.getSourcePaths = exports.getSourcePath = exports.defineCheck = exports.skip = exports.fail = exports.warn = exports.pass = exports.REGISTRY = exports.buildContext = exports.runChecks = void 0;
9
9
  // Primary programmatic API: run checks and receive a structured report.
10
10
  // Never throws for violations, never calls process.exit — the caller decides.
11
11
  var core_1 = require("./core");
@@ -26,3 +26,7 @@ Object.defineProperty(exports, "getSourcePaths", { enumerable: true, get: functi
26
26
  Object.defineProperty(exports, "getIgnoredDirs", { enumerable: true, get: function () { return config_1.getIgnoredDirs; } });
27
27
  Object.defineProperty(exports, "getPackageConfigPath", { enumerable: true, get: function () { return config_1.getPackageConfigPath; } });
28
28
  Object.defineProperty(exports, "hasLocalConfig", { enumerable: true, get: function () { return config_1.hasLocalConfig; } });
29
+ // Standalone logging utility. Consumers instantiate their own Logger so their
30
+ // log level and buffer stay independent from pr-checkmate's internal logger.
31
+ var logger_1 = require("./utils/logger");
32
+ Object.defineProperty(exports, "Logger", { enumerable: true, get: function () { return logger_1.Logger; } });
package/dist/init.js CHANGED
@@ -68,6 +68,14 @@ const DEFAULT_CONFIG = {
68
68
  rules: {},
69
69
  ignores: [],
70
70
  },
71
+ // YAML lint (bundled `yaml` parser). Reports syntax errors, duplicate keys,
72
+ // and tab/indentation problems in .yml/.yaml files. Blocking by default
73
+ // (severity "error"); set severity "warn" to make it advisory, or enabled
74
+ // false to disable. `ignore` takes glob patterns to skip (e.g. "charts/**").
75
+ yamlLint: {
76
+ enabled: true,
77
+ ignore: [],
78
+ },
71
79
  // TypeScript type check
72
80
  typecheck: {
73
81
  enabled: true,
@@ -48,12 +48,13 @@ export interface PRCheckMateConfig {
48
48
  };
49
49
  security?: {
50
50
  gitleaks?: {
51
- enabled: boolean;
51
+ enabled?: boolean;
52
52
  version?: string;
53
53
  };
54
54
  'npm-audit'?: {
55
- enabled: boolean;
56
- auditLevel: string;
55
+ enabled?: boolean;
56
+ auditLevel?: string;
57
+ ignore?: string[];
57
58
  };
58
59
  };
59
60
  typecheck?: {
@@ -196,6 +197,11 @@ export interface PRCheckMateConfig {
196
197
  configValidation?: {
197
198
  enabled?: boolean;
198
199
  };
200
+ yamlLint?: {
201
+ enabled?: boolean;
202
+ severity?: 'error' | 'warn';
203
+ ignore?: string[];
204
+ };
199
205
  vulnScan?: {
200
206
  enabled?: boolean;
201
207
  };
@@ -1,5 +1,5 @@
1
- type LogLevel = 'log' | 'info' | 'warn' | 'error' | 'debug';
2
- declare class Logger {
1
+ export type LogLevel = 'log' | 'info' | 'warn' | 'error' | 'debug';
2
+ export declare class Logger {
3
3
  private logs;
4
4
  private levelOrder;
5
5
  private currentLevel;
@@ -17,4 +17,3 @@ declare class Logger {
17
17
  clear(): void;
18
18
  }
19
19
  export declare const logger: Logger;
20
- export {};
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.logger = void 0;
3
+ exports.logger = exports.Logger = void 0;
4
4
  class Logger {
5
5
  constructor() {
6
6
  this.logs = [];
@@ -65,4 +65,5 @@ class Logger {
65
65
  this.logs = [];
66
66
  }
67
67
  }
68
+ exports.Logger = Logger;
68
69
  exports.logger = new Logger();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pr-checkmate",
3
- "version": "1.23.4",
3
+ "version": "1.23.6",
4
4
  "description": "Automated PR quality checks: linting, formatting, dependency analysis, and spellcheck",
5
5
  "keywords": [
6
6
  "github-actions",
@@ -95,11 +95,12 @@
95
95
  "inquirer": "^14.0.2",
96
96
  "jscpd": "^5.0.11",
97
97
  "license-checker": "^25.0.1",
98
- "markdownlint-cli2": "^0.23.0",
98
+ "markdownlint-cli2": "^0.23.1",
99
99
  "prettier": ">=3.0.0",
100
100
  "ts-node": "^10.9.1",
101
101
  "ts-unused-exports": "^11.0.1",
102
- "typescript": "^6.0.3"
102
+ "typescript": "^6.0.3",
103
+ "yaml": "^2.9.0"
103
104
  },
104
105
  "devDependencies": {
105
106
  "standard-version": "^9.5.0",
@@ -100,6 +100,33 @@
100
100
  "ignore": { "type": "array", "items": { "type": "string" } }
101
101
  }
102
102
  },
103
+ "security": {
104
+ "type": "object",
105
+ "properties": {
106
+ "gitleaks": {
107
+ "type": "object",
108
+ "properties": {
109
+ "enabled": { "type": "boolean" },
110
+ "version": { "type": "string" }
111
+ }
112
+ },
113
+ "npm-audit": {
114
+ "type": "object",
115
+ "properties": {
116
+ "enabled": { "type": "boolean" },
117
+ "auditLevel": {
118
+ "type": "string",
119
+ "enum": ["info", "low", "moderate", "high", "critical"]
120
+ },
121
+ "ignore": {
122
+ "type": "array",
123
+ "items": { "type": "string" },
124
+ "description": "Package names whose vulnerabilities never block, even when direct and at/above auditLevel. Still reported as warnings."
125
+ }
126
+ }
127
+ }
128
+ }
129
+ },
103
130
  "mergeConflict": { "$ref": "#/$defs/toggleSeverity" },
104
131
  "leftoverDebug": { "$ref": "#/$defs/toggleSeverity" },
105
132
  "lockfileDrift": { "$ref": "#/$defs/toggleSeverity" },
@@ -121,6 +148,14 @@
121
148
  "caseCollision": { "$ref": "#/$defs/toggle" },
122
149
  "sensitiveFileGuard": { "$ref": "#/$defs/toggle" },
123
150
  "configValidation": { "$ref": "#/$defs/toggle" },
151
+ "yamlLint": {
152
+ "type": "object",
153
+ "properties": {
154
+ "enabled": { "type": "boolean" },
155
+ "severity": { "enum": ["error", "warn"] },
156
+ "ignore": { "type": "array", "items": { "type": "string" } }
157
+ }
158
+ },
124
159
  "typecheck": { "$ref": "#/$defs/toggle" },
125
160
  "prBody": {
126
161
  "type": "object",
@@ -100,6 +100,33 @@
100
100
  "ignore": { "type": "array", "items": { "type": "string" } }
101
101
  }
102
102
  },
103
+ "security": {
104
+ "type": "object",
105
+ "properties": {
106
+ "gitleaks": {
107
+ "type": "object",
108
+ "properties": {
109
+ "enabled": { "type": "boolean" },
110
+ "version": { "type": "string" }
111
+ }
112
+ },
113
+ "npm-audit": {
114
+ "type": "object",
115
+ "properties": {
116
+ "enabled": { "type": "boolean" },
117
+ "auditLevel": {
118
+ "type": "string",
119
+ "enum": ["info", "low", "moderate", "high", "critical"]
120
+ },
121
+ "ignore": {
122
+ "type": "array",
123
+ "items": { "type": "string" },
124
+ "description": "Package names whose vulnerabilities never block, even when direct and at/above auditLevel. Still reported as warnings."
125
+ }
126
+ }
127
+ }
128
+ }
129
+ },
103
130
  "mergeConflict": { "$ref": "#/$defs/toggleSeverity" },
104
131
  "leftoverDebug": { "$ref": "#/$defs/toggleSeverity" },
105
132
  "lockfileDrift": { "$ref": "#/$defs/toggleSeverity" },
@@ -121,6 +148,14 @@
121
148
  "caseCollision": { "$ref": "#/$defs/toggle" },
122
149
  "sensitiveFileGuard": { "$ref": "#/$defs/toggle" },
123
150
  "configValidation": { "$ref": "#/$defs/toggle" },
151
+ "yamlLint": {
152
+ "type": "object",
153
+ "properties": {
154
+ "enabled": { "type": "boolean" },
155
+ "severity": { "enum": ["error", "warn"] },
156
+ "ignore": { "type": "array", "items": { "type": "string" } }
157
+ }
158
+ },
124
159
  "typecheck": { "$ref": "#/$defs/toggle" },
125
160
  "prBody": {
126
161
  "type": "object",