pr-checkmate 3.2.2 → 3.3.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.
- package/.eslintrc.js +3 -9
- package/README.md +1 -1
- package/dist/cjs/cli.js +13 -0
- package/dist/cjs/config/pr-checkmate.template.json +393 -0
- package/dist/cjs/core/checks/dependencies/grype-scan.js +18 -0
- package/dist/cjs/core/checks/languages/lint.js +12 -0
- package/dist/cjs/core/checks/languages/ruff-shared.js +6 -1
- package/dist/cjs/core/checks/pr/commitlint.js +4 -2
- package/dist/cjs/core/checks/quality/package-size.d.ts +23 -1
- package/dist/cjs/core/checks/quality/package-size.js +20 -4
- package/dist/cjs/core/checks/quality/spellcheck.js +8 -5
- package/dist/cjs/core/install/doctor.js +12 -0
- package/dist/cjs/core/install/grype-db.d.ts +25 -0
- package/dist/cjs/core/install/grype-db.js +45 -0
- package/dist/cjs/core/install/profiles.d.ts +1 -1
- package/dist/cjs/core/install/profiles.js +1 -1
- package/dist/cjs/core/install/reuse.js +6 -3
- package/dist/cjs/init.js +48 -2
- package/dist/cjs/scripts/copy-configs.js +9 -0
- package/dist/cjs/utils/own-deps.js +8 -1
- package/dist/cjs/utils/resolve-bin.d.ts +2 -0
- package/dist/cjs/utils/resolve-bin.js +5 -0
- package/dist/esm/cli.js +14 -1
- package/dist/esm/config/pr-checkmate.template.json +393 -0
- package/dist/esm/core/checks/dependencies/dependency-check.js +2 -2
- package/dist/esm/core/checks/dependencies/grype-scan.js +18 -0
- package/dist/esm/core/checks/dependencies/license-check.js +2 -2
- package/dist/esm/core/checks/dependencies/npm-audit.js +2 -2
- package/dist/esm/core/checks/git/diff-smell.js +1 -1
- package/dist/esm/core/checks/languages/cpp-format.js +1 -1
- package/dist/esm/core/checks/languages/lint.js +12 -3
- package/dist/esm/core/checks/languages/prettier.js +3 -3
- package/dist/esm/core/checks/languages/python-format.js +2 -2
- package/dist/esm/core/checks/languages/python-lint.js +2 -2
- package/dist/esm/core/checks/languages/ruff-shared.js +6 -1
- package/dist/esm/core/checks/languages/typecheck.js +1 -1
- package/dist/esm/core/checks/pr/commitlint.js +4 -2
- package/dist/esm/core/checks/pr/pr-body.js +1 -1
- package/dist/esm/core/checks/pr/pr-size-check.js +1 -1
- package/dist/esm/core/checks/pr/pr-title-lint.js +1 -1
- package/dist/esm/core/checks/quality/dead-code.js +1 -1
- package/dist/esm/core/checks/quality/duplicate-check.js +1 -1
- package/dist/esm/core/checks/quality/markdown-lint.js +1 -1
- package/dist/esm/core/checks/quality/package-size.d.ts +23 -1
- package/dist/esm/core/checks/quality/package-size.js +19 -4
- package/dist/esm/core/checks/quality/spellcheck.js +11 -8
- package/dist/esm/core/checks/security/diff-security.js +1 -1
- package/dist/esm/core/checks/security/security.js +1 -1
- package/dist/esm/core/install/doctor.js +12 -0
- package/dist/esm/core/install/grype-db.d.ts +25 -0
- package/dist/esm/core/install/grype-db.js +41 -0
- package/dist/esm/core/install/prepare.js +1 -1
- package/dist/esm/core/install/profiles.d.ts +1 -1
- package/dist/esm/core/install/profiles.js +1 -1
- package/dist/esm/core/install/reuse.js +6 -3
- package/dist/esm/core/run-checks.js +2 -2
- package/dist/esm/init.js +50 -4
- package/dist/esm/scripts/copy-configs.js +9 -0
- package/dist/esm/utils/own-deps.js +8 -1
- package/dist/esm/utils/resolve-bin.d.ts +2 -0
- package/dist/esm/utils/resolve-bin.js +4 -0
- package/eslint-default-rules.json +2 -8
- package/eslint.config.mjs +37 -8
- package/install-manifests/commitlint/package-lock.json +5 -4
- package/install-manifests/core/package-lock.json +5 -3
- package/install-manifests/js/package-lock.json +250 -571
- package/install-manifests/js/package.json +10 -8
- package/install-manifests/markdown/package-lock.json +16 -13
- package/install-manifests/markdown/package.json +3 -0
- package/install-manifests/spellcheck/package-lock.json +3 -3
- package/package.json +19 -15
- package/src/config/.eslintrc.js +3 -9
- package/src/config/eslint-default-rules.json +2 -8
|
@@ -204,12 +204,15 @@ function dropMissingLanguages(language, missing) {
|
|
|
204
204
|
*/
|
|
205
205
|
function resolveFrom(cwd) {
|
|
206
206
|
return id => {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
207
|
+
// The client's copy first, then the fetched `spellcheck` profile (cspell's own
|
|
208
|
+
// dictionaries live there), then our tree.
|
|
209
|
+
for (const root of [cwd, ...(0, index_js_2.registeredToolPaths)()]) {
|
|
210
|
+
try {
|
|
211
|
+
return require.resolve(id, { paths: [root] });
|
|
212
|
+
}
|
|
213
|
+
catch { }
|
|
212
214
|
}
|
|
215
|
+
return require.resolve(id);
|
|
213
216
|
};
|
|
214
217
|
}
|
|
215
218
|
function createTempCspellConfig(cwd) {
|
|
@@ -16,6 +16,7 @@ const package_version_js_1 = require("../../utils/package-version.js");
|
|
|
16
16
|
Object.defineProperty(exports, "packageVersion", { enumerable: true, get: function () { return package_version_js_1.packageVersion; } });
|
|
17
17
|
const cache_js_1 = require("./cache.js");
|
|
18
18
|
const ensure_js_1 = require("./ensure.js");
|
|
19
|
+
const grype_db_js_1 = require("./grype-db.js");
|
|
19
20
|
const prepare_js_1 = require("./prepare.js");
|
|
20
21
|
const profiles_js_1 = require("./profiles.js");
|
|
21
22
|
/**
|
|
@@ -142,6 +143,17 @@ async function runDoctor(cwd = process.cwd()) {
|
|
|
142
143
|
lines.push(` ${missing.length} profile(s) not installed yet. They are fetched on the next`);
|
|
143
144
|
lines.push(' run, or now with: npx pr-checkmate install');
|
|
144
145
|
}
|
|
146
|
+
if (config.grypeScan?.enabled !== false) {
|
|
147
|
+
const db = await (0, grype_db_js_1.grypeDbState)(cwd);
|
|
148
|
+
lines.push('');
|
|
149
|
+
lines.push(' Grype database (shared by all projects on this machine)');
|
|
150
|
+
if (db.state === 'missing-tool')
|
|
151
|
+
lines.push(' — grype is not installed (brew install grype)');
|
|
152
|
+
else if (db.state === 'ready')
|
|
153
|
+
lines.push(` ✅ up to date${db.built ? ` (built ${db.built})` : ''}`);
|
|
154
|
+
else
|
|
155
|
+
lines.push(` ⬇️ stale${db.built ? ` (built ${db.built})` : ''} — refresh: npx pr-checkmate install`);
|
|
156
|
+
}
|
|
145
157
|
const stale = (0, cache_js_1.stalePaths)((0, package_version_js_1.packageVersion)(), config.install?.cacheDir);
|
|
146
158
|
if (stale.length > 0) {
|
|
147
159
|
lines.push('');
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
type GrypeDbState = {
|
|
2
|
+
state: 'missing-tool';
|
|
3
|
+
} | {
|
|
4
|
+
state: 'ready';
|
|
5
|
+
built?: string;
|
|
6
|
+
} | {
|
|
7
|
+
state: 'stale';
|
|
8
|
+
built?: string;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Is grype's vulnerability database usable right now? grype refuses one older than
|
|
12
|
+
* 5 days (exit code 1 from `db status`). The database lives in grype's own cache,
|
|
13
|
+
* shared by every project on the machine, so one refresh serves them all.
|
|
14
|
+
*/
|
|
15
|
+
export declare function grypeDbState(cwd: string): Promise<GrypeDbState>;
|
|
16
|
+
/**
|
|
17
|
+
* Download a fresh database when the current one is missing or stale.
|
|
18
|
+
*
|
|
19
|
+
* `timeoutMs` bounds the download: a check inside a run passes one so it can never
|
|
20
|
+
* look like a hung commit, `install` passes none because a person asked for exactly
|
|
21
|
+
* this and the download is the whole job. Returns why it is still unusable, or
|
|
22
|
+
* `null` when it is ready.
|
|
23
|
+
*/
|
|
24
|
+
export declare function ensureGrypeDb(cwd: string, timeoutMs?: number): Promise<string | null>;
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.grypeDbState = grypeDbState;
|
|
4
|
+
exports.ensureGrypeDb = ensureGrypeDb;
|
|
5
|
+
const execa_js_1 = require("../../utils/execa.js");
|
|
6
|
+
const logger_js_1 = require("../../utils/logger.js");
|
|
7
|
+
/** `Built:` line of `grype db status`, so `doctor` can say how old the data is. */
|
|
8
|
+
const builtOf = (stdout) => typeof stdout === 'string' ? /^Built:\s*(.+)$/m.exec(stdout)?.[1]?.trim() : undefined;
|
|
9
|
+
/**
|
|
10
|
+
* Is grype's vulnerability database usable right now? grype refuses one older than
|
|
11
|
+
* 5 days (exit code 1 from `db status`). The database lives in grype's own cache,
|
|
12
|
+
* shared by every project on the machine, so one refresh serves them all.
|
|
13
|
+
*/
|
|
14
|
+
async function grypeDbState(cwd) {
|
|
15
|
+
const status = await (0, execa_js_1.execa)('grype', ['db', 'status'], { cwd, reject: false, timeout: 30000 });
|
|
16
|
+
if (status.code === 'ENOENT')
|
|
17
|
+
return { state: 'missing-tool' };
|
|
18
|
+
const built = builtOf(status.stdout);
|
|
19
|
+
return status.exitCode === 0 ? { state: 'ready', built } : { state: 'stale', built };
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Download a fresh database when the current one is missing or stale.
|
|
23
|
+
*
|
|
24
|
+
* `timeoutMs` bounds the download: a check inside a run passes one so it can never
|
|
25
|
+
* look like a hung commit, `install` passes none because a person asked for exactly
|
|
26
|
+
* this and the download is the whole job. Returns why it is still unusable, or
|
|
27
|
+
* `null` when it is ready.
|
|
28
|
+
*/
|
|
29
|
+
async function ensureGrypeDb(cwd, timeoutMs) {
|
|
30
|
+
const current = await grypeDbState(cwd);
|
|
31
|
+
if (current.state === 'missing-tool')
|
|
32
|
+
return 'ENOENT';
|
|
33
|
+
if (current.state === 'ready')
|
|
34
|
+
return null;
|
|
35
|
+
logger_js_1.logger.info('[Grype Scan]: vulnerability database missing or stale — updating it');
|
|
36
|
+
const update = await (0, execa_js_1.execa)('grype', ['db', 'update'], {
|
|
37
|
+
cwd,
|
|
38
|
+
reject: false,
|
|
39
|
+
...(timeoutMs ? { timeout: timeoutMs } : {}),
|
|
40
|
+
});
|
|
41
|
+
if (update.timedOut) {
|
|
42
|
+
return `database update exceeded ${timeoutMs}ms — run "grype db update" once, then retry`;
|
|
43
|
+
}
|
|
44
|
+
return update.exitCode === 0 ? null : 'database update failed (offline?) — run "grype db update"';
|
|
45
|
+
}
|
|
@@ -3,7 +3,7 @@ import type { Language, LanguageUsage } from '../../utils/detect-language.js';
|
|
|
3
3
|
/**
|
|
4
4
|
* A named bundle of tooling, and the checks that stop working without it.
|
|
5
5
|
*
|
|
6
|
-
* The
|
|
6
|
+
* The tooling totals ~233 MB, and no repository needs all of it: a
|
|
7
7
|
* Python service pays for the whole JavaScript toolchain, a repository with
|
|
8
8
|
* Spellcheck switched off still pays for cspell. Profiles are how that stops —
|
|
9
9
|
* they name which slice of the tooling a given repository actually needs.
|
|
@@ -25,9 +25,9 @@ exports.PROFILES = [
|
|
|
25
25
|
'typescript',
|
|
26
26
|
'eslint',
|
|
27
27
|
'eslint-config-prettier',
|
|
28
|
-
'eslint-plugin-import-x',
|
|
29
28
|
'eslint-plugin-jsdoc',
|
|
30
29
|
'eslint-plugin-prettier',
|
|
30
|
+
'eslint-plugin-simple-import-sort',
|
|
31
31
|
'eslint-plugin-sonarjs',
|
|
32
32
|
'@typescript-eslint/eslint-plugin',
|
|
33
33
|
'@typescript-eslint/parser',
|
|
@@ -12,15 +12,18 @@ const resolve_bin_js_1 = require("../../utils/resolve-bin.js");
|
|
|
12
12
|
/**
|
|
13
13
|
* The major version we declare for a tool, from our own manifest.
|
|
14
14
|
*
|
|
15
|
-
* Read from
|
|
15
|
+
* Read from the manifest rather than from the installed tree: what matters is
|
|
16
16
|
* the version our configuration was written against, and a hoisted install can
|
|
17
17
|
* resolve to something else entirely.
|
|
18
18
|
*/
|
|
19
19
|
function ownMajor(pkgName) {
|
|
20
20
|
try {
|
|
21
21
|
const manifest = node_path_1.default.join((0, resolve_bin_js_1.ownPackageRoot)(), 'package.json');
|
|
22
|
-
const
|
|
23
|
-
|
|
22
|
+
const parsed = JSON.parse(node_fs_1.default.readFileSync(manifest, 'utf-8'));
|
|
23
|
+
// Profile tooling is declared in `devDependencies` — it is fetched on demand
|
|
24
|
+
// rather than installed with us — but it is still the version our config was
|
|
25
|
+
// written against, and the manifest keeps both.
|
|
26
|
+
const range = parsed.dependencies?.[pkgName] ?? parsed.devDependencies?.[pkgName];
|
|
24
27
|
if (!range)
|
|
25
28
|
return null;
|
|
26
29
|
// `^10.6.0`, `>=3.0.0`, `~5.1` — the leading number is the major in every
|
package/dist/cjs/init.js
CHANGED
|
@@ -8,8 +8,8 @@ exports.setupWorkflow = setupWorkflow;
|
|
|
8
8
|
exports.buildInitialConfig = buildInitialConfig;
|
|
9
9
|
exports.init = init;
|
|
10
10
|
const fs_1 = __importDefault(require("fs"));
|
|
11
|
-
const path_1 = __importDefault(require("path"));
|
|
12
11
|
const inquirer_1 = __importDefault(require("inquirer"));
|
|
12
|
+
const path_1 = __importDefault(require("path"));
|
|
13
13
|
const index_js_1 = require("./config/index.js");
|
|
14
14
|
const findings_js_1 = require("./core/checks/findings.js");
|
|
15
15
|
const index_js_2 = require("./utils/index.js");
|
|
@@ -270,8 +270,46 @@ const HOW_TO = [
|
|
|
270
270
|
`Every option, annotated, with its default: ${index_js_1.FULL_CONFIG_URL}`,
|
|
271
271
|
`Docs: ${index_js_1.DOCS_URL}`,
|
|
272
272
|
].join(' ');
|
|
273
|
+
// Written out at its defaults so the options are visible and editable in the file
|
|
274
|
+
// instead of having to be looked up. Same trade as `max-len`: the values stop
|
|
275
|
+
// tracking the package, which is the price of being able to see them.
|
|
276
|
+
const GRYPE_SCAN_SEED = {
|
|
277
|
+
'//': 'Anchore grype, cross-ecosystem. ON by default but skips when grype is absent from the runner. Only direct dependencies are reported.',
|
|
278
|
+
enabled: true,
|
|
279
|
+
minSeverity: 'medium',
|
|
280
|
+
'//ignore': 'Suppress by CVE/GHSA id or by package name.',
|
|
281
|
+
ignore: [],
|
|
282
|
+
'//timeoutMs': 'Cap on the scan. On timeout the check skips rather than fails.',
|
|
283
|
+
timeoutMs: 120000,
|
|
284
|
+
'//ci': 'Per-environment overrides (enabled, minSeverity, ignore, timeoutMs): "ci" applies on CI builds, "local" elsewhere. Example: "local": { "enabled": false }',
|
|
285
|
+
ci: {},
|
|
286
|
+
local: {},
|
|
287
|
+
};
|
|
288
|
+
/**
|
|
289
|
+
* Every option the template documents that the file does not already carry, at
|
|
290
|
+
* its default. Without this, options such as `vulnScan` (off by default) or
|
|
291
|
+
* `licenseCheck` exist only in the docs, and nobody edits what they cannot see.
|
|
292
|
+
* Comment keys travel with the option they describe, section headers with the
|
|
293
|
+
* first option after them.
|
|
294
|
+
*/
|
|
295
|
+
function templateExtras(existing) {
|
|
296
|
+
const templatePath = path_1.default.join(__dirname, 'config', 'pr-checkmate.template.json');
|
|
297
|
+
if (!fs_1.default.existsSync(templatePath)) {
|
|
298
|
+
return {};
|
|
299
|
+
}
|
|
300
|
+
const template = JSON.parse(fs_1.default.readFileSync(templatePath, 'utf-8'));
|
|
301
|
+
const extras = {};
|
|
302
|
+
for (const [key, value] of Object.entries(template)) {
|
|
303
|
+
const option = key.startsWith('//') ? key.slice(2) : key;
|
|
304
|
+
if (key in existing || option in existing) {
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
extras[key] = value;
|
|
308
|
+
}
|
|
309
|
+
return extras;
|
|
310
|
+
}
|
|
273
311
|
function buildInitialConfig(sourcePath, languages) {
|
|
274
|
-
|
|
312
|
+
const base = {
|
|
275
313
|
$schema: SCHEMA_PATH,
|
|
276
314
|
'// how-to': HOW_TO,
|
|
277
315
|
commands: index_js_1.DEFAULT_COMMANDS,
|
|
@@ -352,9 +390,13 @@ function buildInitialConfig(sourcePath, languages) {
|
|
|
352
390
|
dataflowWindow: findings_js_1.DEFAULT_DATAFLOW_WINDOW,
|
|
353
391
|
ignorePaths: [...index_js_1.DIFF_SECURITY_IGNORE_DEFAULTS],
|
|
354
392
|
},
|
|
393
|
+
grypeScan: structuredClone(GRYPE_SCAN_SEED),
|
|
355
394
|
documentation: index_js_1.documentation,
|
|
356
395
|
languages,
|
|
357
396
|
};
|
|
397
|
+
const extras = templateExtras(base);
|
|
398
|
+
const { documentation: docs, languages: langs, ...rest } = base;
|
|
399
|
+
return { ...rest, ...extras, documentation: docs, languages: langs };
|
|
358
400
|
}
|
|
359
401
|
/** First-run setup: prompt, write config, and scaffold workflow + tsconfig. */
|
|
360
402
|
async function createConfig(root) {
|
|
@@ -403,6 +445,10 @@ async function updateConfig(root) {
|
|
|
403
445
|
$schema: json.$schema ?? SCHEMA_PATH,
|
|
404
446
|
'// how-to': json['// how-to'] ?? HOW_TO,
|
|
405
447
|
...json,
|
|
448
|
+
// An existing file predates this option, so nobody has seen it. Added only
|
|
449
|
+
// when missing; a client's own block is never touched.
|
|
450
|
+
grypeScan: json.grypeScan ?? structuredClone(GRYPE_SCAN_SEED),
|
|
451
|
+
...templateExtras({ ...json, grypeScan: true }),
|
|
406
452
|
languages,
|
|
407
453
|
};
|
|
408
454
|
fs_1.default.writeFileSync(CONFIG_FILE, `${JSON.stringify(updated, null, 2)}\n`);
|
|
@@ -36,6 +36,15 @@ const configFiles = [
|
|
|
36
36
|
source: 'src/config/pr-checkmate-workflow-docker.yml',
|
|
37
37
|
dest: 'dist/esm/config/pr-checkmate-workflow-docker.yml',
|
|
38
38
|
},
|
|
39
|
+
// Read by `init` to seed every option into a new pr-checkmate.json.
|
|
40
|
+
{
|
|
41
|
+
source: 'src/config/pr-checkmate.template.json',
|
|
42
|
+
dest: 'dist/cjs/config/pr-checkmate.template.json',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
source: 'src/config/pr-checkmate.template.json',
|
|
46
|
+
dest: 'dist/esm/config/pr-checkmate.template.json',
|
|
47
|
+
},
|
|
39
48
|
{ source: 'src/config/pr-checkmate.schema.json', dest: 'pr-checkmate.schema.json' },
|
|
40
49
|
];
|
|
41
50
|
/**
|
|
@@ -7,6 +7,7 @@ exports.findOwnPackageRoot = findOwnPackageRoot;
|
|
|
7
7
|
exports.getOwnDependencyClosure = getOwnDependencyClosure;
|
|
8
8
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
9
9
|
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
+
const profiles_js_1 = require("../core/install/profiles.js");
|
|
10
11
|
const OWN_PACKAGE_NAME = 'pr-checkmate';
|
|
11
12
|
const PACKAGE_JSON = 'package.json';
|
|
12
13
|
let cachedClosure;
|
|
@@ -89,7 +90,13 @@ function getOwnDependencyClosure() {
|
|
|
89
90
|
}
|
|
90
91
|
// BFS over the dependency graph, resolving each package relative to the parent
|
|
91
92
|
// that declared it so nested (non-hoisted) installs resolve correctly.
|
|
92
|
-
|
|
93
|
+
//
|
|
94
|
+
// Profile tooling is ours even though it no longer sits in `dependencies`: it is
|
|
95
|
+
// fetched on demand, so the roots are what we declare plus what the profiles
|
|
96
|
+
// name. Without the second half every fetched tool is "the client's" and its
|
|
97
|
+
// licence blocks the client for a dependency they cannot remove.
|
|
98
|
+
const roots = new Set([...readDependencyNames(root), ...profiles_js_1.PROFILES.flatMap(p => p.packages)]);
|
|
99
|
+
const queue = [...roots].map(name => ({
|
|
93
100
|
name,
|
|
94
101
|
fromDir: root,
|
|
95
102
|
}));
|
|
@@ -16,6 +16,8 @@ export interface ResolvedTool {
|
|
|
16
16
|
* to a run from before there was anything to fetch.
|
|
17
17
|
*/
|
|
18
18
|
export declare function registerToolPaths(dirs: string[]): void;
|
|
19
|
+
/** The roots registered for this run, for tools that load plugins by themselves. */
|
|
20
|
+
export declare function registeredToolPaths(): string[];
|
|
19
21
|
/** Drop every registered profile root. For tests, and for a second run in-process. */
|
|
20
22
|
export declare function clearToolPaths(): void;
|
|
21
23
|
/** The root of the installed pr-checkmate package — where our own tooling lives. */
|
|
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.registerToolPaths = registerToolPaths;
|
|
7
|
+
exports.registeredToolPaths = registeredToolPaths;
|
|
7
8
|
exports.clearToolPaths = clearToolPaths;
|
|
8
9
|
exports.ownPackageRoot = ownPackageRoot;
|
|
9
10
|
exports.packagePresent = packagePresent;
|
|
@@ -33,6 +34,10 @@ let profileRoots = [];
|
|
|
33
34
|
function registerToolPaths(dirs) {
|
|
34
35
|
profileRoots = [...dirs];
|
|
35
36
|
}
|
|
37
|
+
/** The roots registered for this run, for tools that load plugins by themselves. */
|
|
38
|
+
function registeredToolPaths() {
|
|
39
|
+
return [...profileRoots];
|
|
40
|
+
}
|
|
36
41
|
/** Drop every registered profile root. For tests, and for a second run in-process. */
|
|
37
42
|
function clearToolPaths() {
|
|
38
43
|
profileRoots = [];
|
package/dist/esm/cli.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { loadProjectConfig } from './config/loader.js';
|
|
2
3
|
import { COMMAND_TO_CHECK, FLAGS, STANDALONE_COMMANDS, TOOLING_COMMANDS } from './core/commands.js';
|
|
3
4
|
import { runChecks } from './core/index.js';
|
|
4
5
|
import { installProfiles, pruneCache, runDoctor } from './core/install/doctor.js';
|
|
6
|
+
import { ensureGrypeDb } from './core/install/grype-db.js';
|
|
5
7
|
import { init } from './init.js';
|
|
6
|
-
import {
|
|
8
|
+
import { getUpdateNotice, logger } from './utils/index.js';
|
|
7
9
|
const argv = process.argv.slice(2);
|
|
8
10
|
/**
|
|
9
11
|
* Flags are separated from positional arguments so they compose with every command
|
|
@@ -88,6 +90,17 @@ void (async () => {
|
|
|
88
90
|
for (const result of failed) {
|
|
89
91
|
logger.error(`[install]: ❌ ${result.profiles.join(', ') || 'tooling'}: ${result.reason}`);
|
|
90
92
|
}
|
|
93
|
+
// Warm grype's vulnerability database too, with no time cap: the download is
|
|
94
|
+
// hundreds of MB and the scan itself would only be allowed a couple of minutes.
|
|
95
|
+
// Best-effort — an absent grype or a failed download is a warning, not a
|
|
96
|
+
// failed install, since Grype Scan skips cleanly without it.
|
|
97
|
+
if (loadProjectConfig().grypeScan?.enabled !== false) {
|
|
98
|
+
const why = await ensureGrypeDb(process.cwd());
|
|
99
|
+
if (why && why !== 'ENOENT')
|
|
100
|
+
logger.warn(`[install]: ⚠️ grype: ${why}`);
|
|
101
|
+
else if (!why)
|
|
102
|
+
logger.info('[install]: grype vulnerability database is up to date');
|
|
103
|
+
}
|
|
91
104
|
process.exit(failed.length > 0 ? 1 : 0);
|
|
92
105
|
}
|
|
93
106
|
if (cmd === 'doctor') {
|