pr-checkmate 2.1.0 → 2.1.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.
- package/README.md +4 -3
- package/dist/config/constants.d.ts +7 -0
- package/dist/config/constants.js +12 -0
- package/dist/core/checks/dependencies/dependency-check.js +1 -1
- package/dist/core/checks/dependencies/license-check.js +1 -6
- package/dist/core/checks/dependencies/npm-audit.js +1 -1
- package/dist/core/checks/dependencies/outdated-deps.js +1 -6
- package/dist/core/checks/quality/duplicate-check.js +33 -2
- package/dist/core/install/doctor.d.ts +2 -2
- package/dist/core/install/doctor.js +8 -16
- package/dist/core/reporters.js +8 -2
- package/dist/core/types.d.ts +18 -0
- package/dist/core/types.js +28 -0
- package/dist/utils/detect-language.js +11 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/package-version.d.ts +4 -0
- package/dist/utils/package-version.js +41 -0
- package/dist/utils/update-notifier.js +5 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -143,11 +143,12 @@ machine — ShellCheck among them — are inside, so they stop silently skipping
|
|
|
143
143
|
version tag (`:2.0.0`) for a reproducible pipeline; `--user` keeps files that formatters
|
|
144
144
|
rewrite owned by you rather than by root.
|
|
145
145
|
|
|
146
|
-
Verify what you pulled — the image carries
|
|
147
|
-
|
|
146
|
+
Verify what you pulled — the image carries SLSA provenance and an SBOM, attached to
|
|
147
|
+
the image itself in the registry:
|
|
148
148
|
|
|
149
149
|
```bash
|
|
150
|
-
|
|
150
|
+
docker buildx imagetools inspect ghcr.io/mybono/pr-checkmate:2 \
|
|
151
|
+
--format '{{ json .Provenance }}'
|
|
151
152
|
```
|
|
152
153
|
|
|
153
154
|
[All tags →](https://github.com/users/Mybono/packages/container/package/pr-checkmate)
|
|
@@ -70,6 +70,13 @@ export declare const CONFIG_FILE_PATTERNS: string[];
|
|
|
70
70
|
* config/CI YAML and JSON out of the report — they are data, not code, and their
|
|
71
71
|
* repetition is not meaningful duplication. This mirrors the code-only file set the
|
|
72
72
|
* check already uses when scanning a git diff, so full-tree and diff scans agree.
|
|
73
|
+
*
|
|
74
|
+
* One entry per language pr-checkmate detects, all of them verified against the
|
|
75
|
+
* jscpd binary rather than assumed: the list used to stop at C++ while the check
|
|
76
|
+
* only ever handed jscpd `.ts`/`.js` files, so the Go, Rust, C#, Ruby and PHP
|
|
77
|
+
* halves were unreachable and the Python one was decoration. Copy-paste is a
|
|
78
|
+
* language-independent smell, and the tokenizer behind this check has always been
|
|
79
|
+
* able to see it — the file list was the only thing in the way.
|
|
73
80
|
*/
|
|
74
81
|
export declare const DUPLICATE_CODE_FORMATS: string[];
|
|
75
82
|
/**
|
package/dist/config/constants.js
CHANGED
|
@@ -192,6 +192,13 @@ exports.CONFIG_FILE_PATTERNS = [
|
|
|
192
192
|
* config/CI YAML and JSON out of the report — they are data, not code, and their
|
|
193
193
|
* repetition is not meaningful duplication. This mirrors the code-only file set the
|
|
194
194
|
* check already uses when scanning a git diff, so full-tree and diff scans agree.
|
|
195
|
+
*
|
|
196
|
+
* One entry per language pr-checkmate detects, all of them verified against the
|
|
197
|
+
* jscpd binary rather than assumed: the list used to stop at C++ while the check
|
|
198
|
+
* only ever handed jscpd `.ts`/`.js` files, so the Go, Rust, C#, Ruby and PHP
|
|
199
|
+
* halves were unreachable and the Python one was decoration. Copy-paste is a
|
|
200
|
+
* language-independent smell, and the tokenizer behind this check has always been
|
|
201
|
+
* able to see it — the file list was the only thing in the way.
|
|
195
202
|
*/
|
|
196
203
|
exports.DUPLICATE_CODE_FORMATS = [
|
|
197
204
|
'javascript',
|
|
@@ -203,6 +210,11 @@ exports.DUPLICATE_CODE_FORMATS = [
|
|
|
203
210
|
'swift',
|
|
204
211
|
'c',
|
|
205
212
|
'cpp',
|
|
213
|
+
'csharp',
|
|
214
|
+
'go',
|
|
215
|
+
'php',
|
|
216
|
+
'ruby',
|
|
217
|
+
'rust',
|
|
206
218
|
];
|
|
207
219
|
/**
|
|
208
220
|
* File extensions to check
|
|
@@ -106,7 +106,7 @@ exports.dependencyCheck = {
|
|
|
106
106
|
applies(ctx) {
|
|
107
107
|
if (ctx.config.dependency?.enabled === false)
|
|
108
108
|
return false;
|
|
109
|
-
return
|
|
109
|
+
return (0, types_1.isNpmProject)(ctx);
|
|
110
110
|
},
|
|
111
111
|
async run(ctx) {
|
|
112
112
|
// Resolve the bundled knip binary and run it via the current node — never
|
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.licenseCheck = void 0;
|
|
7
|
-
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
9
4
|
const utils_1 = require("../../../utils");
|
|
10
5
|
const execa_1 = require("../../../utils/execa");
|
|
11
6
|
const types_1 = require("../../types");
|
|
@@ -19,7 +14,7 @@ exports.licenseCheck = {
|
|
|
19
14
|
name: 'License Check',
|
|
20
15
|
phase: 'blocking',
|
|
21
16
|
applies(ctx) {
|
|
22
|
-
return
|
|
17
|
+
return (0, types_1.isNpmProject)(ctx);
|
|
23
18
|
},
|
|
24
19
|
async run(ctx) {
|
|
25
20
|
const allowPackages = new Set(ctx.config.licenseCheck?.allowPackages ?? []);
|
|
@@ -34,7 +34,7 @@ exports.npmAuditCheck = {
|
|
|
34
34
|
applies(ctx) {
|
|
35
35
|
if (ctx.config.security?.['npm-audit']?.enabled === false)
|
|
36
36
|
return false;
|
|
37
|
-
return
|
|
37
|
+
return (0, types_1.isNpmProject)(ctx);
|
|
38
38
|
},
|
|
39
39
|
async run(ctx) {
|
|
40
40
|
// `high`, not `moderate`. A moderate CVE in a direct dependency often has no
|
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.outdatedDepsCheck = void 0;
|
|
7
|
-
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
9
4
|
const utils_1 = require("../../../utils");
|
|
10
5
|
const execa_1 = require("../../../utils/execa");
|
|
11
6
|
const types_1 = require("../../types");
|
|
@@ -16,7 +11,7 @@ exports.outdatedDepsCheck = {
|
|
|
16
11
|
applies(ctx) {
|
|
17
12
|
if (ctx.config.outdatedDeps?.enabled === false)
|
|
18
13
|
return false;
|
|
19
|
-
return
|
|
14
|
+
return (0, types_1.isNpmProject)(ctx);
|
|
20
15
|
},
|
|
21
16
|
async run(ctx) {
|
|
22
17
|
// `npm outdated` queries the registry for every dependency and can take a
|
|
@@ -7,6 +7,37 @@ const utils_1 = require("../../../utils");
|
|
|
7
7
|
const execa_1 = require("../../../utils/execa");
|
|
8
8
|
const targets_1 = require("../../targets");
|
|
9
9
|
const types_1 = require("../../types");
|
|
10
|
+
/**
|
|
11
|
+
* The files to hand jscpd, per language.
|
|
12
|
+
*
|
|
13
|
+
* This map is why the check reads `ctx.languages` at all. It used to pass a fixed
|
|
14
|
+
* `*.ts,*.tsx,*.js,*.jsx` and gate itself on TypeScript/JavaScript, so a Python
|
|
15
|
+
* repository got `⏭️ Duplicate Code: no relevant files` — not because it had no
|
|
16
|
+
* duplication, but because nothing ever looked. The formats list in `constants.ts`
|
|
17
|
+
* named Python and C++ all along, which made the report's own claim untrue.
|
|
18
|
+
*
|
|
19
|
+
* Extensions mirror `detect-language.ts` so a language that decides the check runs
|
|
20
|
+
* is a language whose files it actually scans, with one deliberate addition: `.c`
|
|
21
|
+
* and `.h` under `cpp`, since a C project detects as `cpp` and the `c` format is
|
|
22
|
+
* one jscpd supports.
|
|
23
|
+
*/
|
|
24
|
+
const LANGUAGE_GLOBS = {
|
|
25
|
+
typescript: ['*.ts', '*.tsx'],
|
|
26
|
+
javascript: ['*.js', '*.jsx', '*.mjs', '*.cjs'],
|
|
27
|
+
python: ['*.py'],
|
|
28
|
+
cpp: ['*.cpp', '*.cc', '*.cxx', '*.c++', '*.hpp', '*.hh', '*.hxx', '*.c', '*.h'],
|
|
29
|
+
swift: ['*.swift'],
|
|
30
|
+
kotlin: ['*.kt', '*.kts', '*.java'],
|
|
31
|
+
go: ['*.go'],
|
|
32
|
+
rust: ['*.rs'],
|
|
33
|
+
csharp: ['*.cs'],
|
|
34
|
+
ruby: ['*.rb'],
|
|
35
|
+
php: ['*.php'],
|
|
36
|
+
};
|
|
37
|
+
/** The scan globs for one run: every detected language jscpd can tokenise. */
|
|
38
|
+
function globsFor(languages) {
|
|
39
|
+
return [...new Set(languages.flatMap(lang => LANGUAGE_GLOBS[lang] ?? []))];
|
|
40
|
+
}
|
|
10
41
|
// Always excluded regardless of client config — scanning these never yields
|
|
11
42
|
// meaningful production duplication. Config files (eslint/*.config.*) are data,
|
|
12
43
|
// not logic, and are commonly shipped in two formats (e.g. .eslintrc + flat
|
|
@@ -36,12 +67,12 @@ exports.duplicateCheck = {
|
|
|
36
67
|
applies(ctx) {
|
|
37
68
|
if (ctx.config.duplicate?.enabled === false)
|
|
38
69
|
return false;
|
|
39
|
-
return ctx.languages.
|
|
70
|
+
return globsFor(ctx.languages).length > 0;
|
|
40
71
|
},
|
|
41
72
|
async run(ctx) {
|
|
42
73
|
const minLines = ctx.config.duplicate?.minLines ?? 5;
|
|
43
74
|
const minTokens = ctx.config.duplicate?.minTokens ?? 50;
|
|
44
|
-
const targetPaths = await (0, targets_1.resolveScopedTargetFiles)(ctx,
|
|
75
|
+
const targetPaths = await (0, targets_1.resolveScopedTargetFiles)(ctx, globsFor(ctx.languages), (0, config_1.getSourcePaths)());
|
|
45
76
|
if (targetPaths === null)
|
|
46
77
|
return (0, types_1.skip)('git unavailable');
|
|
47
78
|
if (targetPaths.length === 0)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { type LanguageUsage } from '../../utils/detect-language';
|
|
2
|
+
import { packageVersion } from '../../utils/package-version';
|
|
2
3
|
import { type EnsureResult } from './ensure';
|
|
3
4
|
import { type ProfileDecision } from './profiles';
|
|
4
|
-
|
|
5
|
-
export declare function packageVersion(): string;
|
|
5
|
+
export { packageVersion };
|
|
6
6
|
/**
|
|
7
7
|
* Everything the install layer knows about this repository, in one shot.
|
|
8
8
|
*
|
|
@@ -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.packageVersion =
|
|
6
|
+
exports.packageVersion = void 0;
|
|
7
7
|
exports.planInstall = planInstall;
|
|
8
8
|
exports.installProfiles = installProfiles;
|
|
9
9
|
exports.runDoctor = runDoctor;
|
|
@@ -13,19 +13,11 @@ const node_path_1 = __importDefault(require("node:path"));
|
|
|
13
13
|
const loader_1 = require("../../config/loader");
|
|
14
14
|
const detect_language_1 = require("../../utils/detect-language");
|
|
15
15
|
const logger_1 = require("../../utils/logger");
|
|
16
|
+
const package_version_1 = require("../../utils/package-version");
|
|
17
|
+
Object.defineProperty(exports, "packageVersion", { enumerable: true, get: function () { return package_version_1.packageVersion; } });
|
|
16
18
|
const cache_1 = require("./cache");
|
|
17
19
|
const ensure_1 = require("./ensure");
|
|
18
20
|
const profiles_1 = require("./profiles");
|
|
19
|
-
/** Our own version, read from the package manifest that ships with us. */
|
|
20
|
-
function packageVersion() {
|
|
21
|
-
try {
|
|
22
|
-
const manifest = node_path_1.default.resolve(__dirname, '../../..', 'package.json');
|
|
23
|
-
return JSON.parse(node_fs_1.default.readFileSync(manifest, 'utf-8')).version ?? '0.0.0';
|
|
24
|
-
}
|
|
25
|
-
catch {
|
|
26
|
-
return '0.0.0';
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
21
|
/**
|
|
30
22
|
* Everything the install layer knows about this repository, in one shot.
|
|
31
23
|
*
|
|
@@ -36,7 +28,7 @@ function packageVersion() {
|
|
|
36
28
|
async function planInstall(cwd = process.cwd(), usage) {
|
|
37
29
|
const config = (0, loader_1.loadProjectConfig)();
|
|
38
30
|
const detected = usage ?? (await (0, detect_language_1.detectLanguageUsage)(cwd, config.ignoreDirs, config.detect));
|
|
39
|
-
const location = (0, cache_1.resolveCacheDir)(packageVersion(), config.install?.cacheDir);
|
|
31
|
+
const location = (0, cache_1.resolveCacheDir)((0, package_version_1.packageVersion)(), config.install?.cacheDir);
|
|
40
32
|
return { decisions: (0, profiles_1.planProfiles)(detected, config), cacheDir: location?.dir ?? null };
|
|
41
33
|
}
|
|
42
34
|
/** Whether a profile's tooling is present in the cache right now. */
|
|
@@ -71,7 +63,7 @@ async function installProfiles(cwd = process.cwd(), all = false) {
|
|
|
71
63
|
// packages fight over npm's cache and gain nothing; the wall-clock cost lands
|
|
72
64
|
// once, on a cold cache, and clarity of the log matters more there.
|
|
73
65
|
for (const profile of wanted) {
|
|
74
|
-
results.push(await (0, ensure_1.ensureProfile)(profile, { cwd, version: packageVersion(), install }));
|
|
66
|
+
results.push(await (0, ensure_1.ensureProfile)(profile, { cwd, version: (0, package_version_1.packageVersion)(), install }));
|
|
75
67
|
}
|
|
76
68
|
return results;
|
|
77
69
|
}
|
|
@@ -90,7 +82,7 @@ async function runDoctor(cwd = process.cwd()) {
|
|
|
90
82
|
const lines = [];
|
|
91
83
|
lines.push('PR CheckMate — install doctor');
|
|
92
84
|
lines.push('');
|
|
93
|
-
lines.push(` version ${packageVersion()}`);
|
|
85
|
+
lines.push(` version ${(0, package_version_1.packageVersion)()}`);
|
|
94
86
|
lines.push(` repository ${cwd}`);
|
|
95
87
|
lines.push(` cache ${cacheDir ?? 'unavailable (no writable directory)'}`);
|
|
96
88
|
lines.push(` strategy ${config.install?.strategy ?? 'adaptive'}`);
|
|
@@ -116,7 +108,7 @@ async function runDoctor(cwd = process.cwd()) {
|
|
|
116
108
|
lines.push(` ${missing.length} profile(s) not installed yet. They are fetched on the next`);
|
|
117
109
|
lines.push(' run, or now with: npx pr-checkmate install');
|
|
118
110
|
}
|
|
119
|
-
const stale = (0, cache_1.stalePaths)(packageVersion(), config.install?.cacheDir);
|
|
111
|
+
const stale = (0, cache_1.stalePaths)((0, package_version_1.packageVersion)(), config.install?.cacheDir);
|
|
120
112
|
if (stale.length > 0) {
|
|
121
113
|
lines.push('');
|
|
122
114
|
lines.push(` ${stale.length} directory(ies) from other versions: npx pr-checkmate cache-prune`);
|
|
@@ -126,7 +118,7 @@ async function runDoctor(cwd = process.cwd()) {
|
|
|
126
118
|
/** Remove cached tooling belonging to other versions of pr-checkmate. */
|
|
127
119
|
function pruneCache() {
|
|
128
120
|
const config = (0, loader_1.loadProjectConfig)();
|
|
129
|
-
const stale = (0, cache_1.stalePaths)(packageVersion(), config.install?.cacheDir);
|
|
121
|
+
const stale = (0, cache_1.stalePaths)((0, package_version_1.packageVersion)(), config.install?.cacheDir);
|
|
130
122
|
if (stale.length === 0) {
|
|
131
123
|
logger_1.logger.print('Nothing to prune — no tooling from other versions is cached.');
|
|
132
124
|
return;
|
package/dist/core/reporters.js
CHANGED
|
@@ -8,6 +8,7 @@ exports.report = report;
|
|
|
8
8
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
9
9
|
const utils_1 = require("../utils");
|
|
10
10
|
const http_1 = require("../utils/http");
|
|
11
|
+
const package_version_1 = require("../utils/package-version");
|
|
11
12
|
const STATUS_ICON = {
|
|
12
13
|
pass: '✅',
|
|
13
14
|
warn: '⚠️',
|
|
@@ -74,7 +75,7 @@ function buildMarkdownReport(report, cfg = {}) {
|
|
|
74
75
|
// Say so rather than letting the table quietly disagree with the counts above.
|
|
75
76
|
...(hidden > 0 ? ['', `_${hidden} row(s) hidden by \`reporter.include\`._`] : []),
|
|
76
77
|
'',
|
|
77
|
-
|
|
78
|
+
`_Generated by [PR CheckMate](https://www.npmjs.com/package/pr-checkmate) v${(0, package_version_1.packageVersion)()}_`,
|
|
78
79
|
].join('\n');
|
|
79
80
|
}
|
|
80
81
|
/**
|
|
@@ -89,7 +90,12 @@ function printStdout(report, cfg) {
|
|
|
89
90
|
const { passed, warned, failed, skipped } = report.summary;
|
|
90
91
|
utils_1.logger.print('');
|
|
91
92
|
utils_1.logger.print(SUMMARY_BORDER);
|
|
92
|
-
|
|
93
|
+
// The version belongs in the headline, not in a footnote. Which copy of
|
|
94
|
+
// pr-checkmate produced a report is the first thing to establish when the
|
|
95
|
+
// report disagrees with what someone expects, and it is not otherwise
|
|
96
|
+
// recoverable from the log: a stale global install answers `pr-checkmate` on
|
|
97
|
+
// `PATH` ahead of the project's own, and reads the same config while doing it.
|
|
98
|
+
utils_1.logger.print(` PR CheckMate Summary · v${(0, package_version_1.packageVersion)()}`);
|
|
93
99
|
utils_1.logger.print(SUMMARY_BORDER);
|
|
94
100
|
for (const r of report.results) {
|
|
95
101
|
const details = r.details ? `: ${r.details}` : '';
|
package/dist/core/types.d.ts
CHANGED
|
@@ -164,6 +164,24 @@ export declare const pass: (details?: string) => CheckOutcome;
|
|
|
164
164
|
export declare const warn: (details: string) => CheckOutcome;
|
|
165
165
|
export declare const fail: (details: string) => CheckOutcome;
|
|
166
166
|
export declare const skip: (details: string) => CheckOutcome;
|
|
167
|
+
/**
|
|
168
|
+
* Whether this repository is an npm project — the gate for every check that reads
|
|
169
|
+
* the npm dependency tree (NPM Audit, Dependencies, License Check, Outdated Deps).
|
|
170
|
+
*
|
|
171
|
+
* A `package.json` on its own is not the answer, and this is the one predicate
|
|
172
|
+
* where that matters most: installing pr-checkmate as a devDependency *creates*
|
|
173
|
+
* that file. Gate on it alone and a Python service gets four rows auditing the
|
|
174
|
+
* dependency tree of the tool auditing it — `npm audit` over our own transitive
|
|
175
|
+
* packages, licences of our own `node_modules`, "outdated" versions of tooling
|
|
176
|
+
* the team never chose. All four are unactionable by construction: nothing in the
|
|
177
|
+
* repository's own code can change any of them.
|
|
178
|
+
*
|
|
179
|
+
* So both halves must hold — a manifest to read, and JavaScript or TypeScript
|
|
180
|
+
* actually written here. A polyglot repository (a Django backend with a JS
|
|
181
|
+
* frontend) satisfies both and keeps every check; a Python repository whose only
|
|
182
|
+
* `package.json` is the one that installed us satisfies neither.
|
|
183
|
+
*/
|
|
184
|
+
export declare function isNpmProject(ctx: CheckContext): boolean;
|
|
167
185
|
/**
|
|
168
186
|
* Identity helper for authoring a `Check` with full type inference and checking.
|
|
169
187
|
* Purely ergonomic — `defineCheck({ ... })` is equivalent to `{ ... } satisfies Check`.
|
package/dist/core/types.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.defineCheck = exports.skip = exports.fail = exports.warn = exports.pass = exports.PHASES = void 0;
|
|
7
|
+
exports.isNpmProject = isNpmProject;
|
|
8
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
9
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
4
10
|
exports.PHASES = ['blocking', 'informational', 'format'];
|
|
5
11
|
/** Outcome constructors — keep check bodies terse. */
|
|
6
12
|
const pass = (details) => ({
|
|
@@ -14,6 +20,28 @@ const fail = (details) => ({ status: 'fail', details });
|
|
|
14
20
|
exports.fail = fail;
|
|
15
21
|
const skip = (details) => ({ status: 'skip', details });
|
|
16
22
|
exports.skip = skip;
|
|
23
|
+
/**
|
|
24
|
+
* Whether this repository is an npm project — the gate for every check that reads
|
|
25
|
+
* the npm dependency tree (NPM Audit, Dependencies, License Check, Outdated Deps).
|
|
26
|
+
*
|
|
27
|
+
* A `package.json` on its own is not the answer, and this is the one predicate
|
|
28
|
+
* where that matters most: installing pr-checkmate as a devDependency *creates*
|
|
29
|
+
* that file. Gate on it alone and a Python service gets four rows auditing the
|
|
30
|
+
* dependency tree of the tool auditing it — `npm audit` over our own transitive
|
|
31
|
+
* packages, licences of our own `node_modules`, "outdated" versions of tooling
|
|
32
|
+
* the team never chose. All four are unactionable by construction: nothing in the
|
|
33
|
+
* repository's own code can change any of them.
|
|
34
|
+
*
|
|
35
|
+
* So both halves must hold — a manifest to read, and JavaScript or TypeScript
|
|
36
|
+
* actually written here. A polyglot repository (a Django backend with a JS
|
|
37
|
+
* frontend) satisfies both and keeps every check; a Python repository whose only
|
|
38
|
+
* `package.json` is the one that installed us satisfies neither.
|
|
39
|
+
*/
|
|
40
|
+
function isNpmProject(ctx) {
|
|
41
|
+
if (!ctx.languages.includes('javascript') && !ctx.languages.includes('typescript'))
|
|
42
|
+
return false;
|
|
43
|
+
return node_fs_1.default.existsSync(node_path_1.default.join(ctx.cwd, 'package.json'));
|
|
44
|
+
}
|
|
17
45
|
/**
|
|
18
46
|
* Identity helper for authoring a `Check` with full type inference and checking.
|
|
19
47
|
* Purely ergonomic — `defineCheck({ ... })` is equivalent to `{ ... } satisfies Check`.
|
|
@@ -18,7 +18,17 @@ const logger_1 = require("./logger");
|
|
|
18
18
|
*/
|
|
19
19
|
const MARKER_FILES = {
|
|
20
20
|
typescript: ['tsconfig.json'],
|
|
21
|
-
|
|
21
|
+
// Deliberately empty. `package.json` used to sit here, and it is the one marker
|
|
22
|
+
// this package creates itself: installing pr-checkmate through npm puts a
|
|
23
|
+
// `package.json` in a repository that may not hold a single line of JavaScript.
|
|
24
|
+
// A Python service then detected as JavaScript and got the whole JS half of the
|
|
25
|
+
// report — ESLint, Prettier, NPM Audit, License Check — every row of it about
|
|
26
|
+
// the file that installed us. `install/profiles.ts` already refused to buy
|
|
27
|
+
// 152 MB of toolchain off this marker (ticket 064); the checks now refuse to
|
|
28
|
+
// run off it for the same reason. Real JavaScript is detected by its `.js`,
|
|
29
|
+
// `.jsx`, `.mjs` and `.cjs` files, and a repository that has none loses
|
|
30
|
+
// nothing: every JS check it lost would have reported "no files" anyway.
|
|
31
|
+
javascript: [],
|
|
22
32
|
python: ['pyproject.toml', 'setup.py', 'setup.cfg', 'requirements.txt', 'Pipfile'],
|
|
23
33
|
cpp: ['CMakeLists.txt', 'compile_commands.json', 'conanfile.txt', 'conanfile.py'],
|
|
24
34
|
swift: ['Package.swift', 'Package.resolved'],
|
package/dist/utils/index.d.ts
CHANGED
package/dist/utils/index.js
CHANGED
|
@@ -28,6 +28,7 @@ __exportStar(require("./direct-deps"), exports);
|
|
|
28
28
|
__exportStar(require("./detect-language"), exports);
|
|
29
29
|
__exportStar(require("./glob"), exports);
|
|
30
30
|
__exportStar(require("./update-notifier"), exports);
|
|
31
|
+
__exportStar(require("./package-version"), exports);
|
|
31
32
|
__exportStar(require("./ci"), exports);
|
|
32
33
|
__exportStar(require("./git"), exports);
|
|
33
34
|
/**
|
|
@@ -0,0 +1,41 @@
|
|
|
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.packageVersion = packageVersion;
|
|
7
|
+
exports.packageName = packageName;
|
|
8
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
9
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
+
/**
|
|
11
|
+
* Our own name and version, read from the manifest that ships inside the package.
|
|
12
|
+
*
|
|
13
|
+
* Read from disk rather than baked in at build time so it is the *running* copy
|
|
14
|
+
* that answers. That distinction is the whole point: a stale global install
|
|
15
|
+
* (`npm i -g pr-checkmate`) shadows the project's own copy on `PATH`, and a repo
|
|
16
|
+
* pinned to the latest version can still be checked by a year-old binary without
|
|
17
|
+
* anything in the log saying so. One client spent a while reading an upgrade
|
|
18
|
+
* notice for a version they had already installed, because nothing printed which
|
|
19
|
+
* version was speaking.
|
|
20
|
+
*
|
|
21
|
+
* Falls back to placeholders rather than throwing: no caller here is important
|
|
22
|
+
* enough to fail a run over a manifest it could not read.
|
|
23
|
+
*/
|
|
24
|
+
function readManifest() {
|
|
25
|
+
try {
|
|
26
|
+
const manifest = node_path_1.default.resolve(__dirname, '../..', 'package.json');
|
|
27
|
+
const parsed = JSON.parse(node_fs_1.default.readFileSync(manifest, 'utf-8'));
|
|
28
|
+
return { name: parsed.name ?? 'pr-checkmate', version: parsed.version ?? '0.0.0' };
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return { name: 'pr-checkmate', version: '0.0.0' };
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/** The version of pr-checkmate that is executing right now. */
|
|
35
|
+
function packageVersion() {
|
|
36
|
+
return readManifest().version;
|
|
37
|
+
}
|
|
38
|
+
/** The package name that is executing right now — the one to query npm for. */
|
|
39
|
+
function packageName() {
|
|
40
|
+
return readManifest().name;
|
|
41
|
+
}
|
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.isMajorUpgrade = isMajorUpgrade;
|
|
7
4
|
exports.getUpdateNotice = getUpdateNotice;
|
|
8
|
-
const node_fs_1 = __importDefault(require("node:fs"));
|
|
9
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
10
5
|
// Imported from the module rather than the `../config` barrel on purpose:
|
|
11
6
|
// `config/index.ts` imports `../utils`, so going through the barrel would make
|
|
12
7
|
// utils ⇄ config circular. `constants.ts` itself pulls in nothing from utils.
|
|
13
8
|
const constants_1 = require("../config/constants");
|
|
9
|
+
const package_version_1 = require("./package-version");
|
|
14
10
|
// Same widths as the run summary in `reporters.ts`, so the two boxes line up
|
|
15
11
|
// when both are printed at the end of a run.
|
|
16
12
|
const NOTICE_BORDER = '═'.repeat(63);
|
|
@@ -72,8 +68,10 @@ async function getUpdateNotice(timeoutMs = 2500) {
|
|
|
72
68
|
if (process.env.PR_CHECKMATE_NO_UPDATE_CHECK)
|
|
73
69
|
return null;
|
|
74
70
|
try {
|
|
75
|
-
|
|
76
|
-
|
|
71
|
+
// The *running* copy's identity, not the project's declared dependency. A
|
|
72
|
+
// stale global install shadows the local one on `PATH`, and it is the stale
|
|
73
|
+
// copy that must own the notice it prints.
|
|
74
|
+
const pkg = { name: (0, package_version_1.packageName)(), version: (0, package_version_1.packageVersion)() };
|
|
77
75
|
const controller = new AbortController();
|
|
78
76
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
79
77
|
let latest;
|
package/package.json
CHANGED