@socketsecurity/cli-with-sentry 1.9.1 → 1.10.1
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/CHANGELOG.md +31 -0
- package/README.md +5 -5
- package/dist/cli.js +379 -127
- package/dist/cli.js.map +1 -1
- package/dist/constants.js +4 -4
- package/dist/constants.js.map +1 -1
- package/dist/manifest-scripts/maven-extension/coana-maven-extension.jar +0 -0
- package/dist/manifest-scripts/socket-facts.init.gradle +244 -56
- package/dist/manifest-scripts/socket-facts.plugin.scala +290 -73
- package/dist/tsconfig.dts.tsbuildinfo +1 -1
- package/dist/types/commands/manifest/cmd-manifest-dynamic-sbom-inference.d.mts.map +1 -1
- package/dist/types/commands/manifest/convert-gradle-to-facts.d.mts +5 -2
- package/dist/types/commands/manifest/convert-gradle-to-facts.d.mts.map +1 -1
- package/dist/types/commands/manifest/convert-maven-to-facts.d.mts +5 -2
- package/dist/types/commands/manifest/convert-maven-to-facts.d.mts.map +1 -1
- package/dist/types/commands/manifest/convert-sbt-to-facts.d.mts +5 -2
- package/dist/types/commands/manifest/convert-sbt-to-facts.d.mts.map +1 -1
- package/dist/types/commands/manifest/enumerate-workspaces.d.mts.map +1 -1
- package/dist/types/commands/manifest/generate-recursive-manifests.d.mts +8 -1
- package/dist/types/commands/manifest/generate-recursive-manifests.d.mts.map +1 -1
- package/dist/types/commands/manifest/generate_auto_manifest.d.mts.map +1 -1
- package/dist/types/commands/manifest/handle-manifest-dynamic-sbom-inference.d.mts +2 -1
- package/dist/types/commands/manifest/handle-manifest-dynamic-sbom-inference.d.mts.map +1 -1
- package/dist/types/commands/manifest/run-manifest-facts.d.mts +4 -1
- package/dist/types/commands/manifest/run-manifest-facts.d.mts.map +1 -1
- package/dist/types/commands/manifest/scripts/assemble.d.mts.map +1 -1
- package/dist/types/commands/manifest/scripts/facts.d.mts +3 -2
- package/dist/types/commands/manifest/scripts/facts.d.mts.map +1 -1
- package/dist/types/commands/manifest/scripts/records.d.mts +10 -4
- package/dist/types/commands/manifest/scripts/records.d.mts.map +1 -1
- package/dist/types/commands/manifest/scripts/resolution-report-gradle.d.mts +3 -2
- package/dist/types/commands/manifest/scripts/resolution-report-gradle.d.mts.map +1 -1
- package/dist/types/commands/manifest/scripts/resolution-report-maven.d.mts +3 -3
- package/dist/types/commands/manifest/scripts/resolution-report-maven.d.mts.map +1 -1
- package/dist/types/commands/manifest/scripts/resolution-report-render.d.mts +29 -8
- package/dist/types/commands/manifest/scripts/resolution-report-render.d.mts.map +1 -1
- package/dist/types/commands/manifest/scripts/resolution-report.d.mts +14 -3
- package/dist/types/commands/manifest/scripts/resolution-report.d.mts.map +1 -1
- package/dist/types/commands/manifest/scripts/run.d.mts +2 -1
- package/dist/types/commands/manifest/scripts/run.d.mts.map +1 -1
- package/dist/types/commands/manifest/scripts/sidecar.d.mts.map +1 -1
- package/dist/types/commands/scan/cmd-scan-create.d.mts.map +1 -1
- package/dist/types/commands/scan/cmd-scan-reach.d.mts.map +1 -1
- package/dist/types/commands/scan/handle-create-new-scan.d.mts.map +1 -1
- package/dist/types/commands/scan/warn-socket-json-only-flags.d.mts +2 -0
- package/dist/types/commands/scan/warn-socket-json-only-flags.d.mts.map +1 -0
- package/dist/types/constants.d.mts +1 -1
- package/dist/utils.js +2 -1
- package/dist/utils.js.map +1 -1
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -1824,12 +1824,16 @@ function reachMemoryLimitToMb(value) {
|
|
|
1824
1824
|
// independent reactors is intentionally duplicated across all of their
|
|
1825
1825
|
// components[].
|
|
1826
1826
|
|
|
1827
|
-
// `[]` means resolved and found nothing (e.g. a pom/BOM with no artifact)
|
|
1827
|
+
// `[]` means resolved and found nothing (e.g. a pom/BOM with no artifact); no
|
|
1828
|
+
// `targets` means the artifact did not resolve.
|
|
1828
1829
|
function attachPaths(entry, artifactPaths) {
|
|
1829
1830
|
const paths = artifactPaths.pathsById.get(entry.id);
|
|
1831
|
+
const targets = paths ? paths.targets : [];
|
|
1830
1832
|
return {
|
|
1831
1833
|
...entry,
|
|
1832
|
-
|
|
1834
|
+
...(targets ? {
|
|
1835
|
+
targets: [...targets]
|
|
1836
|
+
} : {}),
|
|
1833
1837
|
sources: [...(paths?.sources ?? [])]
|
|
1834
1838
|
};
|
|
1835
1839
|
}
|
|
@@ -2351,9 +2355,17 @@ function classifyGradleFailure(detail) {
|
|
|
2351
2355
|
if (t.includes('no matching variant') || t.includes('no variants of') || t.includes('unable to find a matching variant') || t.includes('no compatible variant')) {
|
|
2352
2356
|
return 'no-matching-variant';
|
|
2353
2357
|
}
|
|
2354
|
-
|
|
2358
|
+
// The artifact-level form: several transforms produce the requested variant
|
|
2359
|
+
// ("transformation chains" from Gradle 8.12, "transforms" before it).
|
|
2360
|
+
if (t.includes('cannot choose between') || t.includes('found multiple transformation chains') || t.includes('found multiple transforms that can produce a variant')) {
|
|
2355
2361
|
return 'variant-ambiguity';
|
|
2356
2362
|
}
|
|
2363
|
+
// One line per problem of the artifact; a checksum missing from the
|
|
2364
|
+
// metadata is no evidence against it, unlike any other problem.
|
|
2365
|
+
const problems = t.split('\n').filter(l => l.startsWith('on artifact '));
|
|
2366
|
+
if (problems.length && problems.every(l => l.endsWith(': checksum is missing from verification metadata.'))) {
|
|
2367
|
+
return 'checksum-missing';
|
|
2368
|
+
}
|
|
2357
2369
|
if (t.includes('could not get') || t.includes('could not head') || t.includes('status code 401') || t.includes('status code 403') || t.includes('connection refused') || t.includes('connection timed out') || t.includes('read timed out') || t.includes('certification path') || t.includes('peer not authenticated')) {
|
|
2358
2370
|
return 'repository-or-network';
|
|
2359
2371
|
}
|
|
@@ -2365,8 +2377,9 @@ function classifyGradleFailure(detail) {
|
|
|
2365
2377
|
|
|
2366
2378
|
// Every kind blocks except variant-ambiguity: the module demonstrably exists
|
|
2367
2379
|
// and is almost always captured via another configuration, so it's benign for
|
|
2368
|
-
// the SBOM (non-blocking, count only).
|
|
2369
|
-
//
|
|
2380
|
+
// the SBOM (non-blocking, count only). The renderer blocks it anyway when the
|
|
2381
|
+
// module resolved in no configuration of its project. Nor does checksum-missing:
|
|
2382
|
+
// teams often record checksums only for what their own tasks resolve.
|
|
2370
2383
|
const GRADLE_DIALECT = {
|
|
2371
2384
|
label: 'Gradle',
|
|
2372
2385
|
classify: classifyGradleFailure,
|
|
@@ -2399,8 +2412,13 @@ const GRADLE_DIALECT = {
|
|
|
2399
2412
|
blocking: true
|
|
2400
2413
|
}, {
|
|
2401
2414
|
key: 'variant-ambiguity',
|
|
2415
|
+
header: n => ` Ambiguous variant — ${n} could not choose a variant, and the module resolved in no other configuration:`,
|
|
2402
2416
|
blocking: false,
|
|
2403
2417
|
notice: (n, depCount, configCount) => `Skipped ${depCount} dependency(ies) with ambiguous variant selection in ${configCount} configuration(s) — re-run with --verbose for ${n}'s messages.`
|
|
2418
|
+
}, {
|
|
2419
|
+
key: 'checksum-missing',
|
|
2420
|
+
blocking: false,
|
|
2421
|
+
notice: (n, depCount, configCount) => `Left ${depCount} dependency(ies) uninstalled in ${configCount} configuration(s): ${n}'s verification metadata has no checksum for their artifacts — re-run with --verbose for ${n}'s messages.`
|
|
2404
2422
|
}]
|
|
2405
2423
|
};
|
|
2406
2424
|
|
|
@@ -2445,19 +2463,29 @@ const SBT_DIALECT = {
|
|
|
2445
2463
|
}]
|
|
2446
2464
|
};
|
|
2447
2465
|
|
|
2448
|
-
// Maven's resolver (Aether/maven-resolver): no attribute-based variants.
|
|
2449
|
-
// failure shapes (artifact-resolution miss
|
|
2450
|
-
//
|
|
2466
|
+
// Maven's resolver (Aether/maven-resolver): no attribute-based variants. Both
|
|
2467
|
+
// failure shapes (artifact-resolution miss, dependency-graph build failure)
|
|
2468
|
+
// classify off the root-cause message.
|
|
2451
2469
|
function classifyMavenFailure(detail) {
|
|
2452
2470
|
const t = (detail || '').toLowerCase();
|
|
2453
|
-
if (t.includes('could not transfer') || t.includes('connection refused') || t.includes('connect timed out') || t.includes('connection timed out') || t.includes('read timed out') || t.includes('status code: 401') || t.includes('status code: 403') || t.includes('unauthorized') || t.includes('forbidden') || t.includes('peer not authenticated') || t.includes('certpathbuilderexception')
|
|
2471
|
+
if (t.includes('could not transfer') || t.includes('connection refused') || t.includes('connect timed out') || t.includes('connection timed out') || t.includes('read timed out') || t.includes('status code: 401') || t.includes('status code: 403') || t.includes('unauthorized') || t.includes('forbidden') || t.includes('peer not authenticated') || t.includes('certpathbuilderexception') ||
|
|
2472
|
+
// Maven 3.8.1+ blocks plain-HTTP repositories behind its
|
|
2473
|
+
// maven-default-http-blocker mirror.
|
|
2474
|
+
t.includes('blocked mirror for repositories') ||
|
|
2475
|
+
// Offline (-o): no repository was asked, so the artifact may well exist.
|
|
2476
|
+
t.includes('in offline mode')) {
|
|
2454
2477
|
return 'repository-or-network';
|
|
2455
2478
|
}
|
|
2456
|
-
if (t.includes('could not find artifact') || t.includes('failure to find') || t.includes('could not resolve') || t.includes('no versions available') || t.includes('not found')
|
|
2479
|
+
if (t.includes('could not find artifact') || t.includes('failure to find') || t.includes('could not resolve') || t.includes('no versions available') || t.includes('not found') ||
|
|
2480
|
+
// maven-resolver's remote repository filters (prefixes, groupId): the
|
|
2481
|
+
// repository is known not to serve that path or group.
|
|
2482
|
+
t.includes('not allowed from')) {
|
|
2457
2483
|
return 'not-found';
|
|
2458
2484
|
}
|
|
2459
2485
|
// POM exists but can't be read/parsed.
|
|
2460
|
-
if (t.includes('failed to read artifact descriptor') || t.includes('invalid pom') || t.includes('could not parse pom')
|
|
2486
|
+
if (t.includes('failed to read artifact descriptor') || t.includes('invalid pom') || t.includes('could not parse pom') ||
|
|
2487
|
+
// Maven's warning for a POM its descriptor policy tolerated as invalid.
|
|
2488
|
+
t.includes('is invalid, transitive dependencies')) {
|
|
2461
2489
|
return 'config-problem';
|
|
2462
2490
|
}
|
|
2463
2491
|
return 'other';
|
|
@@ -2488,68 +2516,143 @@ const MAVEN_DIALECT = {
|
|
|
2488
2516
|
}]
|
|
2489
2517
|
};
|
|
2490
2518
|
|
|
2519
|
+
// A resolution that threw without naming a dependency: one configuration, or
|
|
2520
|
+
// with an empty `config` the whole project.
|
|
2521
|
+
|
|
2522
|
+
// A module (`group:name`, from any longer coordinate) within one project.
|
|
2523
|
+
function moduleKey(project, coord) {
|
|
2524
|
+
return `${project}\t${coord.split(':').slice(0, 2).join(':')}`;
|
|
2525
|
+
}
|
|
2526
|
+
|
|
2491
2527
|
// Recognized from the build tool's message; drives wording AND whether the kind
|
|
2492
2528
|
// is blocking. An unrecognized message degrades to 'other' (blocking) — safe.
|
|
2493
2529
|
|
|
2494
2530
|
// Per-resolver classify + render/score policy.
|
|
2495
2531
|
|
|
2532
|
+
// Where the invoking command lets the user set each build option, so a remedy
|
|
2533
|
+
// names something they can actually set.
|
|
2534
|
+
|
|
2535
|
+
const DSI_SETUP_COMMAND = 'socket manifest setup --dynamic-sbom-inference';
|
|
2536
|
+
const REACH_CONTINUE_ON_INSTALL_ERRORS_FLAG = '--reach-continue-on-install-errors';
|
|
2537
|
+
|
|
2538
|
+
// The single-build manifest commands take every option as a flag.
|
|
2539
|
+
const FLAG_HINTS = {
|
|
2540
|
+
flags: new Set(['excludeConfigs', 'ignoreUnresolved', 'verbose']),
|
|
2541
|
+
setupCommand: 'socket manifest setup',
|
|
2542
|
+
socketJson: constants.default.SOCKET_JSON
|
|
2543
|
+
};
|
|
2496
2544
|
const RESOLUTION_REPORT_ARTIFACT_LIMIT = 15;
|
|
2497
2545
|
const RESOLUTION_REPORT_CONFIG_LIMIT = 20;
|
|
2498
2546
|
|
|
2499
2547
|
// Drop a bare "group:name" when a versioned "group:name:v" of the same module
|
|
2500
|
-
//
|
|
2501
|
-
|
|
2502
|
-
|
|
2548
|
+
// failed in the same projects: the lenient resolver reports both forms for one
|
|
2549
|
+
// failure.
|
|
2550
|
+
function dedupInfos(infos) {
|
|
2503
2551
|
const versioned = new Set();
|
|
2504
|
-
for (const
|
|
2505
|
-
const p =
|
|
2552
|
+
for (const i of infos) {
|
|
2553
|
+
const p = i.coord.split(':');
|
|
2506
2554
|
if (p.length >= 3) {
|
|
2507
|
-
|
|
2555
|
+
for (const project of i.projects) {
|
|
2556
|
+
versioned.add(moduleKey(project, i.coord));
|
|
2557
|
+
}
|
|
2508
2558
|
}
|
|
2509
2559
|
}
|
|
2510
|
-
return
|
|
2560
|
+
return infos.filter(i => i.coord.split(':').length >= 3 || ![...i.projects].every(p => versioned.has(moduleKey(p, i.coord)))).sort((a, b) => a.coord < b.coord ? -1 : a.coord > b.coord ? 1 : 0);
|
|
2561
|
+
}
|
|
2562
|
+
function firstLine(s) {
|
|
2563
|
+
return (s || '').split('\n').map(l => l.trim()).find(Boolean) ?? '';
|
|
2511
2564
|
}
|
|
2512
2565
|
function fmtList(list, limit) {
|
|
2513
2566
|
const shown = list.slice(0, limit).join(', ');
|
|
2514
2567
|
return list.length > limit ? `${shown} (+${list.length - limit} more)` : shown;
|
|
2515
2568
|
}
|
|
2516
|
-
function
|
|
2517
|
-
|
|
2569
|
+
function flagText(option, value) {
|
|
2570
|
+
const flag = `--${utils.camelToKebab(option)}`;
|
|
2571
|
+
return value === true ? flag : `${flag} '${value}'`;
|
|
2572
|
+
}
|
|
2573
|
+
|
|
2574
|
+
// Where a failure happened, for builds with several subprojects.
|
|
2575
|
+
function inProjects(projects) {
|
|
2576
|
+
const list = [...(typeof projects === 'string' ? [projects] : projects)].filter(Boolean).sort().map(p => `\`${p}\``);
|
|
2577
|
+
return list.length ? ` (in ${fmtList(list, 3)})` : '';
|
|
2578
|
+
}
|
|
2579
|
+
function installErrorsHint(hints, ignored) {
|
|
2580
|
+
const flag = hints.continueOnInstallErrorsFlag;
|
|
2581
|
+
if (!flag) {
|
|
2582
|
+
return [];
|
|
2583
|
+
}
|
|
2584
|
+
return [ignored ? `Reachability analysis cannot install the ignored dependencies; pass ${flag} unless you already do.` : `If you ignore unresolved dependencies, also pass ${flag}: reachability analysis cannot install them.`];
|
|
2518
2585
|
}
|
|
2519
2586
|
|
|
2520
|
-
//
|
|
2521
|
-
//
|
|
2522
|
-
|
|
2523
|
-
|
|
2587
|
+
// `excludableConfigs` is empty when excluding configurations cannot clear
|
|
2588
|
+
// every blocking failure.
|
|
2589
|
+
function proceedHint(hints, tool, excludableConfigs) {
|
|
2590
|
+
const options = [['ignoreUnresolved', true]];
|
|
2591
|
+
if (excludableConfigs.length) {
|
|
2592
|
+
options.push(['excludeConfigs', excludableConfigs.join(',')]);
|
|
2593
|
+
}
|
|
2594
|
+
const asFlags = options.filter(o => hints.flags.has(o[0]));
|
|
2595
|
+
const inSocketJson = options.filter(o => !hints.flags.has(o[0]));
|
|
2596
|
+
const setup = `\`${hints.setupCommand}\``;
|
|
2597
|
+
if (!inSocketJson.length) {
|
|
2598
|
+
return asFlags.length === 1 ? [`To proceed, re-run with ${flagText(...asFlags[0])}.`] : ['To proceed, re-run with either:', ...asFlags.map(o => ` ${flagText(...o)}`)];
|
|
2599
|
+
}
|
|
2600
|
+
if (!asFlags.length) {
|
|
2601
|
+
return [`To proceed, set ${options.length > 1 ? 'either of these' : 'this'} under defaults.manifest.${tool} in ${hints.socketJson} (${setup} walks you through it):`, ...options.map(([option, value]) => ` "${option}": ${JSON.stringify(value)}`)];
|
|
2602
|
+
}
|
|
2603
|
+
return ['To proceed, either:', ...asFlags.map(o => ` re-run with ${flagText(...o)}`), ...inSocketJson.map(([option, value]) => ` set defaults.manifest.${tool}.${option} to ${JSON.stringify(value)} in ${hints.socketJson} (${setup})`)];
|
|
2604
|
+
}
|
|
2605
|
+
|
|
2606
|
+
// What resolved elsewhere is consulted only by module (group:name): a failed
|
|
2607
|
+
// selector carries no classifier/type to relate it to one artifact.
|
|
2608
|
+
function renderResolutionReport(failures, scannedConfigs, tool, opts = {}) {
|
|
2609
|
+
const dialect = dialectFor(tool);
|
|
2524
2610
|
const name = dialect.label;
|
|
2611
|
+
const hints = opts.hints ?? FLAG_HINTS;
|
|
2525
2612
|
const unscannable = opts.unscannable ?? [];
|
|
2526
|
-
const unscannableConfigs = new Set(unscannable.map(u => u.config));
|
|
2613
|
+
const unscannableConfigs = new Set(unscannable.filter(u => u.config).map(u => u.config));
|
|
2527
2614
|
const specOf = new Map(dialect.categories.map(c => [c.key, c]));
|
|
2528
2615
|
const isBlocking = cat => specOf.get(cat)?.blocking ?? true;
|
|
2529
2616
|
|
|
2530
|
-
//
|
|
2531
|
-
//
|
|
2617
|
+
// A non-blocking kind is only benign when its module resolved anyway: one
|
|
2618
|
+
// that resolved in no configuration of its project is missing from that
|
|
2619
|
+
// project's SBOM.
|
|
2620
|
+
const resolved = opts.resolvedModules;
|
|
2621
|
+
const isFailureBlocking = (category, f) => isBlocking(category) || !!resolved && !resolved.has(moduleKey(f.project, f.coord));
|
|
2622
|
+
|
|
2623
|
+
// Aggregate by (coord, category, blocking): one module can fail with
|
|
2624
|
+
// different causes across configs, and block in only some projects. Keep
|
|
2625
|
+
// first-seen detail, union the configs and projects.
|
|
2532
2626
|
|
|
2533
2627
|
const byKey = new Map();
|
|
2534
|
-
const keyOf = (coord, category) => `${coord} ${category}`;
|
|
2535
2628
|
for (const f of failures) {
|
|
2536
2629
|
const category = dialect.classify(f.detail);
|
|
2537
|
-
const
|
|
2630
|
+
const blocking = isFailureBlocking(category, f);
|
|
2631
|
+
const key = `${f.coord} ${category} ${blocking}`;
|
|
2538
2632
|
let info = byKey.get(key);
|
|
2539
2633
|
if (!info) {
|
|
2540
2634
|
info = {
|
|
2541
2635
|
coord: f.coord,
|
|
2542
2636
|
category,
|
|
2637
|
+
blocking,
|
|
2543
2638
|
detail: f.detail,
|
|
2544
|
-
configs: new Set()
|
|
2639
|
+
configs: new Set(),
|
|
2640
|
+
projects: new Set(),
|
|
2641
|
+
configless: false
|
|
2545
2642
|
};
|
|
2546
2643
|
byKey.set(key, info);
|
|
2547
2644
|
}
|
|
2548
2645
|
if (f.config) {
|
|
2549
2646
|
info.configs.add(f.config);
|
|
2647
|
+
} else {
|
|
2648
|
+
info.configless = true;
|
|
2649
|
+
}
|
|
2650
|
+
if (f.project) {
|
|
2651
|
+
info.projects.add(f.project);
|
|
2550
2652
|
}
|
|
2551
2653
|
}
|
|
2552
2654
|
const allInfos = [...byKey.values()];
|
|
2655
|
+
const isInfoBlocking = info => info.blocking;
|
|
2553
2656
|
|
|
2554
2657
|
// A whole-config throw is classified by the same cause rules as a per-dep
|
|
2555
2658
|
// failure: ambiguity stays lenient, every other cause is fail-closed.
|
|
@@ -2563,31 +2666,43 @@ function renderResolutionReport(failures, scannedConfigs, dialect, opts = {}) {
|
|
|
2563
2666
|
});
|
|
2564
2667
|
const blockingUnscannable = unscannableInfos.filter(u => u.blocking);
|
|
2565
2668
|
const nonBlockingUnscannable = unscannableInfos.filter(u => !u.blocking);
|
|
2669
|
+
const blockingUnscannableConfigs = blockingUnscannable.filter(u => u.config);
|
|
2670
|
+
const blockingUnscannableProjects = blockingUnscannable.filter(u => !u.config);
|
|
2566
2671
|
const perDepBlockingConfigs = new Set();
|
|
2567
2672
|
for (const info of allInfos) {
|
|
2568
|
-
if (
|
|
2673
|
+
if (isInfoBlocking(info)) {
|
|
2569
2674
|
for (const c of info.configs) {
|
|
2570
2675
|
perDepBlockingConfigs.add(c);
|
|
2571
2676
|
}
|
|
2572
2677
|
}
|
|
2573
2678
|
}
|
|
2574
|
-
const blockingConfigs = new Set([...perDepBlockingConfigs, ...
|
|
2679
|
+
const blockingConfigs = new Set([...perDepBlockingConfigs, ...blockingUnscannableConfigs.map(u => u.config)]);
|
|
2575
2680
|
const blockingFailed = [...blockingConfigs].sort();
|
|
2681
|
+
// A blocking failure outside any configuration leaves every configuration of
|
|
2682
|
+
// its project incomplete, so none can be reported as succeeded.
|
|
2683
|
+
const hasBlockingConfiglessFailure = blockingUnscannableProjects.length > 0 || allInfos.some(i => i.configless && isInfoBlocking(i));
|
|
2576
2684
|
// An un-scannable config was attempted but resolved nothing, so it didn't succeed.
|
|
2577
|
-
const succeeded = scannedConfigs.filter(c => !blockingConfigs.has(c) && !unscannableConfigs.has(c)).sort();
|
|
2685
|
+
const succeeded = hasBlockingConfiglessFailure ? [] : scannedConfigs.filter(c => !blockingConfigs.has(c) && !unscannableConfigs.has(c)).sort();
|
|
2578
2686
|
const groups = dialect.categories.map(spec => ({
|
|
2579
2687
|
spec,
|
|
2580
|
-
infos:
|
|
2688
|
+
infos: dedupInfos(allInfos.filter(i => i.category === spec.key))
|
|
2689
|
+
}));
|
|
2690
|
+
const blockingGroups = groups.map(g => ({
|
|
2691
|
+
...g,
|
|
2692
|
+
infos: g.infos.filter(isInfoBlocking)
|
|
2693
|
+
})).filter(g => g.infos.length);
|
|
2694
|
+
const nonBlockingGroups = groups.map(g => ({
|
|
2695
|
+
...g,
|
|
2696
|
+
infos: g.infos.filter(i => !isInfoBlocking(i))
|
|
2581
2697
|
})).filter(g => g.infos.length);
|
|
2582
|
-
const blockingGroups = groups.filter(g => g.spec.blocking);
|
|
2583
|
-
const nonBlockingGroups = groups.filter(g => !g.spec.blocking);
|
|
2584
2698
|
const blockingCount = blockingGroups.reduce((n, g) => n + g.infos.length, 0);
|
|
2585
2699
|
const hasBlockingFailures = blockingCount > 0 || blockingUnscannable.length > 0;
|
|
2586
2700
|
const willFail = hasBlockingFailures && !opts.ignoreUnresolved;
|
|
2587
2701
|
const out = [];
|
|
2588
2702
|
if (hasBlockingFailures) {
|
|
2589
2703
|
if (blockingCount > 0) {
|
|
2590
|
-
|
|
2704
|
+
const inConfigs = perDepBlockingConfigs.size ? ` in ${perDepBlockingConfigs.size} configuration(s)` : '';
|
|
2705
|
+
out.push(opts.ignoreUnresolved ? `Ignored ${blockingCount} unresolved dependency(ies)${inConfigs}:` : `Could not resolve ${blockingCount} dependency(ies)${inConfigs}:`);
|
|
2591
2706
|
for (const {
|
|
2592
2707
|
infos,
|
|
2593
2708
|
spec
|
|
@@ -2597,29 +2712,36 @@ function renderResolutionReport(failures, scannedConfigs, dialect, opts = {}) {
|
|
|
2597
2712
|
for (const info of infos.slice(0, RESOLUTION_REPORT_ARTIFACT_LIMIT)) {
|
|
2598
2713
|
const fl = firstLine(info.detail);
|
|
2599
2714
|
const reasonSuffix = spec.showReason && fl ? ` [${fl}]` : '';
|
|
2600
|
-
out.push(` - ${info.coord}${reasonSuffix}`);
|
|
2715
|
+
out.push(` - ${info.coord}${inProjects(info.projects)}${reasonSuffix}`);
|
|
2601
2716
|
}
|
|
2602
2717
|
if (infos.length > RESOLUTION_REPORT_ARTIFACT_LIMIT) {
|
|
2603
2718
|
out.push(` … and ${infos.length - RESOLUTION_REPORT_ARTIFACT_LIMIT} more`);
|
|
2604
2719
|
}
|
|
2605
2720
|
}
|
|
2606
2721
|
}
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2722
|
+
const pushUnscannable = (items, header, label) => {
|
|
2723
|
+
if (!items.length) {
|
|
2724
|
+
return;
|
|
2725
|
+
}
|
|
2726
|
+
// Separate from the block above, but only if there is one — otherwise the
|
|
2727
|
+
// summary would lead with a blank line (a dangling ✗ under logger.fail).
|
|
2610
2728
|
if (out.length) {
|
|
2611
2729
|
out.push('');
|
|
2612
2730
|
}
|
|
2613
|
-
out.push(
|
|
2614
|
-
for (const u of
|
|
2731
|
+
out.push(header);
|
|
2732
|
+
for (const u of items.slice(0, RESOLUTION_REPORT_CONFIG_LIMIT)) {
|
|
2615
2733
|
const fl = firstLine(u.detail);
|
|
2616
|
-
out.push(` - ${u
|
|
2734
|
+
out.push(` - ${label(u)}${fl ? ` [${fl}]` : ''}`);
|
|
2617
2735
|
}
|
|
2618
|
-
if (
|
|
2619
|
-
out.push(` … and ${
|
|
2736
|
+
if (items.length > RESOLUTION_REPORT_CONFIG_LIMIT) {
|
|
2737
|
+
out.push(` … and ${items.length - RESOLUTION_REPORT_CONFIG_LIMIT} more`);
|
|
2620
2738
|
}
|
|
2739
|
+
};
|
|
2740
|
+
pushUnscannable(blockingUnscannableConfigs, opts.ignoreUnresolved ? `Ignored ${blockingUnscannableConfigs.length} configuration(s) that could not be scanned:` : `Could not scan ${blockingUnscannableConfigs.length} configuration(s) (reason from ${name}):`, u => `${u.config}${inProjects(u.project)}`);
|
|
2741
|
+
pushUnscannable(blockingUnscannableProjects, opts.ignoreUnresolved ? `Ignored ${blockingUnscannableProjects.length} project(s) whose dependencies could not be resolved:` : `Could not resolve the dependencies of ${blockingUnscannableProjects.length} project(s) (reason from ${name}):`, u => `\`${u.project}\``);
|
|
2742
|
+
if (succeeded.length || blockingFailed.length) {
|
|
2743
|
+
out.push('');
|
|
2621
2744
|
}
|
|
2622
|
-
out.push('');
|
|
2623
2745
|
if (succeeded.length) {
|
|
2624
2746
|
out.push(`Resolution succeeded in: ${fmtList(succeeded, RESOLUTION_REPORT_CONFIG_LIMIT)}`);
|
|
2625
2747
|
}
|
|
@@ -2628,14 +2750,12 @@ function renderResolutionReport(failures, scannedConfigs, dialect, opts = {}) {
|
|
|
2628
2750
|
}
|
|
2629
2751
|
if (willFail) {
|
|
2630
2752
|
out.push('');
|
|
2631
|
-
out.push(
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
out.push(` --exclude-configs '${blockingFailed.join(',')}'`);
|
|
2635
|
-
}
|
|
2753
|
+
out.push(...proceedHint(hints, tool, hasBlockingConfiglessFailure ? [] : blockingFailed), ...installErrorsHint(hints, false));
|
|
2754
|
+
} else if (hints.continueOnInstallErrorsFlag) {
|
|
2755
|
+
out.push('', ...installErrorsHint(hints, true));
|
|
2636
2756
|
}
|
|
2637
2757
|
out.push('');
|
|
2638
|
-
out.push(`Re-run with --verbose for ${name}'s full messages.`);
|
|
2758
|
+
out.push(hints.flags.has('verbose') ? `Re-run with --verbose for ${name}'s full messages.` : `For ${name}'s full messages, ${verboseHint(hints, tool)}.`);
|
|
2639
2759
|
}
|
|
2640
2760
|
const notices = [];
|
|
2641
2761
|
for (const {
|
|
@@ -2648,11 +2768,11 @@ function renderResolutionReport(failures, scannedConfigs, dialect, opts = {}) {
|
|
|
2648
2768
|
const configCount = new Set(infos.flatMap(i => [...i.configs])).size;
|
|
2649
2769
|
notices.push(spec.notice(name, infos.length, configCount));
|
|
2650
2770
|
}
|
|
2651
|
-
// A config-level throw whose cause classifies as variant ambiguity is surfaced, not failed
|
|
2652
|
-
//
|
|
2771
|
+
// A config-level throw whose cause classifies as variant ambiguity is surfaced, not failed: many
|
|
2772
|
+
// configurations are marked resolvable yet too underspecified to resolve.
|
|
2653
2773
|
if (nonBlockingUnscannable.length) {
|
|
2654
2774
|
const n = new Set(nonBlockingUnscannable.map(u => u.config)).size;
|
|
2655
|
-
notices.push(`Could not scan ${n} configuration(s) — re-run with --verbose for ${name}'s messages.`);
|
|
2775
|
+
notices.push(hints.flags.has('verbose') ? `Could not scan ${n} configuration(s) — re-run with --verbose for ${name}'s messages.` : `Could not scan ${n} configuration(s); for ${name}'s messages, ${verboseHint(hints, tool)}.`);
|
|
2656
2776
|
}
|
|
2657
2777
|
const detailLines = [`${name}'s full message for each unresolved dependency:`];
|
|
2658
2778
|
for (const info of allInfos) {
|
|
@@ -2664,10 +2784,10 @@ function renderResolutionReport(failures, scannedConfigs, dialect, opts = {}) {
|
|
|
2664
2784
|
}
|
|
2665
2785
|
if (unscannable.length) {
|
|
2666
2786
|
detailLines.push('');
|
|
2667
|
-
detailLines.push(`${name}
|
|
2787
|
+
detailLines.push(`${name} resolutions that could not be scanned:`);
|
|
2668
2788
|
for (const u of unscannable) {
|
|
2669
2789
|
detailLines.push('');
|
|
2670
|
-
detailLines.push(` ${u.config}:`);
|
|
2790
|
+
detailLines.push(` ${u.config || 'all dependencies'}${inProjects(u.project)}:`);
|
|
2671
2791
|
for (const line of (u.detail || '(no message)').split('\n')) {
|
|
2672
2792
|
detailLines.push(` ${line}`);
|
|
2673
2793
|
}
|
|
@@ -2691,7 +2811,10 @@ function dialectFor(tool) {
|
|
|
2691
2811
|
}
|
|
2692
2812
|
}
|
|
2693
2813
|
function renderResolutionErrorReport(failures, scannedConfigs = [], tool = 'gradle', opts = {}) {
|
|
2694
|
-
return renderResolutionReport(failures, scannedConfigs,
|
|
2814
|
+
return renderResolutionReport(failures, scannedConfigs, tool, opts);
|
|
2815
|
+
}
|
|
2816
|
+
function verboseHint(hints, tool) {
|
|
2817
|
+
return hints.flags.has('verbose') ? 'pass --verbose' : `set defaults.manifest.${tool}.verbose to true in ${hints.socketJson}`;
|
|
2695
2818
|
}
|
|
2696
2819
|
|
|
2697
2820
|
const PURL_TYPE_MAVEN = 'maven';
|
|
@@ -2726,7 +2849,7 @@ function assembleFacts(parsed, opts = {}) {
|
|
|
2726
2849
|
return {
|
|
2727
2850
|
facts,
|
|
2728
2851
|
report: buildReport(parsed),
|
|
2729
|
-
artifactPaths: buildArtifactPaths(finalNodes, [...parsed.projects.values()], graphs, fileExists)
|
|
2852
|
+
artifactPaths: buildArtifactPaths(finalNodes, [...parsed.projects.values()], [...parsed.excludedProjects.values()], graphs, fileExists)
|
|
2730
2853
|
};
|
|
2731
2854
|
}
|
|
2732
2855
|
|
|
@@ -2753,11 +2876,13 @@ function buildPerRoot(parsed) {
|
|
|
2753
2876
|
children: [],
|
|
2754
2877
|
direct: false,
|
|
2755
2878
|
project: n.project,
|
|
2756
|
-
targets: []
|
|
2879
|
+
targets: [],
|
|
2880
|
+
unfetched: false
|
|
2757
2881
|
};
|
|
2758
2882
|
nodes.set(id, node);
|
|
2759
2883
|
}
|
|
2760
2884
|
node.direct ||= n.direct;
|
|
2885
|
+
node.unfetched ||= n.unfetched;
|
|
2761
2886
|
node.targets.push(...n.targets);
|
|
2762
2887
|
}
|
|
2763
2888
|
for (const [p, c] of r.edges) {
|
|
@@ -2797,7 +2922,7 @@ function mergeById(perRoot) {
|
|
|
2797
2922
|
prod: false,
|
|
2798
2923
|
direct: false,
|
|
2799
2924
|
project: node.project,
|
|
2800
|
-
|
|
2925
|
+
resolutions: []
|
|
2801
2926
|
};
|
|
2802
2927
|
finalNodes.set(coordId, fn);
|
|
2803
2928
|
}
|
|
@@ -2807,8 +2932,11 @@ function mergeById(perRoot) {
|
|
|
2807
2932
|
if (node.direct) {
|
|
2808
2933
|
fn.direct = true;
|
|
2809
2934
|
}
|
|
2810
|
-
|
|
2811
|
-
fn.
|
|
2935
|
+
if (node.unfetched || node.targets.length) {
|
|
2936
|
+
fn.resolutions.push({
|
|
2937
|
+
targets: node.targets,
|
|
2938
|
+
unfetched: node.unfetched
|
|
2939
|
+
});
|
|
2812
2940
|
}
|
|
2813
2941
|
}
|
|
2814
2942
|
}
|
|
@@ -3029,19 +3157,34 @@ function byProject(projects, graphs, ids) {
|
|
|
3029
3157
|
return [p.projectKey, g ? [...ids(g)].sort() : []];
|
|
3030
3158
|
}));
|
|
3031
3159
|
}
|
|
3032
|
-
function buildArtifactPaths(finalNodes, projects, graphs, fileExists) {
|
|
3160
|
+
function buildArtifactPaths(finalNodes, projects, excludedProjects, graphs, fileExists) {
|
|
3033
3161
|
const pathsById = new Map();
|
|
3162
|
+
// Every root that fetched an artifact lists its file, so check each path once.
|
|
3163
|
+
const existing = new Map();
|
|
3164
|
+
const exists = p => {
|
|
3165
|
+
let e = existing.get(p);
|
|
3166
|
+
if (e === undefined) {
|
|
3167
|
+
e = fileExists(p);
|
|
3168
|
+
existing.set(p, e);
|
|
3169
|
+
}
|
|
3170
|
+
return e;
|
|
3171
|
+
};
|
|
3034
3172
|
for (const [id, fn] of finalNodes) {
|
|
3035
3173
|
if (!fn.project) {
|
|
3036
|
-
|
|
3174
|
+
// Only a root that fetched every file of the artifact, all still on
|
|
3175
|
+
// disk, resolved it: a partial list would claim it resolved whole.
|
|
3176
|
+
const complete = fn.resolutions.filter(r => !r.unfetched && r.targets.every(exists));
|
|
3177
|
+
pathsById.set(id, fn.resolutions.length && !complete.length ? {
|
|
3178
|
+
sources: []
|
|
3179
|
+
} : {
|
|
3037
3180
|
sources: [],
|
|
3038
|
-
targets: [...
|
|
3181
|
+
targets: [...new Set(complete.flatMap(r => r.targets))].sort()
|
|
3039
3182
|
});
|
|
3040
3183
|
}
|
|
3041
3184
|
}
|
|
3042
3185
|
// A project's own component shares its id, so this also covers dependency
|
|
3043
3186
|
// edges onto a sibling project.
|
|
3044
|
-
for (const p of projects) {
|
|
3187
|
+
for (const p of [...projects, ...excludedProjects]) {
|
|
3045
3188
|
pathsById.set(p.projectKey, {
|
|
3046
3189
|
sources: [...new Set(p.sources)].filter(fileExists).sort(),
|
|
3047
3190
|
targets: [...new Set(p.targets)].filter(fileExists).sort()
|
|
@@ -3056,7 +3199,7 @@ function buildArtifactPaths(finalNodes, projects, graphs, fileExists) {
|
|
|
3056
3199
|
function buildReport(parsed) {
|
|
3057
3200
|
const seen = new Set();
|
|
3058
3201
|
const failures = parsed.failures.filter(f => {
|
|
3059
|
-
const key = `${f.coord}|${f.detail}|${f.config}`;
|
|
3202
|
+
const key = `${f.coord}|${f.detail}|${f.config}|${f.project}`;
|
|
3060
3203
|
if (seen.has(key)) {
|
|
3061
3204
|
return false;
|
|
3062
3205
|
}
|
|
@@ -3065,15 +3208,24 @@ function buildReport(parsed) {
|
|
|
3065
3208
|
});
|
|
3066
3209
|
const seenUnscannable = new Set();
|
|
3067
3210
|
const unscannable = parsed.unscannable.filter(u => {
|
|
3068
|
-
const key = `${u.config}|${u.detail}`;
|
|
3211
|
+
const key = `${u.config}|${u.detail}|${u.project}`;
|
|
3069
3212
|
if (seenUnscannable.has(key)) {
|
|
3070
3213
|
return false;
|
|
3071
3214
|
}
|
|
3072
3215
|
seenUnscannable.add(key);
|
|
3073
3216
|
return true;
|
|
3074
3217
|
});
|
|
3218
|
+
const resolvedModules = new Set();
|
|
3219
|
+
if (failures.length) {
|
|
3220
|
+
for (const r of parsed.roots.values()) {
|
|
3221
|
+
for (const n of r.nodes.values()) {
|
|
3222
|
+
resolvedModules.add(moduleKey(r.projectKey, `${n.coord.group}:${n.coord.name}`));
|
|
3223
|
+
}
|
|
3224
|
+
}
|
|
3225
|
+
}
|
|
3075
3226
|
return {
|
|
3076
3227
|
failures,
|
|
3228
|
+
resolvedModules,
|
|
3077
3229
|
scannedConfigs: parsed.scannedConfigs,
|
|
3078
3230
|
unscannable
|
|
3079
3231
|
};
|
|
@@ -3227,13 +3379,15 @@ function serializeExcludePathPatterns(paths) {
|
|
|
3227
3379
|
// projectSrc projectKey path (--with-files only)
|
|
3228
3380
|
// projectTgt projectKey path (--with-files only)
|
|
3229
3381
|
// projectBuild projectKey path (build-root-relative; may be absent on disk)
|
|
3382
|
+
// excludedProject projectKey (--with-files only; paths without a project)
|
|
3230
3383
|
// root rootId projectKey config prod(0|1)
|
|
3231
3384
|
// node rootId coordId group name version ext classifier direct(0|1) project
|
|
3232
3385
|
// edge rootId parentCoordId childCoordId
|
|
3233
3386
|
// file rootId coordId path (--with-files only)
|
|
3387
|
+
// unfetched rootId coordId (--with-files only; requested artifact not fetched)
|
|
3234
3388
|
// scanned config
|
|
3235
|
-
// failure coord detail config
|
|
3236
|
-
// unscannable config detail
|
|
3389
|
+
// failure coord detail config projectKey (config empty: not placeable in one)
|
|
3390
|
+
// unscannable config detail projectKey (config empty: whole project)
|
|
3237
3391
|
//
|
|
3238
3392
|
// `projectKey` is the tool's unique project identity (Maven: GAV; Gradle:
|
|
3239
3393
|
// project path; sbt: project id), emitted as the project id; a `node` whose
|
|
@@ -3269,12 +3423,14 @@ function parseRecords(text) {
|
|
|
3269
3423
|
buildRoot: '',
|
|
3270
3424
|
entry: '',
|
|
3271
3425
|
projects: new Map(),
|
|
3426
|
+
excludedProjects: new Map(),
|
|
3272
3427
|
roots: new Map(),
|
|
3273
3428
|
scannedConfigs: [],
|
|
3274
3429
|
failures: [],
|
|
3275
3430
|
unscannable: []
|
|
3276
3431
|
};
|
|
3277
3432
|
const scanned = new Set();
|
|
3433
|
+
const excluded = new Set();
|
|
3278
3434
|
const root = id => {
|
|
3279
3435
|
let r = result.roots.get(id);
|
|
3280
3436
|
if (!r) {
|
|
@@ -3343,6 +3499,9 @@ function parseRecords(text) {
|
|
|
3343
3499
|
project(f[1] ?? '').targets.push(f[2]);
|
|
3344
3500
|
}
|
|
3345
3501
|
break;
|
|
3502
|
+
case 'excludedProject':
|
|
3503
|
+
excluded.add(f[1] ?? '');
|
|
3504
|
+
break;
|
|
3346
3505
|
case 'projectBuild':
|
|
3347
3506
|
if (f[2]) {
|
|
3348
3507
|
project(f[1] ?? '').buildFiles.push(f[2]);
|
|
@@ -3371,7 +3530,8 @@ function parseRecords(text) {
|
|
|
3371
3530
|
},
|
|
3372
3531
|
direct: bool(f[8]),
|
|
3373
3532
|
project: f[9] ?? '',
|
|
3374
|
-
targets: []
|
|
3533
|
+
targets: [],
|
|
3534
|
+
unfetched: false
|
|
3375
3535
|
});
|
|
3376
3536
|
break;
|
|
3377
3537
|
}
|
|
@@ -3384,6 +3544,14 @@ function parseRecords(text) {
|
|
|
3384
3544
|
}
|
|
3385
3545
|
break;
|
|
3386
3546
|
}
|
|
3547
|
+
case 'unfetched':
|
|
3548
|
+
{
|
|
3549
|
+
const node = root(f[1] ?? '').nodes.get(f[2] ?? '');
|
|
3550
|
+
if (node) {
|
|
3551
|
+
node.unfetched = true;
|
|
3552
|
+
}
|
|
3553
|
+
break;
|
|
3554
|
+
}
|
|
3387
3555
|
case 'file':
|
|
3388
3556
|
{
|
|
3389
3557
|
const node = root(f[1] ?? '').nodes.get(f[2] ?? '');
|
|
@@ -3402,20 +3570,29 @@ function parseRecords(text) {
|
|
|
3402
3570
|
result.failures.push({
|
|
3403
3571
|
coord: f[1],
|
|
3404
3572
|
detail: f[2] ?? '',
|
|
3405
|
-
config: f[3] ?? ''
|
|
3573
|
+
config: f[3] ?? '',
|
|
3574
|
+
project: f[4] ?? ''
|
|
3406
3575
|
});
|
|
3407
3576
|
}
|
|
3408
3577
|
break;
|
|
3409
3578
|
case 'unscannable':
|
|
3410
|
-
if (f[1]) {
|
|
3579
|
+
if (f[1] || f[3]) {
|
|
3411
3580
|
result.unscannable.push({
|
|
3412
|
-
config: f[1],
|
|
3413
|
-
detail: f[2] ?? ''
|
|
3581
|
+
config: f[1] ?? '',
|
|
3582
|
+
detail: f[2] ?? '',
|
|
3583
|
+
project: f[3] ?? ''
|
|
3414
3584
|
});
|
|
3415
3585
|
}
|
|
3416
3586
|
break;
|
|
3417
3587
|
}
|
|
3418
3588
|
}
|
|
3589
|
+
for (const key of excluded) {
|
|
3590
|
+
const p = result.projects.get(key);
|
|
3591
|
+
if (p) {
|
|
3592
|
+
result.projects.delete(key);
|
|
3593
|
+
result.excludedProjects.set(key, p);
|
|
3594
|
+
}
|
|
3595
|
+
}
|
|
3419
3596
|
result.scannedConfigs = [...scanned].sort();
|
|
3420
3597
|
return result;
|
|
3421
3598
|
}
|
|
@@ -3431,6 +3608,10 @@ function manifestScriptsPath(...parts) {
|
|
|
3431
3608
|
return path.join(constants.default.distPath, 'manifest-scripts', ...parts);
|
|
3432
3609
|
}
|
|
3433
3610
|
|
|
3611
|
+
// Streamed build output is a diagnostic: it goes to stderr so the command's own
|
|
3612
|
+
// stdout (e.g. --json) stays machine-readable.
|
|
3613
|
+
const STREAM_TO_STDERR = ['inherit', 2, 'inherit'];
|
|
3614
|
+
|
|
3434
3615
|
// Don't throw on a non-zero exit: the script emits failure records, so a usable
|
|
3435
3616
|
// records file still exists. A non-exit spawn error (e.g. missing executable)
|
|
3436
3617
|
// propagates.
|
|
@@ -3438,7 +3619,7 @@ async function runNeverThrow(bin, args, opts) {
|
|
|
3438
3619
|
try {
|
|
3439
3620
|
const result = await spawn.spawn(bin, args, {
|
|
3440
3621
|
cwd: opts.projectDir,
|
|
3441
|
-
stdio: opts.stdio
|
|
3622
|
+
stdio: opts.stdio === 'pipe' ? 'pipe' : [...STREAM_TO_STDERR],
|
|
3442
3623
|
...(opts.env ? {
|
|
3443
3624
|
env: opts.env
|
|
3444
3625
|
} : {}),
|
|
@@ -3563,7 +3744,10 @@ async function invokeGradle(opts, initScriptFilename, task, tmpDirPrefix) {
|
|
|
3563
3744
|
const bin = resolveBuildToolBin('gradle', opts.projectDir, opts.bin);
|
|
3564
3745
|
// Disable the configuration cache: the init script's legacy
|
|
3565
3746
|
// resolvedConfiguration API and shared accumulator aren't cache-safe.
|
|
3566
|
-
const args = ['--init-script', initScript, '-Dorg.gradle.configuration-cache=false',
|
|
3747
|
+
const args = ['--init-script', initScript, '-Dorg.gradle.configuration-cache=false',
|
|
3748
|
+
// Names each artifact's own verification problem, so a checksum missing
|
|
3749
|
+
// from the metadata can be told apart from a mismatch.
|
|
3750
|
+
'-Porg.gradle.dependency.verification.console=verbose', `-Psocket.recordsFile=${recordsFile}`, ...commonProps(opts, '-P'), ...(opts.toolOpts ?? []), task, '--no-daemon', '--console=plain'];
|
|
3567
3751
|
const out = await runNeverThrow(bin, args, opts);
|
|
3568
3752
|
return await assembleFromRecords('gradle', out, recordsFile);
|
|
3569
3753
|
});
|
|
@@ -3577,6 +3761,11 @@ async function enumerateGradleWorkspaces(opts) {
|
|
|
3577
3761
|
async function invokeSbtIn(globalBase, opts, pluginSourceFilename, pluginDestFilename, task) {
|
|
3578
3762
|
await writeSbtPlugin(globalBase, pluginSourceFilename, pluginDestFilename);
|
|
3579
3763
|
const recordsFile = path.join(globalBase, 'records.tsv');
|
|
3764
|
+
// The global base is shared across sbt roots: a build that dies before
|
|
3765
|
+
// writing must not be read as the previous root's records.
|
|
3766
|
+
await u.promises.rm(recordsFile, {
|
|
3767
|
+
force: true
|
|
3768
|
+
});
|
|
3580
3769
|
const bin = resolveBuildToolBin('sbt', opts.projectDir, opts.bin);
|
|
3581
3770
|
// Fresh per-run global base (not ~/.sbt): sbt executes everything under
|
|
3582
3771
|
// plugins/, so a shared path is a code-injection surface. BSP off for this
|
|
@@ -3654,6 +3843,7 @@ async function runManifestFacts({
|
|
|
3654
3843
|
ecosystem,
|
|
3655
3844
|
excludeConfigs,
|
|
3656
3845
|
excludePaths,
|
|
3846
|
+
hints = FLAG_HINTS,
|
|
3657
3847
|
ignoreUnresolved,
|
|
3658
3848
|
includeConfigs,
|
|
3659
3849
|
javaHome,
|
|
@@ -3705,7 +3895,7 @@ async function runManifestFacts({
|
|
|
3705
3895
|
logger.logger.info(`(Running ${ecosystem} with output streaming; this can take a while.)`);
|
|
3706
3896
|
result = await runManifestScript(ecosystem, scriptOpts);
|
|
3707
3897
|
} else {
|
|
3708
|
-
logger.logger.info(`(No live output;
|
|
3898
|
+
logger.logger.info(`(No live output; to stream the ${ecosystem} build output, ${verboseHint(hints, ecosystem)}.)`);
|
|
3709
3899
|
spinner.start(`Resolving ${ecosystem} dependencies ...`);
|
|
3710
3900
|
result = await runManifestScript(ecosystem, scriptOpts);
|
|
3711
3901
|
if (result.code === 0) {
|
|
@@ -3721,7 +3911,7 @@ async function runManifestFacts({
|
|
|
3721
3911
|
spinner.failAndStop(`Failed to run ${ecosystem}.`);
|
|
3722
3912
|
}
|
|
3723
3913
|
process.exitCode = 1;
|
|
3724
|
-
logger.logger.fail(`Could not run the ${ecosystem} build tool
|
|
3914
|
+
logger.logger.fail(`Could not run the ${ecosystem} build tool: ${utils.getErrorMessageOr(e, String(e))}`);
|
|
3725
3915
|
return null;
|
|
3726
3916
|
}
|
|
3727
3917
|
const {
|
|
@@ -3735,7 +3925,9 @@ async function runManifestFacts({
|
|
|
3735
3925
|
stdout
|
|
3736
3926
|
} = result;
|
|
3737
3927
|
const rendered = renderResolutionErrorReport(report.failures, report.scannedConfigs, ecosystem, {
|
|
3928
|
+
hints,
|
|
3738
3929
|
ignoreUnresolved,
|
|
3930
|
+
resolvedModules: report.resolvedModules,
|
|
3739
3931
|
unscannable: report.unscannable
|
|
3740
3932
|
});
|
|
3741
3933
|
if (rendered.hasBlockingFailures) {
|
|
@@ -3821,16 +4013,22 @@ async function runManifestFacts({
|
|
|
3821
4013
|
};
|
|
3822
4014
|
}
|
|
3823
4015
|
|
|
4016
|
+
// Relative to the user's shell when inside it, else absolute.
|
|
4017
|
+
function displayPath(file) {
|
|
4018
|
+
const rel = path.relative(process.cwd(), file);
|
|
4019
|
+
return rel.startsWith('..') || path.isAbsolute(rel) ? file : rel;
|
|
4020
|
+
}
|
|
4021
|
+
|
|
3824
4022
|
// A cascaded (not just root-level) disabled additionally skips this one build
|
|
3825
4023
|
// root here, on top of its existing root-only ecosystem-wide meaning for
|
|
3826
4024
|
// auto/gradle/etc. facts:false has no pom-mode equivalent here (facts-only),
|
|
3827
4025
|
// so it skips too.
|
|
3828
|
-
function getSkipReason(disabled, facts) {
|
|
4026
|
+
function getSkipReason(ecosystem, disabled, facts) {
|
|
3829
4027
|
if (disabled) {
|
|
3830
|
-
return
|
|
4028
|
+
return `defaults.manifest.${ecosystem}.disabled is true`;
|
|
3831
4029
|
}
|
|
3832
4030
|
if (facts === false) {
|
|
3833
|
-
return
|
|
4031
|
+
return `defaults.manifest.${ecosystem}.facts is false (pom mode)`;
|
|
3834
4032
|
}
|
|
3835
4033
|
return undefined;
|
|
3836
4034
|
}
|
|
@@ -3849,6 +4047,8 @@ function nearestDisabledRoot(dir, disabledRoots) {
|
|
|
3849
4047
|
async function runEcosystemCandidates({
|
|
3850
4048
|
candidatesByTool,
|
|
3851
4049
|
excludePaths,
|
|
4050
|
+
ignoreUnresolved: ignoreUnresolvedOverride,
|
|
4051
|
+
optionFlags,
|
|
3852
4052
|
realCwd,
|
|
3853
4053
|
rootSockJson,
|
|
3854
4054
|
sbtTmpDir,
|
|
@@ -3879,7 +4079,8 @@ async function runEcosystemCandidates({
|
|
|
3879
4079
|
ignoreUnresolved,
|
|
3880
4080
|
includeConfigs,
|
|
3881
4081
|
javaHome,
|
|
3882
|
-
skipReason
|
|
4082
|
+
skipReason,
|
|
4083
|
+
verbose: configVerbose
|
|
3883
4084
|
} = resolveEcosystemConfig(ecosystem, dir, sockJson);
|
|
3884
4085
|
if (skipReason) {
|
|
3885
4086
|
// Only warn for a genuinely new disabled root, not one already
|
|
@@ -3914,12 +4115,18 @@ async function runEcosystemCandidates({
|
|
|
3914
4115
|
ecosystem,
|
|
3915
4116
|
excludeConfigs,
|
|
3916
4117
|
excludePaths: excludePathsForRoot,
|
|
3917
|
-
|
|
4118
|
+
hints: {
|
|
4119
|
+
continueOnInstallErrorsFlag: withFiles ? REACH_CONTINUE_ON_INSTALL_ERRORS_FLAG : undefined,
|
|
4120
|
+
flags: optionFlags,
|
|
4121
|
+
setupCommand: DSI_SETUP_COMMAND,
|
|
4122
|
+
socketJson: displayPath(path.join(dir, constants.SOCKET_JSON))
|
|
4123
|
+
},
|
|
4124
|
+
ignoreUnresolved: ignoreUnresolvedOverride ?? ignoreUnresolved,
|
|
3918
4125
|
includeConfigs,
|
|
3919
4126
|
javaHome,
|
|
3920
4127
|
sidecarAcc,
|
|
3921
4128
|
tmpDir: ecosystem === 'sbt' ? sbtTmpDir : undefined,
|
|
3922
|
-
verbose,
|
|
4129
|
+
verbose: verbose || configVerbose,
|
|
3923
4130
|
withFiles
|
|
3924
4131
|
});
|
|
3925
4132
|
if (result === null) {
|
|
@@ -3928,8 +4135,11 @@ async function runEcosystemCandidates({
|
|
|
3928
4135
|
ecosystem,
|
|
3929
4136
|
status: 'failed'
|
|
3930
4137
|
});
|
|
3931
|
-
|
|
3932
|
-
|
|
4138
|
+
const abortedDirs = dirs.slice(dirIndex + 1);
|
|
4139
|
+
if (abortedDirs.length) {
|
|
4140
|
+
logger.logger.warn(`Aborting ${ecosystem} discovery: ${dir} failed to generate Socket facts, and without them the remaining build roots cannot be safely classified as covered or independent.`);
|
|
4141
|
+
}
|
|
4142
|
+
for (const abortedDir of abortedDirs) {
|
|
3933
4143
|
outcomes.push({
|
|
3934
4144
|
dir: abortedDir,
|
|
3935
4145
|
ecosystem,
|
|
@@ -3966,6 +4176,8 @@ async function runEcosystemCandidates({
|
|
|
3966
4176
|
async function generateRecursiveManifests({
|
|
3967
4177
|
cwd,
|
|
3968
4178
|
excludePaths,
|
|
4179
|
+
ignoreUnresolved,
|
|
4180
|
+
optionFlags = new Set(),
|
|
3969
4181
|
sbtTmpDir,
|
|
3970
4182
|
sidecarAcc,
|
|
3971
4183
|
verbose,
|
|
@@ -3987,6 +4199,8 @@ async function generateRecursiveManifests({
|
|
|
3987
4199
|
const runAll = resolvedSbtTmpDir => runEcosystemCandidates({
|
|
3988
4200
|
candidatesByTool,
|
|
3989
4201
|
excludePaths,
|
|
4202
|
+
ignoreUnresolved,
|
|
4203
|
+
optionFlags,
|
|
3990
4204
|
realCwd,
|
|
3991
4205
|
rootSockJson,
|
|
3992
4206
|
sbtTmpDir: resolvedSbtTmpDir,
|
|
@@ -3998,10 +4212,10 @@ async function generateRecursiveManifests({
|
|
|
3998
4212
|
// A shared global base across every sbt root in this run lets sbt's own
|
|
3999
4213
|
// Scala-toolchain cache under <base>/boot survive between invocations
|
|
4000
4214
|
// instead of being reprovisioned per root (the plugin file is rewritten and
|
|
4001
|
-
// records.tsv is
|
|
4002
|
-
//
|
|
4003
|
-
//
|
|
4004
|
-
//
|
|
4215
|
+
// records.tsv is removed before every invocation, so reuse is safe). A
|
|
4216
|
+
// caller-supplied sbtTmpDir is reused as-is (the caller owns its lifetime);
|
|
4217
|
+
// otherwise one is allocated and cleaned up here, but only when there's an
|
|
4218
|
+
// sbt root to benefit from sharing it at all.
|
|
4005
4219
|
const outcomes = sbtTmpDir ? await runAll(sbtTmpDir) : candidatesByTool.get('sbt')?.length ? await utils.withTmpDir('socket-sbt-facts-shared-', runAll) : await runAll(undefined);
|
|
4006
4220
|
if (verbose) {
|
|
4007
4221
|
logger.logger.info(`Discovered ${outcomes.length} build-tool candidate(s).`);
|
|
@@ -4023,7 +4237,8 @@ function resolveEcosystemConfig(ecosystem, dir, sockJson) {
|
|
|
4023
4237
|
ignoreUnresolved: Boolean(config?.ignoreUnresolved),
|
|
4024
4238
|
includeConfigs: config?.includeConfigs ?? '',
|
|
4025
4239
|
javaHome: config?.javaHome ?? undefined,
|
|
4026
|
-
skipReason: getSkipReason(config?.disabled, config?.facts)
|
|
4240
|
+
skipReason: getSkipReason(ecosystem, config?.disabled, config?.facts),
|
|
4241
|
+
verbose: Boolean(config?.verbose)
|
|
4027
4242
|
};
|
|
4028
4243
|
}
|
|
4029
4244
|
if (ecosystem === 'gradle') {
|
|
@@ -4036,7 +4251,8 @@ function resolveEcosystemConfig(ecosystem, dir, sockJson) {
|
|
|
4036
4251
|
ignoreUnresolved: Boolean(config?.ignoreUnresolved),
|
|
4037
4252
|
includeConfigs: config?.includeConfigs ?? '',
|
|
4038
4253
|
javaHome: config?.javaHome ?? undefined,
|
|
4039
|
-
skipReason: getSkipReason(config?.disabled, config?.facts)
|
|
4254
|
+
skipReason: getSkipReason(ecosystem, config?.disabled, config?.facts),
|
|
4255
|
+
verbose: Boolean(config?.verbose)
|
|
4040
4256
|
};
|
|
4041
4257
|
}
|
|
4042
4258
|
const config = sockJson.defaults?.manifest?.maven;
|
|
@@ -4048,7 +4264,8 @@ function resolveEcosystemConfig(ecosystem, dir, sockJson) {
|
|
|
4048
4264
|
ignoreUnresolved: Boolean(config?.ignoreUnresolved),
|
|
4049
4265
|
includeConfigs: config?.includeConfigs ?? '',
|
|
4050
4266
|
javaHome: config?.javaHome ?? undefined,
|
|
4051
|
-
skipReason: getSkipReason(config?.disabled)
|
|
4267
|
+
skipReason: getSkipReason(ecosystem, config?.disabled),
|
|
4268
|
+
verbose: Boolean(config?.verbose)
|
|
4052
4269
|
};
|
|
4053
4270
|
}
|
|
4054
4271
|
|
|
@@ -6463,6 +6680,7 @@ async function convertGradleToFacts({
|
|
|
6463
6680
|
excludeConfigs,
|
|
6464
6681
|
excludePaths,
|
|
6465
6682
|
gradleOpts,
|
|
6683
|
+
hints,
|
|
6466
6684
|
ignoreUnresolved,
|
|
6467
6685
|
includeConfigs,
|
|
6468
6686
|
javaHome,
|
|
@@ -6470,13 +6688,14 @@ async function convertGradleToFacts({
|
|
|
6470
6688
|
verbose,
|
|
6471
6689
|
withFiles
|
|
6472
6690
|
}) {
|
|
6473
|
-
await runManifestFacts({
|
|
6691
|
+
return await runManifestFacts({
|
|
6474
6692
|
bin,
|
|
6475
6693
|
buildOpts: gradleOpts,
|
|
6476
6694
|
cwd,
|
|
6477
6695
|
ecosystem: 'gradle',
|
|
6478
6696
|
excludeConfigs,
|
|
6479
6697
|
excludePaths,
|
|
6698
|
+
hints,
|
|
6480
6699
|
ignoreUnresolved,
|
|
6481
6700
|
includeConfigs,
|
|
6482
6701
|
javaHome,
|
|
@@ -6493,6 +6712,7 @@ async function convertMavenToFacts({
|
|
|
6493
6712
|
cwd,
|
|
6494
6713
|
excludeConfigs,
|
|
6495
6714
|
excludePaths,
|
|
6715
|
+
hints,
|
|
6496
6716
|
ignoreUnresolved,
|
|
6497
6717
|
includeConfigs,
|
|
6498
6718
|
javaHome,
|
|
@@ -6501,13 +6721,14 @@ async function convertMavenToFacts({
|
|
|
6501
6721
|
verbose,
|
|
6502
6722
|
withFiles
|
|
6503
6723
|
}) {
|
|
6504
|
-
await runManifestFacts({
|
|
6724
|
+
return await runManifestFacts({
|
|
6505
6725
|
bin,
|
|
6506
6726
|
buildOpts: mavenOpts,
|
|
6507
6727
|
cwd,
|
|
6508
6728
|
ecosystem: 'maven',
|
|
6509
6729
|
excludeConfigs,
|
|
6510
6730
|
excludePaths,
|
|
6731
|
+
hints,
|
|
6511
6732
|
ignoreUnresolved,
|
|
6512
6733
|
includeConfigs,
|
|
6513
6734
|
javaHome,
|
|
@@ -6525,6 +6746,7 @@ async function convertSbtToFacts({
|
|
|
6525
6746
|
cwd,
|
|
6526
6747
|
excludeConfigs,
|
|
6527
6748
|
excludePaths,
|
|
6749
|
+
hints,
|
|
6528
6750
|
ignoreUnresolved,
|
|
6529
6751
|
includeConfigs,
|
|
6530
6752
|
javaHome,
|
|
@@ -6534,13 +6756,14 @@ async function convertSbtToFacts({
|
|
|
6534
6756
|
verbose,
|
|
6535
6757
|
withFiles
|
|
6536
6758
|
}) {
|
|
6537
|
-
await runManifestFacts({
|
|
6759
|
+
return await runManifestFacts({
|
|
6538
6760
|
bin,
|
|
6539
6761
|
buildOpts: sbtOpts,
|
|
6540
6762
|
cwd,
|
|
6541
6763
|
ecosystem: 'sbt',
|
|
6542
6764
|
excludeConfigs,
|
|
6543
6765
|
excludePaths,
|
|
6766
|
+
hints,
|
|
6544
6767
|
ignoreUnresolved,
|
|
6545
6768
|
includeConfigs,
|
|
6546
6769
|
javaHome,
|
|
@@ -7004,12 +7227,20 @@ async function handleManifestConda({
|
|
|
7004
7227
|
await outputRequirements(data, outputKind, out === '-' ? out : path.resolve(cwd, out));
|
|
7005
7228
|
}
|
|
7006
7229
|
|
|
7007
|
-
//
|
|
7008
|
-
|
|
7009
|
-
|
|
7010
|
-
|
|
7011
|
-
|
|
7012
|
-
|
|
7230
|
+
// Auto-manifest reads every build option from the root socket.json.
|
|
7231
|
+
const AUTO_MANIFEST_HINTS = {
|
|
7232
|
+
flags: new Set(),
|
|
7233
|
+
setupCommand: 'socket manifest setup',
|
|
7234
|
+
socketJson: constants.SOCKET_JSON
|
|
7235
|
+
};
|
|
7236
|
+
// A failed generator aborts --auto-manifest: a partial SBOM silently
|
|
7237
|
+
// under-reports dependencies. Facts generators return null on failure; the
|
|
7238
|
+
// others only change the exit code.
|
|
7239
|
+
function abortFactsRunIfFailed(ecosystem, outcome) {
|
|
7240
|
+
if (outcome === null) {
|
|
7241
|
+
throw new utils.InputError(`Auto-manifest generation failed for the ${ecosystem} project; aborting (see the errors above).`);
|
|
7242
|
+
}
|
|
7243
|
+
}
|
|
7013
7244
|
function abortManifestRunIfFailed(ecosystem, beforeExitCode) {
|
|
7014
7245
|
if (process.exitCode && process.exitCode !== beforeExitCode) {
|
|
7015
7246
|
throw new utils.InputError(`Auto-manifest generation failed for the ${ecosystem} project; aborting (see the errors above).`);
|
|
@@ -7026,6 +7257,10 @@ async function generateAutoManifest({
|
|
|
7026
7257
|
}) {
|
|
7027
7258
|
const sockJson = utils.readOrDefaultSocketJson(cwd);
|
|
7028
7259
|
const generatedFiles = [];
|
|
7260
|
+
const hints = computeArtifactsSidecar ? {
|
|
7261
|
+
...AUTO_MANIFEST_HINTS,
|
|
7262
|
+
continueOnInstallErrorsFlag: REACH_CONTINUE_ON_INSTALL_ERRORS_FLAG
|
|
7263
|
+
} : AUTO_MANIFEST_HINTS;
|
|
7029
7264
|
|
|
7030
7265
|
// Resolved paths across all JVM roots, serialized to one sidecar at the end.
|
|
7031
7266
|
const sidecarAcc = computeArtifactsSidecar ? new Map() : undefined;
|
|
@@ -7047,19 +7282,19 @@ async function generateAutoManifest({
|
|
|
7047
7282
|
// `defaults.manifest.sbt.facts: false` in socket.json.
|
|
7048
7283
|
if (sockJson.defaults?.manifest?.sbt?.facts !== false) {
|
|
7049
7284
|
logger.logger.log('Detected a Scala sbt build, generating Socket facts...');
|
|
7050
|
-
const
|
|
7051
|
-
await convertSbtToFacts({
|
|
7285
|
+
const outcome = await convertSbtToFacts({
|
|
7052
7286
|
...sbtArgs,
|
|
7053
7287
|
excludeConfigs: sockJson.defaults?.manifest?.sbt?.excludeConfigs ?? '',
|
|
7054
7288
|
excludePaths,
|
|
7055
7289
|
ignoreUnresolved: Boolean(sockJson.defaults?.manifest?.sbt?.ignoreUnresolved),
|
|
7056
7290
|
includeConfigs: sockJson.defaults?.manifest?.sbt?.includeConfigs ?? '',
|
|
7057
7291
|
javaHome: sockJson.defaults?.manifest?.sbt?.javaHome ?? undefined,
|
|
7292
|
+
hints,
|
|
7058
7293
|
sidecarAcc,
|
|
7059
7294
|
tmpDir,
|
|
7060
7295
|
withFiles: computeArtifactsSidecar
|
|
7061
7296
|
});
|
|
7062
|
-
|
|
7297
|
+
abortFactsRunIfFailed('sbt', outcome);
|
|
7063
7298
|
} else {
|
|
7064
7299
|
logger.logger.log('Detected a Scala sbt build, generating pom files with sbt...');
|
|
7065
7300
|
const beforeExitCode = process.exitCode;
|
|
@@ -7082,18 +7317,18 @@ async function generateAutoManifest({
|
|
|
7082
7317
|
// `defaults.manifest.gradle.facts: false` in socket.json.
|
|
7083
7318
|
if (sockJson.defaults?.manifest?.gradle?.facts !== false) {
|
|
7084
7319
|
logger.logger.log('Detected a gradle build (Gradle, Kotlin, Scala), generating Socket facts...');
|
|
7085
|
-
const
|
|
7086
|
-
await convertGradleToFacts({
|
|
7320
|
+
const outcome = await convertGradleToFacts({
|
|
7087
7321
|
...gradleArgs,
|
|
7088
7322
|
excludeConfigs: sockJson.defaults?.manifest?.gradle?.excludeConfigs ?? '',
|
|
7089
7323
|
excludePaths,
|
|
7090
7324
|
ignoreUnresolved: Boolean(sockJson.defaults?.manifest?.gradle?.ignoreUnresolved),
|
|
7091
7325
|
includeConfigs: sockJson.defaults?.manifest?.gradle?.includeConfigs ?? '',
|
|
7092
7326
|
javaHome: sockJson.defaults?.manifest?.gradle?.javaHome ?? undefined,
|
|
7327
|
+
hints,
|
|
7093
7328
|
sidecarAcc,
|
|
7094
7329
|
withFiles: computeArtifactsSidecar
|
|
7095
7330
|
});
|
|
7096
|
-
|
|
7331
|
+
abortFactsRunIfFailed('gradle', outcome);
|
|
7097
7332
|
} else {
|
|
7098
7333
|
logger.logger.log('Detected a gradle build (Gradle, Kotlin, Scala), running default gradle generator...');
|
|
7099
7334
|
const beforeExitCode = process.exitCode;
|
|
@@ -7103,8 +7338,7 @@ async function generateAutoManifest({
|
|
|
7103
7338
|
}
|
|
7104
7339
|
if (!sockJson?.defaults?.manifest?.maven?.disabled && detected.maven) {
|
|
7105
7340
|
logger.logger.log('Detected a Maven pom.xml build, generating Socket facts...');
|
|
7106
|
-
const
|
|
7107
|
-
await convertMavenToFacts({
|
|
7341
|
+
const outcome = await convertMavenToFacts({
|
|
7108
7342
|
// Configured bin wins; else prefer ./mvnw, else mvn on PATH.
|
|
7109
7343
|
bin: sockJson.defaults?.manifest?.maven?.bin ?? resolveBuildToolBin('maven', cwd),
|
|
7110
7344
|
cwd,
|
|
@@ -7114,11 +7348,12 @@ async function generateAutoManifest({
|
|
|
7114
7348
|
includeConfigs: sockJson.defaults?.manifest?.maven?.includeConfigs ?? '',
|
|
7115
7349
|
javaHome: sockJson.defaults?.manifest?.maven?.javaHome ?? undefined,
|
|
7116
7350
|
mavenOpts: parseBuildToolOpts(sockJson.defaults?.manifest?.maven?.mavenOpts ?? undefined),
|
|
7351
|
+
hints,
|
|
7117
7352
|
sidecarAcc,
|
|
7118
7353
|
verbose: Boolean(sockJson.defaults?.manifest?.maven?.verbose),
|
|
7119
7354
|
withFiles: computeArtifactsSidecar
|
|
7120
7355
|
});
|
|
7121
|
-
|
|
7356
|
+
abortFactsRunIfFailed('maven', outcome);
|
|
7122
7357
|
}
|
|
7123
7358
|
if (!sockJson?.defaults?.manifest?.conda?.disabled && detected.conda) {
|
|
7124
7359
|
const condaFile = sockJson.defaults?.manifest?.conda?.infile || detected.condaFile || constants.ENVIRONMENT_YML;
|
|
@@ -7414,17 +7649,13 @@ async function createNewScan({
|
|
|
7414
7649
|
logger.logger.success('Reachability analysis completed successfully');
|
|
7415
7650
|
reachabilityReport = reachResult.data?.reachabilityReport;
|
|
7416
7651
|
|
|
7417
|
-
//
|
|
7418
|
-
//
|
|
7419
|
-
const pathsForScan = reach.reachUseOnlyPregeneratedSboms ? filterToPregeneratedSboms(packagePaths, supportedFiles).filter(p => !utils.isReachabilityReportPath(p, {
|
|
7652
|
+
// Upload Coana's inputs next to its report: depscan drops the report's
|
|
7653
|
+
// components without public metadata, such as private libraries.
|
|
7654
|
+
const pathsForScan = (reach.reachUseOnlyPregeneratedSboms ? filterToPregeneratedSboms(packagePaths, supportedFiles) : packagePaths).filter(p => !utils.isReachabilityReportPath(p, {
|
|
7420
7655
|
cwd,
|
|
7421
7656
|
outputPath: constants.default.DOT_SOCKET_DOT_FACTS_JSON
|
|
7422
|
-
}))
|
|
7423
|
-
|
|
7424
|
-
// Append coana's reachability report, but not twice: a pre-generated facts
|
|
7425
|
-
// input can resolve to the same path coana wrote its report to.
|
|
7426
|
-
const reportPath = reachabilityReport ? path.resolve(cwd, reachabilityReport) : undefined;
|
|
7427
|
-
scanPaths = [...pathsForScan.filter(p => path.resolve(cwd, p) !== reportPath), ...(reachabilityReport ? [reachabilityReport] : [])];
|
|
7657
|
+
}));
|
|
7658
|
+
scanPaths = [...pathsForScan, ...(reachabilityReport ? [reachabilityReport] : [])];
|
|
7428
7659
|
tier1ReachabilityScanId = reachResult.data?.tier1ReachabilityScanId;
|
|
7429
7660
|
}
|
|
7430
7661
|
}
|
|
@@ -9989,7 +10220,7 @@ const excludePathsFlag$1 = {
|
|
|
9989
10220
|
excludePaths: {
|
|
9990
10221
|
type: 'string',
|
|
9991
10222
|
isMultiple: true,
|
|
9992
|
-
description: 'List of glob patterns to exclude from the scan, including SCA/SBOM manifest discovery and (when --reach is enabled) full application reachability analysis. Patterns are anchored micromatch globs matched relative to the Socket scan root, which is the command working directory (`--cwd` if set), not the reachability target: `tests` matches only `<cwd>/tests`; use `**/tests` to match at any depth. Negation patterns (`!path`) are not supported. Accepts a comma-separated value or multiple flags.'
|
|
10223
|
+
description: 'List of glob patterns to exclude from the scan, including SCA/SBOM manifest discovery and (when --reach is enabled) full application reachability analysis. Patterns are anchored micromatch globs matched relative to the Socket scan root, which is the command working directory (`--cwd` if set), not the reachability target: `tests` matches only `<cwd>/tests`; use `**/tests` to match at any depth. Negation patterns (`!path`) are not supported. Accepts a comma-separated value or multiple flags. An excluded JVM build module is not analysed as a project of its own, but where a project that is not excluded depends on it, its code is analysed as part of that project.'
|
|
9993
10224
|
}
|
|
9994
10225
|
};
|
|
9995
10226
|
|
|
@@ -12634,7 +12865,7 @@ const excludePathsFlag = {
|
|
|
12634
12865
|
excludePaths: {
|
|
12635
12866
|
type: 'string',
|
|
12636
12867
|
isMultiple: true,
|
|
12637
|
-
description: 'List of glob patterns to exclude from manifest/facts generation. Patterns are anchored micromatch globs matched relative to CWD (`--cwd` if set): `tests` matches only `<cwd>/tests`; use `**/tests` to match at any depth. Negation patterns (`!path`) are not supported. Accepts a comma-separated value or multiple flags.'
|
|
12868
|
+
description: 'List of glob patterns to exclude from manifest/facts generation. Patterns are anchored micromatch globs matched relative to CWD (`--cwd` if set): `tests` matches only `<cwd>/tests`; use `**/tests` to match at any depth. Negation patterns (`!path`) are not supported. Accepts a comma-separated value or multiple flags. An excluded JVM build module is not analysed as a project of its own, but where a project that is not excluded depends on it, its code is analysed as part of that project.'
|
|
12638
12869
|
}
|
|
12639
12870
|
};
|
|
12640
12871
|
|
|
@@ -12935,12 +13166,15 @@ async function outputManifestDynamicSbomInference(result, outputKind) {
|
|
|
12935
13166
|
async function handleManifestDynamicSbomInference({
|
|
12936
13167
|
cwd,
|
|
12937
13168
|
excludePaths,
|
|
13169
|
+
ignoreUnresolved,
|
|
12938
13170
|
outputKind,
|
|
12939
13171
|
verbose
|
|
12940
13172
|
}) {
|
|
12941
13173
|
const outcomes = await generateRecursiveManifests({
|
|
12942
13174
|
cwd,
|
|
12943
13175
|
excludePaths,
|
|
13176
|
+
ignoreUnresolved,
|
|
13177
|
+
optionFlags: new Set(['ignoreUnresolved', 'verbose']),
|
|
12944
13178
|
verbose
|
|
12945
13179
|
});
|
|
12946
13180
|
const result = !outcomes.length ? {
|
|
@@ -12968,6 +13202,10 @@ const config$d = {
|
|
|
12968
13202
|
...flags.commonFlags,
|
|
12969
13203
|
...flags.outputFlags,
|
|
12970
13204
|
...excludePathsFlag,
|
|
13205
|
+
ignoreUnresolved: {
|
|
13206
|
+
type: 'boolean',
|
|
13207
|
+
description: "Warn on unresolved dependencies instead of failing the run (unresolved deps are not emitted to the facts files); overrides each build's socket.json setting"
|
|
13208
|
+
},
|
|
12971
13209
|
verbose: {
|
|
12972
13210
|
type: 'boolean',
|
|
12973
13211
|
default: false,
|
|
@@ -13012,6 +13250,7 @@ async function run$E(argv, importMeta, {
|
|
|
13012
13250
|
});
|
|
13013
13251
|
const {
|
|
13014
13252
|
dryRun,
|
|
13253
|
+
ignoreUnresolved,
|
|
13015
13254
|
json,
|
|
13016
13255
|
markdown,
|
|
13017
13256
|
verbose: verboseFlag
|
|
@@ -13055,6 +13294,7 @@ async function run$E(argv, importMeta, {
|
|
|
13055
13294
|
await handleManifestDynamicSbomInference({
|
|
13056
13295
|
cwd,
|
|
13057
13296
|
excludePaths,
|
|
13297
|
+
ignoreUnresolved,
|
|
13058
13298
|
outputKind,
|
|
13059
13299
|
verbose
|
|
13060
13300
|
});
|
|
@@ -14731,7 +14971,7 @@ async function enumerateWorkspaces({
|
|
|
14731
14971
|
result = await enumerateWorkspaces$1(ecosystem, scriptOpts);
|
|
14732
14972
|
} catch (e) {
|
|
14733
14973
|
process.exitCode = 1;
|
|
14734
|
-
logger.logger.fail(`Could not run the ${ecosystem} build tool
|
|
14974
|
+
logger.logger.fail(`Could not run the ${ecosystem} build tool: ${utils.getErrorMessageOr(e, String(e))}`);
|
|
14735
14975
|
return;
|
|
14736
14976
|
}
|
|
14737
14977
|
if (!result.projects.length) {
|
|
@@ -19871,6 +20111,16 @@ async function validateReachabilityTarget(targets, cwd) {
|
|
|
19871
20111
|
return result;
|
|
19872
20112
|
}
|
|
19873
20113
|
|
|
20114
|
+
// Build options that `socket manifest <tool>` takes as flags but the scan
|
|
20115
|
+
// commands read from each build's socket.json, so passing them does nothing.
|
|
20116
|
+
const SOCKET_JSON_ONLY_FLAGS = ['excludeConfigs', 'ignoreUnresolved', 'includeConfigs', 'verbose'];
|
|
20117
|
+
function warnSocketJsonOnlyFlags(flags, command) {
|
|
20118
|
+
const ignored = SOCKET_JSON_ONLY_FLAGS.filter(name => flags[name] !== undefined).map(name => `--${utils.camelToKebab(name)}`);
|
|
20119
|
+
if (ignored.length) {
|
|
20120
|
+
logger.logger.warn(`Ignoring ${ignored.join(', ')}: \`${command}\` reads build options from each build's socket.json. Set them there, for example with \`${DSI_SETUP_COMMAND}\`.`);
|
|
20121
|
+
}
|
|
20122
|
+
}
|
|
20123
|
+
|
|
19874
20124
|
const CMD_NAME$a = 'create';
|
|
19875
20125
|
|
|
19876
20126
|
// Accepted for backwards compatibility but forwarded nowhere, so passing one
|
|
@@ -20110,6 +20360,7 @@ async function run$d(argv, importMeta, {
|
|
|
20110
20360
|
|
|
20111
20361
|
// Validate ecosystem values against the reachability-supported set.
|
|
20112
20362
|
const reachEcosystems = utils.parseReachEcosystems(utils.cmdFlagValueToArray(cli.flags['reachEcosystems']));
|
|
20363
|
+
warnSocketJsonOnlyFlags(cli.flags, `${parentName} ${CMD_NAME$a}`);
|
|
20113
20364
|
const dryRun = !!cli.flags['dryRun'];
|
|
20114
20365
|
const uvMembers = !!cli.flags['uvMembers'];
|
|
20115
20366
|
let {
|
|
@@ -22463,6 +22714,7 @@ async function run$7(argv, importMeta, {
|
|
|
22463
22714
|
importMeta,
|
|
22464
22715
|
parentName
|
|
22465
22716
|
});
|
|
22717
|
+
warnSocketJsonOnlyFlags(cli.flags, `${parentName} ${CMD_NAME$4}`);
|
|
22466
22718
|
const {
|
|
22467
22719
|
cwd: cwdOverride,
|
|
22468
22720
|
dynamicSbomInference,
|
|
@@ -24539,5 +24791,5 @@ process.on('unhandledRejection', async (reason, promise) => {
|
|
|
24539
24791
|
// eslint-disable-next-line n/no-process-exit
|
|
24540
24792
|
process.exit(1);
|
|
24541
24793
|
});
|
|
24542
|
-
//# debugId=
|
|
24794
|
+
//# debugId=3195b2a9-2bce-44ed-8eea-bd5e763d40fa
|
|
24543
24795
|
//# sourceMappingURL=cli.js.map
|