@cdxgen/cdxgen 13.3.0 → 13.4.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/README.md +9 -6
- package/bin/cdxgen.js +90 -26
- package/bin/evinse.js +18 -1
- package/bin/repl.js +86 -0
- package/bin/sign.js +45 -15
- package/bin/tracebom.js +5 -1
- package/bin/validate.js +84 -18
- package/bin/verify.js +155 -86
- package/data/helpers/init.gradle +46 -21
- package/data/remediations.json +61 -0
- package/data/rules/dependency-sources.yaml +67 -0
- package/data/rules/scala-evidence.yaml +115 -0
- package/lib/audit/index.js +38 -6
- package/lib/cli/index.js +113 -54
- package/lib/cli/jsBom.js +50 -29
- package/lib/cli/jvmBom.js +665 -138
- package/lib/cli/managedBom.js +320 -200
- package/lib/cli/nativeBom.js +139 -30
- package/lib/core/activity.js +12 -0
- package/lib/core/env.js +17 -9
- package/lib/core/fs.js +3 -2
- package/lib/core/httpClient.js +159 -24
- package/lib/core/pluginRun.js +184 -0
- package/lib/core/processTree.js +100 -0
- package/lib/core/runState.js +39 -0
- package/lib/core/toml.js +72 -0
- package/lib/ecosystems/asarutils.js +4 -2
- package/lib/ecosystems/bunutils.js +5 -7
- package/lib/ecosystems/cargoCache.js +231 -8
- package/lib/ecosystems/cmakeResolver.js +10 -21
- package/lib/ecosystems/cppBuildContext.js +631 -0
- package/lib/ecosystems/cppEvidence.js +337 -22
- package/lib/ecosystems/cppIncludeResolver.js +294 -0
- package/lib/ecosystems/denoutils.js +93 -66
- package/lib/ecosystems/ecosystems.js +1967 -788
- package/lib/ecosystems/golem.js +28 -12
- package/lib/ecosystems/gradleutils.js +314 -30
- package/lib/ecosystems/jsEvidence.js +85 -24
- package/lib/ecosystems/kosi.js +21 -4
- package/lib/ecosystems/npmutils.js +64 -0
- package/lib/ecosystems/parsers-dotnet.js +567 -151
- package/lib/ecosystems/parsers-elm.js +46 -28
- package/lib/ecosystems/parsers-gleam.js +3 -4
- package/lib/ecosystems/parsers-go.js +324 -61
- package/lib/ecosystems/parsers-js.js +92 -26
- package/lib/ecosystems/parsers-julia.js +2 -3
- package/lib/ecosystems/parsers-jvm.js +204 -12
- package/lib/ecosystems/parsers-misc.js +182 -55
- package/lib/ecosystems/parsers-python.js +141 -10
- package/lib/ecosystems/parsers-rust.js +4 -4
- package/lib/ecosystems/parsers-terraform.js +1573 -19
- package/lib/ecosystems/pythonutils.js +102 -6
- package/lib/ecosystems/remote/huggingface.js +3 -0
- package/lib/ecosystems/rubyutils.js +67 -17
- package/lib/ecosystems/rusi.js +22 -9
- package/lib/ecosystems/sbtutils.js +243 -187
- package/lib/ecosystems/scalacliutils.js +342 -0
- package/lib/ecosystems/scalasem.js +2618 -0
- package/lib/ecosystems/terraformRegistry.js +288 -0
- package/lib/ecosystems/utils.js +2 -0
- package/lib/ecosystems/vendoredCode.js +156 -0
- package/lib/evinser/evinser.js +578 -69
- package/lib/helpers/agenticToolutils.js +2 -2
- package/lib/helpers/bomSigner.js +576 -128
- package/lib/inventory/aiCollector.js +4 -1
- package/lib/inventory/analyzer.js +110 -127
- package/lib/inventory/atomUtils.js +171 -48
- package/lib/inventory/auditCategories.js +7 -0
- package/lib/inventory/buildToolRateLimit.js +144 -0
- package/lib/inventory/cacheCommands.js +2 -1
- package/lib/inventory/cmakeBuildDirs.js +254 -0
- package/lib/inventory/deps.js +501 -124
- package/lib/inventory/dosai.js +248 -44
- package/lib/inventory/dosaiParsers.js +310 -15
- package/lib/inventory/dynamic.js +52 -6
- package/lib/inventory/evidenceUtils.js +154 -109
- package/lib/inventory/fetchBatch.js +574 -38
- package/lib/inventory/fetchDiskCache.js +597 -0
- package/lib/inventory/forgeEnricher.js +14 -5
- package/lib/inventory/jvmLocalRepos.js +917 -0
- package/lib/inventory/mcpConfigParser.js +2 -3
- package/lib/inventory/parentSelection.js +146 -0
- package/lib/inventory/rustFormulationParser.js +3 -4
- package/lib/inventory/scalaCoords.js +153 -0
- package/lib/inventory/source.js +8 -8
- package/lib/inventory/spdx.js +101 -0
- package/lib/inventory/traceRunner.js +88 -7
- package/lib/inventory/tracedCrypto.js +294 -0
- package/lib/managers/docker.js +33 -32
- package/lib/managers/oci.js +24 -2
- package/lib/parsers/cmakeCache.js +69 -0
- package/lib/parsers/cmakeLists.js +16 -2
- package/lib/parsers/cmakePresets.js +400 -0
- package/lib/parsers/compileCommands.js +557 -0
- package/lib/parsers/strictJson.js +322 -0
- package/lib/parsers/terraformConfig.js +1073 -0
- package/lib/parsers/terraformSource.js +652 -0
- package/lib/server/server.js +23 -0
- package/lib/stages/postgen/introspection/markers.js +1 -1
- package/lib/stages/postgen/postgen.js +15 -1
- package/lib/stages/postgen/specVersionCompat.js +32 -0
- package/lib/validator/index.js +25 -10
- package/lib/validator/reporters/json.js +1 -0
- package/package.json +21 -21
- package/types/lib/audit/index.d.ts.map +1 -1
- package/types/lib/cli/index.d.ts.map +1 -1
- package/types/lib/cli/jsBom.d.ts.map +1 -1
- package/types/lib/cli/jvmBom.d.ts +24 -1
- package/types/lib/cli/jvmBom.d.ts.map +1 -1
- package/types/lib/cli/managedBom.d.ts +7 -4
- package/types/lib/cli/managedBom.d.ts.map +1 -1
- package/types/lib/cli/nativeBom.d.ts +3 -1
- package/types/lib/cli/nativeBom.d.ts.map +1 -1
- package/types/lib/core/activity.d.ts.map +1 -1
- package/types/lib/core/env.d.ts +9 -0
- package/types/lib/core/env.d.ts.map +1 -1
- package/types/lib/core/fs.d.ts.map +1 -1
- package/types/lib/core/httpClient.d.ts +13 -8
- package/types/lib/core/httpClient.d.ts.map +1 -1
- package/types/lib/core/pluginRun.d.ts +97 -0
- package/types/lib/core/pluginRun.d.ts.map +1 -0
- package/types/lib/core/processTree.d.ts +36 -0
- package/types/lib/core/processTree.d.ts.map +1 -0
- package/types/lib/core/runState.d.ts +29 -0
- package/types/lib/core/runState.d.ts.map +1 -0
- package/types/lib/core/toml.d.ts +20 -0
- package/types/lib/core/toml.d.ts.map +1 -0
- package/types/lib/ecosystems/asarutils.d.ts.map +1 -1
- package/types/lib/ecosystems/bunutils.d.ts.map +1 -1
- package/types/lib/ecosystems/cargoCache.d.ts +12 -0
- package/types/lib/ecosystems/cargoCache.d.ts.map +1 -1
- package/types/lib/ecosystems/cmakeResolver.d.ts +5 -3
- package/types/lib/ecosystems/cmakeResolver.d.ts.map +1 -1
- package/types/lib/ecosystems/cppBuildContext.d.ts +55 -0
- package/types/lib/ecosystems/cppBuildContext.d.ts.map +1 -0
- package/types/lib/ecosystems/cppEvidence.d.ts +18 -2
- package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
- package/types/lib/ecosystems/cppIncludeResolver.d.ts +92 -0
- package/types/lib/ecosystems/cppIncludeResolver.d.ts.map +1 -0
- package/types/lib/ecosystems/denoutils.d.ts +9 -3
- package/types/lib/ecosystems/denoutils.d.ts.map +1 -1
- package/types/lib/ecosystems/ecosystems.d.ts +62 -13
- package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
- package/types/lib/ecosystems/golem.d.ts.map +1 -1
- package/types/lib/ecosystems/gradleutils.d.ts +50 -0
- package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
- package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -1
- package/types/lib/ecosystems/kosi.d.ts.map +1 -1
- package/types/lib/ecosystems/npmutils.d.ts +15 -0
- package/types/lib/ecosystems/npmutils.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-dotnet.d.ts +118 -4
- package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-elm.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-gleam.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-go.d.ts +44 -2
- package/types/lib/ecosystems/parsers-go.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-js.d.ts +6 -1
- package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-julia.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-jvm.d.ts +46 -1
- package/types/lib/ecosystems/parsers-jvm.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-misc.d.ts +3 -1
- package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-python.d.ts +53 -3
- package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-terraform.d.ts +38 -21
- package/types/lib/ecosystems/parsers-terraform.d.ts.map +1 -1
- package/types/lib/ecosystems/pythonutils.d.ts +27 -2
- package/types/lib/ecosystems/pythonutils.d.ts.map +1 -1
- package/types/lib/ecosystems/remote/huggingface.d.ts.map +1 -1
- package/types/lib/ecosystems/rubyutils.d.ts +21 -1
- package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
- package/types/lib/ecosystems/rusi.d.ts.map +1 -1
- package/types/lib/ecosystems/sbtutils.d.ts +88 -2
- package/types/lib/ecosystems/sbtutils.d.ts.map +1 -1
- package/types/lib/ecosystems/scalacliutils.d.ts +56 -0
- package/types/lib/ecosystems/scalacliutils.d.ts.map +1 -0
- package/types/lib/ecosystems/scalasem.d.ts +311 -0
- package/types/lib/ecosystems/scalasem.d.ts.map +1 -0
- package/types/lib/ecosystems/terraformRegistry.d.ts +9 -0
- package/types/lib/ecosystems/terraformRegistry.d.ts.map +1 -0
- package/types/lib/ecosystems/utils.d.ts +2 -2
- package/types/lib/ecosystems/utils.d.ts.map +1 -1
- package/types/lib/ecosystems/vendoredCode.d.ts +32 -0
- package/types/lib/ecosystems/vendoredCode.d.ts.map +1 -0
- package/types/lib/evinser/evinser.d.ts +75 -8
- package/types/lib/evinser/evinser.d.ts.map +1 -1
- package/types/lib/helpers/bomSigner.d.ts +80 -4
- package/types/lib/helpers/bomSigner.d.ts.map +1 -1
- package/types/lib/inventory/aiCollector.d.ts.map +1 -1
- package/types/lib/inventory/analyzer.d.ts.map +1 -1
- package/types/lib/inventory/atomUtils.d.ts +82 -10
- package/types/lib/inventory/atomUtils.d.ts.map +1 -1
- package/types/lib/inventory/auditCategories.d.ts +3 -0
- package/types/lib/inventory/auditCategories.d.ts.map +1 -1
- package/types/lib/inventory/buildToolRateLimit.d.ts +38 -0
- package/types/lib/inventory/buildToolRateLimit.d.ts.map +1 -0
- package/types/lib/inventory/cacheCommands.d.ts +2 -1
- package/types/lib/inventory/cacheCommands.d.ts.map +1 -1
- package/types/lib/inventory/cmakeBuildDirs.d.ts +58 -0
- package/types/lib/inventory/cmakeBuildDirs.d.ts.map +1 -0
- package/types/lib/inventory/deps.d.ts +59 -1
- package/types/lib/inventory/deps.d.ts.map +1 -1
- package/types/lib/inventory/dosai.d.ts +57 -8
- package/types/lib/inventory/dosai.d.ts.map +1 -1
- package/types/lib/inventory/dosaiParsers.d.ts +53 -7
- package/types/lib/inventory/dosaiParsers.d.ts.map +1 -1
- package/types/lib/inventory/dynamic.d.ts +10 -0
- package/types/lib/inventory/dynamic.d.ts.map +1 -1
- package/types/lib/inventory/evidenceUtils.d.ts +19 -4
- package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
- package/types/lib/inventory/fetchBatch.d.ts +83 -14
- package/types/lib/inventory/fetchBatch.d.ts.map +1 -1
- package/types/lib/inventory/fetchDiskCache.d.ts +169 -0
- package/types/lib/inventory/fetchDiskCache.d.ts.map +1 -0
- package/types/lib/inventory/forgeEnricher.d.ts.map +1 -1
- package/types/lib/inventory/jvmLocalRepos.d.ts +178 -0
- package/types/lib/inventory/jvmLocalRepos.d.ts.map +1 -0
- package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/parentSelection.d.ts +34 -0
- package/types/lib/inventory/parentSelection.d.ts.map +1 -0
- package/types/lib/inventory/rustFormulationParser.d.ts.map +1 -1
- package/types/lib/inventory/scalaCoords.d.ts +80 -0
- package/types/lib/inventory/scalaCoords.d.ts.map +1 -0
- package/types/lib/inventory/source.d.ts.map +1 -1
- package/types/lib/inventory/spdx.d.ts +9 -0
- package/types/lib/inventory/spdx.d.ts.map +1 -1
- package/types/lib/inventory/traceRunner.d.ts +12 -0
- package/types/lib/inventory/traceRunner.d.ts.map +1 -1
- package/types/lib/inventory/tracedCrypto.d.ts +42 -0
- package/types/lib/inventory/tracedCrypto.d.ts.map +1 -0
- package/types/lib/managers/docker.d.ts.map +1 -1
- package/types/lib/managers/oci.d.ts +3 -1
- package/types/lib/managers/oci.d.ts.map +1 -1
- package/types/lib/parsers/cmakeCache.d.ts +16 -0
- package/types/lib/parsers/cmakeCache.d.ts.map +1 -1
- package/types/lib/parsers/cmakeLists.d.ts +2 -1
- package/types/lib/parsers/cmakeLists.d.ts.map +1 -1
- package/types/lib/parsers/cmakePresets.d.ts +78 -0
- package/types/lib/parsers/cmakePresets.d.ts.map +1 -0
- package/types/lib/parsers/compileCommands.d.ts +115 -0
- package/types/lib/parsers/compileCommands.d.ts.map +1 -0
- package/types/lib/parsers/strictJson.d.ts +46 -0
- package/types/lib/parsers/strictJson.d.ts.map +1 -0
- package/types/lib/parsers/terraformConfig.d.ts +70 -0
- package/types/lib/parsers/terraformConfig.d.ts.map +1 -0
- package/types/lib/parsers/terraformSource.d.ts +66 -0
- package/types/lib/parsers/terraformSource.d.ts.map +1 -0
- package/types/lib/server/server.d.ts.map +1 -1
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
- package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -1
- package/types/lib/validator/index.d.ts +8 -0
- package/types/lib/validator/index.d.ts.map +1 -1
- package/types/lib/validator/reporters/json.d.ts.map +1 -1
- package/lib/third-party/arborist/snapshots/calc-dep-flags.test.cjs +0 -462
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
import { constants, readdirSync, readFileSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
2
|
+
import { delimiter, join } from "node:path";
|
|
3
3
|
import process from "node:process";
|
|
4
4
|
|
|
5
5
|
import { build } from "@cdxgen/cdx-purl";
|
|
6
|
-
import { valid } from "semver";
|
|
6
|
+
import { major, valid } from "semver";
|
|
7
7
|
|
|
8
|
+
import { isWin } from "../core/paths.js";
|
|
9
|
+
import { registerRunReset } from "../core/runState.js";
|
|
10
|
+
import {
|
|
11
|
+
coursierCacheDir,
|
|
12
|
+
locateInCoursierCache,
|
|
13
|
+
resetJvmLocalRepoCaches,
|
|
14
|
+
} from "../inventory/jvmLocalRepos.js";
|
|
8
15
|
import { applyPurl, mavenPurl } from "../inventory/purl.js";
|
|
16
|
+
import { parseScalaArtifact } from "../inventory/scalaCoords.js";
|
|
9
17
|
|
|
10
18
|
/**
|
|
11
19
|
* A lightweight parser for Java .properties files, replacing the properties-reader library.
|
|
@@ -61,6 +69,7 @@ import {
|
|
|
61
69
|
multiChecksumFile,
|
|
62
70
|
safeCopyFileSync,
|
|
63
71
|
safeExistsSync,
|
|
72
|
+
safeSpawnSync,
|
|
64
73
|
safeUnlinkSync,
|
|
65
74
|
safeWriteSync,
|
|
66
75
|
} from "../core/fs.js";
|
|
@@ -152,174 +161,6 @@ export function cleanupPlugin(projectPath, originalPluginsFile) {
|
|
|
152
161
|
return false;
|
|
153
162
|
}
|
|
154
163
|
|
|
155
|
-
/**
|
|
156
|
-
* Returns the root directory of the local Coursier cache, or null when it
|
|
157
|
-
* cannot be determined or does not exist.
|
|
158
|
-
*
|
|
159
|
-
* @returns {string|null} Absolute path to the cache root
|
|
160
|
-
*/
|
|
161
|
-
function coursierCacheRoot() {
|
|
162
|
-
const configured = readEnvironmentVariable("COURSIER_CACHE");
|
|
163
|
-
let cacheRoot = configured;
|
|
164
|
-
if (!cacheRoot) {
|
|
165
|
-
const home =
|
|
166
|
-
readEnvironmentVariable("HOME") || readEnvironmentVariable("USERPROFILE");
|
|
167
|
-
if (!home) {
|
|
168
|
-
return null;
|
|
169
|
-
}
|
|
170
|
-
if (process.platform === "darwin") {
|
|
171
|
-
cacheRoot = join(home, "Library", "Caches", "Coursier", "v1");
|
|
172
|
-
} else if (process.platform === "win32") {
|
|
173
|
-
const localAppData = readEnvironmentVariable("LOCALAPPDATA");
|
|
174
|
-
if (localAppData) {
|
|
175
|
-
cacheRoot = join(localAppData, "Coursier", "Cache", "v1");
|
|
176
|
-
} else {
|
|
177
|
-
cacheRoot = join(home, "AppData", "Local", "Coursier", "Cache", "v1");
|
|
178
|
-
}
|
|
179
|
-
} else {
|
|
180
|
-
cacheRoot = join(home, ".cache", "coursier", "v1");
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
return safeExistsSync(cacheRoot) ? cacheRoot : null;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// Coursier stores an artifact at
|
|
187
|
-
// <cacheRoot>/<protocol>/<host>/<repo path...>/<groupPath>/<name>/<version>/,
|
|
188
|
-
// so the only unknown part of the path is the repository prefix. Those prefixes
|
|
189
|
-
// are enumerated once per cache root with a shallow, depth-bounded scan and then
|
|
190
|
-
// probed directly. The previous implementation instead ran a recursive `**` glob
|
|
191
|
-
// across the whole cache for every single coordinate - twice, once for the
|
|
192
|
-
// registry URL and once for the jar - which costs O(cache size) per lookup
|
|
193
|
-
// regardless of whether the artifact is present. On a large shared CI cache that
|
|
194
|
-
// turned a few hundred packages into tens of minutes of directory walking.
|
|
195
|
-
// See https://github.com/cdxgen/cdxgen/issues/4291
|
|
196
|
-
const _repoRootsCache = new Map();
|
|
197
|
-
|
|
198
|
-
// A repository prefix always starts with <protocol>/<host>, and in practice adds
|
|
199
|
-
// at most a couple of path segments ("maven2", "content/repositories/releases").
|
|
200
|
-
// The scan stops at this depth so that it never descends into the group-id
|
|
201
|
-
// directories, which is where the bulk of a cache lives.
|
|
202
|
-
const MAX_REPO_PREFIX_DEPTH = 5;
|
|
203
|
-
|
|
204
|
-
function coursierRepoRoots(cacheRoot) {
|
|
205
|
-
if (_repoRootsCache.has(cacheRoot)) {
|
|
206
|
-
return _repoRootsCache.get(cacheRoot);
|
|
207
|
-
}
|
|
208
|
-
const roots = [];
|
|
209
|
-
// Breadth-first so that shallower prefixes, which are the more common ones,
|
|
210
|
-
// are probed first.
|
|
211
|
-
let frontier = [[]];
|
|
212
|
-
for (let depth = 1; depth <= MAX_REPO_PREFIX_DEPTH; depth++) {
|
|
213
|
-
const next = [];
|
|
214
|
-
for (const parts of frontier) {
|
|
215
|
-
let entries;
|
|
216
|
-
try {
|
|
217
|
-
entries = readdirSync(join(cacheRoot, ...parts), {
|
|
218
|
-
withFileTypes: true,
|
|
219
|
-
});
|
|
220
|
-
} catch (_err) {
|
|
221
|
-
continue;
|
|
222
|
-
}
|
|
223
|
-
for (const entry of entries) {
|
|
224
|
-
if (!entry.isDirectory()) {
|
|
225
|
-
continue;
|
|
226
|
-
}
|
|
227
|
-
const childParts = [...parts, entry.name];
|
|
228
|
-
// <protocol>/<host> is the shortest thing that can be a repository.
|
|
229
|
-
if (depth >= 2) {
|
|
230
|
-
roots.push(childParts);
|
|
231
|
-
}
|
|
232
|
-
next.push(childParts);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
frontier = next;
|
|
236
|
-
if (!frontier.length) {
|
|
237
|
-
break;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
_repoRootsCache.set(cacheRoot, roots);
|
|
241
|
-
return roots;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* Converts a repository prefix in the Coursier cache layout into a URL.
|
|
246
|
-
*
|
|
247
|
-
* @param {string[]} parts Path segments of the prefix, relative to the cache root
|
|
248
|
-
* @returns {string|null} The repository URL
|
|
249
|
-
*/
|
|
250
|
-
function repoPrefixToUrl(parts) {
|
|
251
|
-
if (!parts || parts.length < 2) {
|
|
252
|
-
return null;
|
|
253
|
-
}
|
|
254
|
-
const protocol = parts[0];
|
|
255
|
-
if (protocol === "file") {
|
|
256
|
-
let localPath = parts.slice(1).join("/");
|
|
257
|
-
if (!localPath.startsWith("/")) {
|
|
258
|
-
localPath = `/${localPath}`;
|
|
259
|
-
}
|
|
260
|
-
return `file://${localPath}`;
|
|
261
|
-
}
|
|
262
|
-
const host = parts[1];
|
|
263
|
-
const repoPath = parts.slice(2).join("/");
|
|
264
|
-
let repoUrl = `${protocol}://${host}`;
|
|
265
|
-
if (repoPath) {
|
|
266
|
-
repoUrl += `/${repoPath}`;
|
|
267
|
-
}
|
|
268
|
-
if (repoUrl.endsWith("/")) {
|
|
269
|
-
repoUrl = repoUrl.substring(0, repoUrl.length - 1);
|
|
270
|
-
}
|
|
271
|
-
return repoUrl;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// Memoized so that the registry URL and the jar path for one coordinate cost a
|
|
275
|
-
// single lookup between them.
|
|
276
|
-
const _coursierLocationCache = new Map();
|
|
277
|
-
|
|
278
|
-
/**
|
|
279
|
-
* Locate a Maven coordinate in the local Coursier cache.
|
|
280
|
-
*
|
|
281
|
-
* @param {string} group Maven groupId
|
|
282
|
-
* @param {string} name Maven artifactId (original name with suffix if applicable)
|
|
283
|
-
* @param {string} version Package version
|
|
284
|
-
* @returns {{ repoUrl: string, dir: string }|null} The repository URL and the artifact directory
|
|
285
|
-
*/
|
|
286
|
-
function locateInCoursierCache(group, name, version) {
|
|
287
|
-
if (!group || !name || !version) {
|
|
288
|
-
return null;
|
|
289
|
-
}
|
|
290
|
-
const cacheRoot = coursierCacheRoot();
|
|
291
|
-
if (!cacheRoot) {
|
|
292
|
-
return null;
|
|
293
|
-
}
|
|
294
|
-
// The cache root is part of the key so that a changed COURSIER_CACHE is never
|
|
295
|
-
// answered from a memo built against the previous one.
|
|
296
|
-
const cacheKey = `${cacheRoot}|${group}:${name}:${version}`;
|
|
297
|
-
if (_coursierLocationCache.has(cacheKey)) {
|
|
298
|
-
return _coursierLocationCache.get(cacheKey);
|
|
299
|
-
}
|
|
300
|
-
const groupParts = group.split(".");
|
|
301
|
-
const roots = coursierRepoRoots(cacheRoot);
|
|
302
|
-
let result = null;
|
|
303
|
-
for (let i = 0; i < roots.length; i++) {
|
|
304
|
-
const dir = join(cacheRoot, ...roots[i], ...groupParts, name, version);
|
|
305
|
-
if (safeExistsSync(dir)) {
|
|
306
|
-
const repoUrl = repoPrefixToUrl(roots[i]);
|
|
307
|
-
if (repoUrl) {
|
|
308
|
-
result = { repoUrl, dir };
|
|
309
|
-
// Most coordinates in a build come from the same handful of
|
|
310
|
-
// repositories, so keeping the one that just matched at the front makes
|
|
311
|
-
// the common case a single stat call.
|
|
312
|
-
if (i > 0) {
|
|
313
|
-
roots.unshift(roots.splice(i, 1)[0]);
|
|
314
|
-
}
|
|
315
|
-
break;
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
_coursierLocationCache.set(cacheKey, result);
|
|
320
|
-
return result;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
164
|
/**
|
|
324
165
|
* Find the repository URL from the local Coursier cache for a given Maven package.
|
|
325
166
|
*
|
|
@@ -408,6 +249,19 @@ export function findLocalJarPath(group, name, version) {
|
|
|
408
249
|
// take many minutes and appear as a hang. See
|
|
409
250
|
// https://github.com/cdxgen/cdxgen/issues/4291
|
|
410
251
|
const _jarDistributionCache = new Map();
|
|
252
|
+
registerRunReset("sbt:jar-distributions", () => _jarDistributionCache.clear());
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Forget every memoised Coursier lookup, including misses. A long-lived
|
|
256
|
+
* process calls this per scan so that artifacts downloaded since the previous
|
|
257
|
+
* scan are found.
|
|
258
|
+
*
|
|
259
|
+
* @returns {void}
|
|
260
|
+
*/
|
|
261
|
+
export function resetSbtResolutionCaches() {
|
|
262
|
+
_jarDistributionCache.clear();
|
|
263
|
+
resetJvmLocalRepoCaches();
|
|
264
|
+
}
|
|
411
265
|
|
|
412
266
|
/**
|
|
413
267
|
* Resolve the repo/jar download URLs and optional hashes for a Maven coordinate.
|
|
@@ -423,7 +277,7 @@ const _jarDistributionCache = new Map();
|
|
|
423
277
|
* Resolved URLs with optional hashes, or null when no registry URL is found.
|
|
424
278
|
*/
|
|
425
279
|
export async function resolveJarDistribution(group, name, version) {
|
|
426
|
-
const cacheKey = `${
|
|
280
|
+
const cacheKey = `${coursierCacheDir()}|${group}:${name}:${version}`;
|
|
427
281
|
if (_jarDistributionCache.has(cacheKey)) {
|
|
428
282
|
return _jarDistributionCache.get(cacheKey);
|
|
429
283
|
}
|
|
@@ -463,9 +317,12 @@ export async function resolveJarDistribution(group, name, version) {
|
|
|
463
317
|
* coordinates, building a hierarchical dependency graph. Evicted packages and ranges are ignored.
|
|
464
318
|
*
|
|
465
319
|
* @param {string} sbtTreeFile Path to the sbt dependency tree output file
|
|
320
|
+
* @param {string} [srcFile] Build definition the tree was resolved from, named as the
|
|
321
|
+
* identity source of every component. The tree file itself is a temporary file cdxgen
|
|
322
|
+
* deletes after the scan, and naming it would make the BOM differ between two runs.
|
|
466
323
|
* @returns {{ pkgList: Object[], dependenciesList: Object[] }}
|
|
467
324
|
*/
|
|
468
|
-
export async function parseSbtTree(sbtTreeFile) {
|
|
325
|
+
export async function parseSbtTree(sbtTreeFile, srcFile = sbtTreeFile) {
|
|
469
326
|
const pkgList = [];
|
|
470
327
|
const dependenciesList = [];
|
|
471
328
|
const keys_cache = {};
|
|
@@ -548,12 +405,10 @@ export async function parseSbtTree(sbtTreeFile) {
|
|
|
548
405
|
continue;
|
|
549
406
|
}
|
|
550
407
|
const originalName = name;
|
|
551
|
-
const
|
|
552
|
-
const
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
compilerVersion = match[1];
|
|
556
|
-
name = name.replace(scalaSuffixRegex, "");
|
|
408
|
+
const scalaArtifact = parseScalaArtifact(name);
|
|
409
|
+
const compilerVersion = scalaArtifact.binaryVersion;
|
|
410
|
+
if (compilerVersion) {
|
|
411
|
+
name = scalaArtifact.purlName;
|
|
557
412
|
}
|
|
558
413
|
const distInfo = await resolveJarDistribution(group, originalName, version);
|
|
559
414
|
const qualifiers = { type: "jar" };
|
|
@@ -590,7 +445,7 @@ export async function parseSbtTree(sbtTreeFile) {
|
|
|
590
445
|
{
|
|
591
446
|
technique: "manifest-analysis",
|
|
592
447
|
confidence: 1,
|
|
593
|
-
value:
|
|
448
|
+
value: srcFile,
|
|
594
449
|
},
|
|
595
450
|
],
|
|
596
451
|
},
|
|
@@ -695,14 +550,9 @@ export async function parseSbtLock(pkgLockFile) {
|
|
|
695
550
|
}
|
|
696
551
|
}
|
|
697
552
|
const originalName = pkg.name;
|
|
698
|
-
|
|
699
|
-
const
|
|
700
|
-
const
|
|
701
|
-
let compilerVersion = null;
|
|
702
|
-
if (match) {
|
|
703
|
-
compilerVersion = match[1];
|
|
704
|
-
name = name.replace(scalaSuffixRegex, "");
|
|
705
|
-
}
|
|
553
|
+
const scalaArtifact = parseScalaArtifact(originalName);
|
|
554
|
+
const name = scalaArtifact.purlName;
|
|
555
|
+
const compilerVersion = scalaArtifact.binaryVersion;
|
|
706
556
|
const distInfo = await resolveJarDistribution(
|
|
707
557
|
pkg.org,
|
|
708
558
|
originalName,
|
|
@@ -909,6 +759,58 @@ export function parseSbtProjects(stdout) {
|
|
|
909
759
|
return { projects, root };
|
|
910
760
|
}
|
|
911
761
|
|
|
762
|
+
/**
|
|
763
|
+
* Whether an sbt version is sbt 2 or later, the release candidates of sbt 2
|
|
764
|
+
* included: `2.0.0-RC12` is below `2.0.0` in semver order.
|
|
765
|
+
*
|
|
766
|
+
* @param {string|null|undefined} sbtVersion Version from project/build.properties
|
|
767
|
+
* @returns {boolean} True for sbt 2.x and later
|
|
768
|
+
*/
|
|
769
|
+
export function isSbt2Version(sbtVersion) {
|
|
770
|
+
return Boolean(sbtVersion && valid(sbtVersion) && major(sbtVersion) >= 2);
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
/**
|
|
774
|
+
* Quote a path as an sbt command argument. Inside double quotes sbt reads
|
|
775
|
+
* backslash escapes, so the backslashes of a Windows path are escaped, and a
|
|
776
|
+
* path with spaces stays one argument.
|
|
777
|
+
*
|
|
778
|
+
* @param {string} filePath Path to quote
|
|
779
|
+
* @returns {string} The quoted path
|
|
780
|
+
*/
|
|
781
|
+
function sbtQuotedPath(filePath) {
|
|
782
|
+
return `"${`${filePath}`.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"`;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
/**
|
|
786
|
+
* Build the single command line an sbt 2 dependency tree session runs.
|
|
787
|
+
*
|
|
788
|
+
* sbt 2 joins separate command-line arguments into one command line, so the
|
|
789
|
+
* commands are joined with `;` and passed as one argument. sbt 2 ships the
|
|
790
|
+
* dependency tree tasks itself, and `dependencyTree / toFile` became
|
|
791
|
+
* `dependencyTree --out`, so every tree is written to its own file, the
|
|
792
|
+
* extra roots a tree may list included. The trees are not aggregated: the
|
|
793
|
+
* tree of an aggregating root would otherwise be overwritten by the trees of
|
|
794
|
+
* the projects it aggregates. Each tree is scoped to its project, so one
|
|
795
|
+
* unresolvable subproject cannot fail the others.
|
|
796
|
+
*
|
|
797
|
+
* @param {{project?: string, file: string}[]} trees The project id of each
|
|
798
|
+
* tree, none for the current project, and the file it is written to
|
|
799
|
+
* @returns {string} The `;`-joined command line
|
|
800
|
+
*/
|
|
801
|
+
export function sbt2DependencyTreeCommand(trees) {
|
|
802
|
+
const commands = [
|
|
803
|
+
"set ThisBuild / asciiGraphWidth := 800",
|
|
804
|
+
"set ThisBuild / dependencyTree / aggregate := false",
|
|
805
|
+
];
|
|
806
|
+
for (const { project, file } of trees || []) {
|
|
807
|
+
commands.push(
|
|
808
|
+
`${project ? `${project}/` : ""}dependencyTree --out ${sbtQuotedPath(file)}`,
|
|
809
|
+
);
|
|
810
|
+
}
|
|
811
|
+
return commands.join("; ");
|
|
812
|
+
}
|
|
813
|
+
|
|
912
814
|
/**
|
|
913
815
|
* Parse plugins.sbt files to extract sbt plugins as development dependencies.
|
|
914
816
|
*
|
|
@@ -970,3 +872,157 @@ export function parseSbtPlugins(projectPath) {
|
|
|
970
872
|
}
|
|
971
873
|
return plugins;
|
|
972
874
|
}
|
|
875
|
+
|
|
876
|
+
/**
|
|
877
|
+
* The sbt launcher jar beside the `sbt.bat` on PATH, or under `SBT_HOME`.
|
|
878
|
+
* `SBT_LAUNCH_JAR` names one directly.
|
|
879
|
+
*
|
|
880
|
+
* @returns {string|undefined} Path of `sbt-launch.jar`
|
|
881
|
+
*/
|
|
882
|
+
export function sbtLaunchJar() {
|
|
883
|
+
const named = readEnvironmentVariable("SBT_LAUNCH_JAR");
|
|
884
|
+
if (named && safeExistsSync(named)) {
|
|
885
|
+
return named;
|
|
886
|
+
}
|
|
887
|
+
const dirs =
|
|
888
|
+
`${readEnvironmentVariable("PATH") || readEnvironmentVariable("Path") || ""}`
|
|
889
|
+
.split(delimiter)
|
|
890
|
+
.filter(Boolean);
|
|
891
|
+
const sbtHome = readEnvironmentVariable("SBT_HOME");
|
|
892
|
+
if (sbtHome) {
|
|
893
|
+
dirs.unshift(join(sbtHome, "bin"));
|
|
894
|
+
}
|
|
895
|
+
for (const dir of dirs) {
|
|
896
|
+
if (safeExistsSync(join(dir, "sbt.bat"))) {
|
|
897
|
+
const jar = join(dir, "sbt-launch.jar");
|
|
898
|
+
if (safeExistsSync(jar)) {
|
|
899
|
+
return jar;
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
return undefined;
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
/**
|
|
907
|
+
* Split sbt arguments written for a POSIX shell into the arguments the shell
|
|
908
|
+
* would pass: whitespace separates them outside quotes, and single or double
|
|
909
|
+
* quotes group words without escapes, so a Windows path keeps its
|
|
910
|
+
* backslashes.
|
|
911
|
+
*
|
|
912
|
+
* @param {string[]} args Shell-quoted argument fragments
|
|
913
|
+
* @returns {string[]} The arguments
|
|
914
|
+
*/
|
|
915
|
+
export function splitSbtShellArgs(args) {
|
|
916
|
+
const out = [];
|
|
917
|
+
for (const fragment of args) {
|
|
918
|
+
let current = "";
|
|
919
|
+
let quote;
|
|
920
|
+
let started = false;
|
|
921
|
+
for (const ch of `${fragment}`) {
|
|
922
|
+
if (quote) {
|
|
923
|
+
if (ch === quote) {
|
|
924
|
+
quote = undefined;
|
|
925
|
+
} else {
|
|
926
|
+
current += ch;
|
|
927
|
+
}
|
|
928
|
+
} else if (ch === "'" || ch === '"') {
|
|
929
|
+
quote = ch;
|
|
930
|
+
started = true;
|
|
931
|
+
} else if (/\s/.test(ch)) {
|
|
932
|
+
if (started) {
|
|
933
|
+
out.push(current);
|
|
934
|
+
current = "";
|
|
935
|
+
started = false;
|
|
936
|
+
}
|
|
937
|
+
} else {
|
|
938
|
+
current += ch;
|
|
939
|
+
started = true;
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
if (started) {
|
|
943
|
+
out.push(current);
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
return out;
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
/**
|
|
950
|
+
* The command line that starts sbt from its launcher jar. The options
|
|
951
|
+
* `sbt.bat` handles itself become what it makes of them: `-no-colors` and
|
|
952
|
+
* `-D` properties are JVM options, `-addPluginSbtFile` is sbt's own
|
|
953
|
+
* `--addPluginSbtFile`, and `-batch` and the thin client switches have no
|
|
954
|
+
* meaning there. The JVM options of `SBT_OPTS`, `JAVA_OPTS` and the build's
|
|
955
|
+
* `.jvmopts` are kept.
|
|
956
|
+
*
|
|
957
|
+
* @param {string[]} args Arguments as given to `sbt`
|
|
958
|
+
* @param {string} jar The sbt launcher jar
|
|
959
|
+
* @param {string} [dir] Build directory
|
|
960
|
+
* @returns {{ command: string, args: string[] }}
|
|
961
|
+
*/
|
|
962
|
+
export function sbtLauncherCommand(args, jar, dir) {
|
|
963
|
+
const jvm = ["-Dfile.encoding=UTF-8", "-Xss4m", "-Dsbt.supershell=false"];
|
|
964
|
+
for (const name of ["JAVA_OPTS", "SBT_OPTS"]) {
|
|
965
|
+
jvm.push(
|
|
966
|
+
...`${readEnvironmentVariable(name) || ""}`.split(/\s+/).filter(Boolean),
|
|
967
|
+
);
|
|
968
|
+
}
|
|
969
|
+
const jvmopts = dir ? join(dir, ".jvmopts") : undefined;
|
|
970
|
+
if (jvmopts && safeExistsSync(jvmopts)) {
|
|
971
|
+
jvm.push(
|
|
972
|
+
...readFileSync(jvmopts, "utf-8")
|
|
973
|
+
.split(/\s+/)
|
|
974
|
+
.filter((o) => o && !o.startsWith("#")),
|
|
975
|
+
);
|
|
976
|
+
}
|
|
977
|
+
const commands = [];
|
|
978
|
+
for (const arg of args) {
|
|
979
|
+
if (arg === "-no-colors") {
|
|
980
|
+
jvm.push("-Dsbt.log.noformat=true");
|
|
981
|
+
} else if (arg.startsWith("-D")) {
|
|
982
|
+
jvm.push(arg);
|
|
983
|
+
} else if (arg.startsWith("-J")) {
|
|
984
|
+
jvm.push(arg.slice(2));
|
|
985
|
+
} else if (arg.startsWith("-addPluginSbtFile=")) {
|
|
986
|
+
commands.push(`-${arg}`);
|
|
987
|
+
} else if (!["-batch", "--server", "--client", "-client"].includes(arg)) {
|
|
988
|
+
commands.push(arg);
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
const javaHome = readEnvironmentVariable("JAVA_HOME");
|
|
992
|
+
return {
|
|
993
|
+
command: javaHome ? join(javaHome, "bin", "java.exe") : "java.exe",
|
|
994
|
+
args: [...jvm, "-jar", jar, ...commands],
|
|
995
|
+
};
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
/**
|
|
999
|
+
* Run sbt. On Windows, `sbt.bat` parses its arguments as batch syntax, which
|
|
1000
|
+
* keeps the POSIX quoting of the commands and mangles quotes inside them, so
|
|
1001
|
+
* sbt starts there from its launcher jar with each command one argument.
|
|
1002
|
+
* Elsewhere, or without a launcher jar, sbt runs as given.
|
|
1003
|
+
*
|
|
1004
|
+
* @param {string} sbtCmd sbt executable
|
|
1005
|
+
* @param {string[]} args sbt arguments
|
|
1006
|
+
* @param {Object} options Spawn options, as for safeSpawnSync
|
|
1007
|
+
* @param {boolean} [shellQuoted] Whether the arguments carry POSIX shell quoting
|
|
1008
|
+
* @returns {Object} The spawn result
|
|
1009
|
+
*/
|
|
1010
|
+
export function sbtSpawnSync(sbtCmd, args, options, shellQuoted = false) {
|
|
1011
|
+
const jar =
|
|
1012
|
+
isWin && !`${sbtCmd}`.toLowerCase().endsWith(".jar")
|
|
1013
|
+
? sbtLaunchJar()
|
|
1014
|
+
: undefined;
|
|
1015
|
+
if (!jar) {
|
|
1016
|
+
return safeSpawnSync(sbtCmd, args, options);
|
|
1017
|
+
}
|
|
1018
|
+
const launcher = sbtLauncherCommand(
|
|
1019
|
+
shellQuoted ? splitSbtShellArgs(args) : args,
|
|
1020
|
+
jar,
|
|
1021
|
+
options?.cwd,
|
|
1022
|
+
);
|
|
1023
|
+
const { cdxgenRawShellArgs: _raw, ...spawnOptions } = options || {};
|
|
1024
|
+
return safeSpawnSync(launcher.command, launcher.args, {
|
|
1025
|
+
...spawnOptions,
|
|
1026
|
+
shell: false,
|
|
1027
|
+
});
|
|
1028
|
+
}
|