@cdxgen/cdxgen 13.3.0 → 13.5.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 +12 -9
- package/bin/cdxgen-secure.js +15 -0
- package/bin/cdxgen.js +115 -31
- package/bin/evinse.js +23 -1
- package/bin/hbom.js +15 -7
- package/bin/repl.js +115 -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/cbomosdb-queries.json +13 -1
- package/data/component-tags.json +8 -0
- package/data/helpers/init.gradle +46 -21
- package/data/queries-darwin.json +25 -1
- package/data/queries-win.json +19 -1
- package/data/queries.json +41 -0
- package/data/remediations.json +344 -1
- package/data/rules/cbom-security.yaml +235 -4
- package/data/rules/dependency-sources.yaml +67 -0
- package/data/rules/go-binaries.yaml +46 -0
- package/data/rules/golem-go.yaml +80 -0
- package/data/rules/hbom-compliance.yaml +40 -22
- package/data/rules/hbom-performance.yaml +7 -1
- package/data/rules/host-topology.yaml +1 -1
- package/data/rules/obom-runtime.yaml +31 -2
- package/data/rules/php-runtime.yaml +143 -0
- package/data/rules/scala-evidence.yaml +115 -0
- package/lib/audit/index.js +52 -6
- package/lib/cli/bomAssembly.js +28 -3
- package/lib/cli/cliOptions.js +110 -3
- package/lib/cli/index.js +186 -58
- package/lib/cli/jsBom.js +50 -29
- package/lib/cli/jvmBom.js +779 -146
- package/lib/cli/managedBom.js +1021 -261
- package/lib/cli/nativeBom.js +364 -58
- package/lib/core/activity.js +12 -0
- package/lib/core/env.js +47 -17
- package/lib/core/fs.js +31 -21
- package/lib/core/httpClient.js +233 -69
- package/lib/core/pluginRun.js +184 -0
- package/lib/core/processTree.js +100 -0
- package/lib/core/projectConfig.js +1 -0
- package/lib/core/projectTooling.js +97 -0
- package/lib/core/propertySanitizer.js +72 -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 +578 -18
- package/lib/ecosystems/gradleutils.js +340 -46
- 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 +668 -153
- package/lib/ecosystems/parsers-elm.js +46 -28
- package/lib/ecosystems/parsers-gleam.js +3 -4
- package/lib/ecosystems/parsers-go.js +1283 -297
- package/lib/ecosystems/parsers-js.js +92 -26
- package/lib/ecosystems/parsers-julia.js +2 -3
- package/lib/ecosystems/parsers-jvm.js +463 -29
- package/lib/ecosystems/parsers-misc.js +405 -93
- package/lib/ecosystems/parsers-python.js +152 -20
- 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 +73 -20
- package/lib/ecosystems/rusi.js +32 -9
- package/lib/ecosystems/sbtutils.js +243 -187
- package/lib/ecosystems/scalacliutils.js +343 -0
- package/lib/ecosystems/scalasem.js +2620 -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 +695 -78
- package/lib/evinser/swiftsem.js +68 -5
- package/lib/helpers/agenticToolutils.js +2 -2
- package/lib/helpers/bomSigner.js +576 -128
- package/lib/inventory/agentSkillSpec.js +17 -0
- package/lib/inventory/aiCollector.js +4 -1
- package/lib/inventory/analyzer.js +110 -127
- package/lib/inventory/apkInstalledDb.js +243 -0
- package/lib/inventory/atomUtils.js +204 -48
- package/lib/inventory/auditCategories.js +10 -3
- package/lib/inventory/buildToolRateLimit.js +144 -0
- package/lib/inventory/cacheCommands.js +2 -1
- package/lib/inventory/cacheDir.js +52 -9
- package/lib/inventory/certificates.js +2371 -0
- package/lib/inventory/cmakeBuildDirs.js +254 -0
- package/lib/inventory/communityAiConfigParser.js +4 -9
- package/lib/inventory/deps.js +507 -128
- package/lib/inventory/display.js +200 -3
- package/lib/inventory/dosai.js +747 -101
- package/lib/inventory/dosaiParsers.js +310 -15
- package/lib/inventory/dynamic.js +96 -8
- package/lib/inventory/envcontext.js +168 -5
- package/lib/inventory/evidenceUtils.js +590 -132
- package/lib/inventory/fetchBatch.js +574 -38
- package/lib/inventory/fetchDiskCache.js +597 -0
- package/lib/inventory/forgeEnricher.js +14 -5
- package/lib/inventory/formulationParsers.js +3 -2
- package/lib/inventory/goBuildInfo.js +140 -0
- package/lib/inventory/hbom.js +67 -7
- package/lib/inventory/hbomAnalysis.js +16 -0
- package/lib/inventory/hostTopology.js +21 -0
- package/lib/inventory/jvmLocalRepos.js +917 -0
- package/lib/inventory/mcpConfigParser.js +2 -3
- package/lib/inventory/osPackagePurl.js +244 -0
- package/lib/inventory/osPackageResolver.js +89 -87
- package/lib/inventory/osinfo.js +79 -31
- package/lib/inventory/osqueryTransform.js +114 -5
- package/lib/inventory/parentSelection.js +146 -0
- package/lib/inventory/phpBuildInfo.js +487 -0
- package/lib/inventory/phpHostProbe.js +641 -0
- package/lib/inventory/rootfsCerts.js +426 -0
- package/lib/inventory/rustFormulationParser.js +3 -4
- package/lib/inventory/scalaCoords.js +153 -0
- package/lib/inventory/source.js +215 -11
- package/lib/inventory/spdx.js +245 -0
- package/lib/inventory/traceRunner.js +93 -8
- package/lib/inventory/tracedCrypto.js +423 -0
- package/lib/managers/binary.js +529 -183
- package/lib/managers/docker.js +199 -44
- 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/email.js +277 -0
- package/lib/parsers/largeJson.js +34 -2
- 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 +209 -1
- package/lib/stages/postgen/annotator.js +17 -1
- package/lib/stages/postgen/introspection/markers.js +1 -1
- package/lib/stages/postgen/introspection/reflect.js +4 -1
- package/lib/stages/postgen/postgen.js +20 -6
- package/lib/stages/postgen/ruleEngine.js +58 -1
- package/lib/stages/postgen/specVersionCompat.js +62 -0
- package/lib/stages/pregen/pregen.js +117 -5
- package/lib/validator/bomValidator.js +26 -5
- package/lib/validator/generated/runtime.mjs +13 -0
- package/lib/validator/generated/validate-1.6.mjs +26 -7
- package/lib/validator/generated/validate-1.7.mjs +31 -12
- package/lib/validator/generated/validate-2.0.mjs +30 -11
- package/lib/validator/index.js +25 -10
- package/lib/validator/reporters/json.js +1 -0
- package/package.json +23 -23
- package/types/bin/cdxgen-secure.d.ts +3 -0
- package/types/bin/cdxgen-secure.d.ts.map +1 -0
- package/types/bin/repl.d.ts.map +1 -1
- package/types/lib/audit/index.d.ts.map +1 -1
- package/types/lib/cli/bomAssembly.d.ts +9 -2
- package/types/lib/cli/bomAssembly.d.ts.map +1 -1
- package/types/lib/cli/cliOptions.d.ts +43 -0
- package/types/lib/cli/cliOptions.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 +44 -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 +27 -2
- 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/projectConfig.d.ts.map +1 -1
- package/types/lib/core/projectTooling.d.ts +41 -0
- package/types/lib/core/projectTooling.d.ts.map +1 -0
- package/types/lib/core/propertySanitizer.d.ts +26 -0
- package/types/lib/core/propertySanitizer.d.ts.map +1 -1
- 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 +19 -3
- 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 +81 -1
- package/types/lib/ecosystems/golem.d.ts.map +1 -1
- package/types/lib/ecosystems/gradleutils.d.ts +59 -7
- 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 +155 -5
- 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 +119 -5
- 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 +74 -3
- package/types/lib/ecosystems/parsers-jvm.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-misc.d.ts +21 -59
- package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-python.d.ts +57 -6
- 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 +26 -1
- 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 +100 -8
- package/types/lib/evinser/evinser.d.ts.map +1 -1
- package/types/lib/evinser/swiftsem.d.ts +10 -0
- package/types/lib/evinser/swiftsem.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/agentSkillSpec.d.ts +13 -0
- package/types/lib/inventory/agentSkillSpec.d.ts.map +1 -0
- package/types/lib/inventory/aiCollector.d.ts.map +1 -1
- package/types/lib/inventory/analyzer.d.ts.map +1 -1
- package/types/lib/inventory/apkInstalledDb.d.ts +42 -0
- package/types/lib/inventory/apkInstalledDb.d.ts.map +1 -0
- package/types/lib/inventory/atomUtils.d.ts +96 -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/cacheDir.d.ts +12 -4
- package/types/lib/inventory/cacheDir.d.ts.map +1 -1
- package/types/lib/inventory/certificates.d.ts +205 -0
- package/types/lib/inventory/certificates.d.ts.map +1 -0
- package/types/lib/inventory/cmakeBuildDirs.d.ts +58 -0
- package/types/lib/inventory/cmakeBuildDirs.d.ts.map +1 -0
- package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/deps.d.ts +61 -2
- package/types/lib/inventory/deps.d.ts.map +1 -1
- package/types/lib/inventory/display.d.ts +18 -0
- package/types/lib/inventory/display.d.ts.map +1 -1
- package/types/lib/inventory/dosai.d.ts +202 -14
- 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 +16 -1
- package/types/lib/inventory/dynamic.d.ts.map +1 -1
- package/types/lib/inventory/envcontext.d.ts +40 -2
- package/types/lib/inventory/envcontext.d.ts.map +1 -1
- package/types/lib/inventory/evidenceUtils.d.ts +48 -14
- 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/formulationParsers.d.ts.map +1 -1
- package/types/lib/inventory/goBuildInfo.d.ts +10 -0
- package/types/lib/inventory/goBuildInfo.d.ts.map +1 -0
- package/types/lib/inventory/hbom.d.ts.map +1 -1
- package/types/lib/inventory/hbomAnalysis.d.ts +10 -0
- package/types/lib/inventory/hbomAnalysis.d.ts.map +1 -1
- package/types/lib/inventory/hostTopology.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/osPackagePurl.d.ts +99 -0
- package/types/lib/inventory/osPackagePurl.d.ts.map +1 -0
- package/types/lib/inventory/osPackageResolver.d.ts +6 -2
- package/types/lib/inventory/osPackageResolver.d.ts.map +1 -1
- package/types/lib/inventory/osinfo.d.ts +31 -6
- package/types/lib/inventory/osinfo.d.ts.map +1 -1
- package/types/lib/inventory/osqueryTransform.d.ts +51 -0
- package/types/lib/inventory/osqueryTransform.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/phpBuildInfo.d.ts +142 -0
- package/types/lib/inventory/phpBuildInfo.d.ts.map +1 -0
- package/types/lib/inventory/phpHostProbe.d.ts +35 -0
- package/types/lib/inventory/phpHostProbe.d.ts.map +1 -0
- package/types/lib/inventory/rootfsCerts.d.ts +27 -0
- package/types/lib/inventory/rootfsCerts.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 +28 -0
- package/types/lib/inventory/source.d.ts.map +1 -1
- package/types/lib/inventory/spdx.d.ts +24 -0
- package/types/lib/inventory/spdx.d.ts.map +1 -1
- package/types/lib/inventory/traceRunner.d.ts +16 -1
- package/types/lib/inventory/traceRunner.d.ts.map +1 -1
- package/types/lib/inventory/tracedCrypto.d.ts +64 -0
- package/types/lib/inventory/tracedCrypto.d.ts.map +1 -0
- package/types/lib/managers/binary.d.ts +10 -0
- package/types/lib/managers/binary.d.ts.map +1 -1
- package/types/lib/managers/docker.d.ts +12 -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/email.d.ts +19 -0
- package/types/lib/parsers/email.d.ts.map +1 -0
- package/types/lib/parsers/largeJson.d.ts +13 -0
- package/types/lib/parsers/largeJson.d.ts.map +1 -1
- 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 +8 -0
- package/types/lib/server/server.d.ts.map +1 -1
- package/types/lib/stages/postgen/annotator.d.ts.map +1 -1
- package/types/lib/stages/postgen/introspection/reflect.d.ts.map +1 -1
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
- package/types/lib/stages/postgen/ruleEngine.d.ts +7 -4
- package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
- package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -1
- package/types/lib/stages/pregen/pregen.d.ts +14 -1
- package/types/lib/stages/pregen/pregen.d.ts.map +1 -1
- package/types/lib/validator/bomValidator.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
package/lib/cli/jvmBom.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Buffer } from "node:buffer";
|
|
2
|
-
import { readFileSync } from "node:fs";
|
|
2
|
+
import { readdirSync, readFileSync } from "node:fs";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { basename, dirname, join, resolve, sep } from "node:path";
|
|
5
5
|
import process from "node:process";
|
|
@@ -35,16 +35,23 @@ import {
|
|
|
35
35
|
} from "../core/fs.js";
|
|
36
36
|
import { thoughtLog } from "../core/logger.js";
|
|
37
37
|
import { dirNameStr } from "../core/paths.js";
|
|
38
|
+
import {
|
|
39
|
+
isSecureModeActive,
|
|
40
|
+
reportProjectToolingSkipped,
|
|
41
|
+
} from "../core/projectTooling.js";
|
|
38
42
|
import { extractJarArchive, getMvnMetadata } from "../ecosystems/ecosystems.js";
|
|
39
43
|
import {
|
|
40
44
|
buildGradleCommandArguments,
|
|
41
45
|
buildObjectForGradleModule,
|
|
42
46
|
collectGradleDependencies,
|
|
43
47
|
executeParallelGradleProperties,
|
|
48
|
+
findMillDaemons,
|
|
44
49
|
getGradleCommand,
|
|
45
50
|
getMavenCommand,
|
|
46
51
|
getMillCommand,
|
|
52
|
+
getMillVersion,
|
|
47
53
|
hasGradleInvocationFailure,
|
|
54
|
+
isProjectToolingAllowed,
|
|
48
55
|
parseGradleDep,
|
|
49
56
|
parseGradleInfoLogsForUrls,
|
|
50
57
|
parseGradleProperties,
|
|
@@ -52,29 +59,42 @@ import {
|
|
|
52
59
|
parseGradleVersionCatalog,
|
|
53
60
|
recordGradleInvocationFailure,
|
|
54
61
|
splitOutputByGradleProjects,
|
|
62
|
+
stopMillDaemons,
|
|
55
63
|
} from "../ecosystems/gradleutils.js";
|
|
56
64
|
import {
|
|
57
65
|
parseModuleBazel,
|
|
58
66
|
parseModuleBazelLock,
|
|
59
67
|
} from "../ecosystems/parsers-bazel.js";
|
|
60
68
|
import {
|
|
69
|
+
MAVEN_MODULE_TREE_STEM,
|
|
61
70
|
parseBazelActionGraph,
|
|
62
71
|
parseBazelSkyframe,
|
|
63
72
|
parseMavenTree,
|
|
64
73
|
parseMavenTreeJson,
|
|
65
74
|
parseMillDependency,
|
|
66
75
|
parsePom,
|
|
76
|
+
parsePomWithLocalParents,
|
|
77
|
+
readMavenModuleTrees,
|
|
67
78
|
} from "../ecosystems/parsers-jvm.js";
|
|
68
79
|
import {
|
|
69
80
|
addPlugin,
|
|
70
81
|
cleanupPlugin,
|
|
71
82
|
determineSbtVersion,
|
|
72
83
|
discoverSbtProjects,
|
|
84
|
+
isSbt2Version,
|
|
73
85
|
parseSbtLock,
|
|
74
86
|
parseSbtProjects,
|
|
75
87
|
parseSbtRootProject,
|
|
76
88
|
parseSbtTree,
|
|
89
|
+
sbt2DependencyTreeCommand,
|
|
90
|
+
sbtSpawnSync,
|
|
77
91
|
} from "../ecosystems/sbtutils.js";
|
|
92
|
+
import { collectScalaCliComponents } from "../ecosystems/scalacliutils.js";
|
|
93
|
+
import { collectScalaJsNpmComponents } from "../ecosystems/scalasem.js";
|
|
94
|
+
import {
|
|
95
|
+
isBuildToolRateLimited,
|
|
96
|
+
noteBuildToolRateLimit,
|
|
97
|
+
} from "../inventory/buildToolRateLimit.js";
|
|
78
98
|
import {
|
|
79
99
|
collectJarNS,
|
|
80
100
|
collectMvnDependencies,
|
|
@@ -85,8 +105,15 @@ import {
|
|
|
85
105
|
attachIdentityTools,
|
|
86
106
|
extractToolRefs,
|
|
87
107
|
} from "../inventory/evidenceUtils.js";
|
|
108
|
+
import {
|
|
109
|
+
coursierCacheDir,
|
|
110
|
+
findLocalMavenArtifact,
|
|
111
|
+
locateInCoursierCache,
|
|
112
|
+
projectSettingsArgs,
|
|
113
|
+
} from "../inventory/jvmLocalRepos.js";
|
|
88
114
|
import { readGradleWrapperVersion } from "../inventory/jvmToolEnv.js";
|
|
89
115
|
import { applyPurl, mavenPurl } from "../inventory/purl.js";
|
|
116
|
+
import { publishedArtifactId } from "../inventory/scalaCoords.js";
|
|
90
117
|
import {
|
|
91
118
|
buildBomNSData,
|
|
92
119
|
createDefaultParentComponent,
|
|
@@ -136,6 +163,127 @@ export const GRADLE_INIT_SCRIPT = resolve(
|
|
|
136
163
|
export const SBT_CACHE_DIR =
|
|
137
164
|
readEnvironmentVariable("SBT_CACHE_DIR") || join(homedir(), ".ivy2", "cache");
|
|
138
165
|
|
|
166
|
+
/**
|
|
167
|
+
* Collect the jars in the caches sbt uses: the Coursier cache, which sbt 1.3
|
|
168
|
+
* and later resolve into, and the Ivy cache named by SBT_CACHE_DIR, which
|
|
169
|
+
* older builds used.
|
|
170
|
+
*
|
|
171
|
+
* @returns {Promise<Object>} Jar namespace mapping, as collectJarNS returns.
|
|
172
|
+
*/
|
|
173
|
+
async function collectSbtCacheDependencies() {
|
|
174
|
+
let nsMapping = {};
|
|
175
|
+
for (const cacheDir of [coursierCacheDir(), SBT_CACHE_DIR]) {
|
|
176
|
+
if (cacheDir && safeExistsSync(cacheDir)) {
|
|
177
|
+
nsMapping = { ...nsMapping, ...(await collectJarNS(cacheDir)) };
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return nsMapping;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* The jar of a resolved sbt dependency in the user's own caches: the Coursier
|
|
185
|
+
* cache sbt 1.3 and later resolve into, the other local Maven caches, and the
|
|
186
|
+
* Ivy cache older builds used.
|
|
187
|
+
*
|
|
188
|
+
* @param {Object} pkg Package with group, name and version.
|
|
189
|
+
* @returns {string|undefined} Path of the jar.
|
|
190
|
+
*/
|
|
191
|
+
function localSbtDependencyJar(pkg) {
|
|
192
|
+
// Scala components are named without their binary suffix; the jar has it.
|
|
193
|
+
const name = publishedArtifactId(pkg);
|
|
194
|
+
const coursierDir = locateInCoursierCache(pkg.group, name, pkg.version)?.dir;
|
|
195
|
+
const coursierJar =
|
|
196
|
+
coursierDir && join(coursierDir, `${name}-${pkg.version}.jar`);
|
|
197
|
+
if (coursierJar && safeExistsSync(coursierJar)) {
|
|
198
|
+
return coursierJar;
|
|
199
|
+
}
|
|
200
|
+
const jarPath = findLocalMavenArtifact(pkg.group, name, pkg.version)?.jarPath;
|
|
201
|
+
if (jarPath) {
|
|
202
|
+
return jarPath;
|
|
203
|
+
}
|
|
204
|
+
for (const typeDir of ["jars", "bundles"]) {
|
|
205
|
+
const ivyJar = join(
|
|
206
|
+
SBT_CACHE_DIR,
|
|
207
|
+
pkg.group,
|
|
208
|
+
name,
|
|
209
|
+
typeDir,
|
|
210
|
+
`${name}-${pkg.version}.jar`,
|
|
211
|
+
);
|
|
212
|
+
if (safeExistsSync(ivyJar)) {
|
|
213
|
+
return ivyJar;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return undefined;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Collect the namespaces of the jars of resolved sbt dependencies. sbt has
|
|
221
|
+
* just downloaded them into the user's caches, so each is read from there;
|
|
222
|
+
* the rest of the cache is never walked.
|
|
223
|
+
*
|
|
224
|
+
* @param {Object[]} pkgList Packages from the sbt dependency trees.
|
|
225
|
+
* @param {Object} [known] Jar namespace mapping already collected. Packages
|
|
226
|
+
* it covers are skipped.
|
|
227
|
+
* @returns {Promise<Object>} Jar namespace mapping keyed by package purl.
|
|
228
|
+
*/
|
|
229
|
+
export async function collectSbtDependencyJars(pkgList, known = {}) {
|
|
230
|
+
const nsMapping = {};
|
|
231
|
+
for (const pkg of pkgList || []) {
|
|
232
|
+
// Applications are the build's own projects, which no cache holds.
|
|
233
|
+
if (
|
|
234
|
+
!pkg?.purl ||
|
|
235
|
+
!pkg.group ||
|
|
236
|
+
!pkg.name ||
|
|
237
|
+
!pkg.version ||
|
|
238
|
+
pkg.type === "application" ||
|
|
239
|
+
known[pkg.purl] ||
|
|
240
|
+
nsMapping[pkg.purl]
|
|
241
|
+
) {
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
const jarPath = localSbtDependencyJar(pkg);
|
|
245
|
+
if (!jarPath) {
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
const [entry] = Object.values(await collectJarNS(jarPath));
|
|
249
|
+
if (entry) {
|
|
250
|
+
nsMapping[pkg.purl] = entry;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return nsMapping;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* The purls and bom-refs of a parent component and the module components
|
|
258
|
+
* nested under it.
|
|
259
|
+
*
|
|
260
|
+
* @param {Object} parentComponent Parent component.
|
|
261
|
+
* @returns {Set<string>} Purls and bom-refs.
|
|
262
|
+
*/
|
|
263
|
+
export function ownComponentRefs(parentComponent) {
|
|
264
|
+
const refs = new Set();
|
|
265
|
+
// The Gradle path nests modules that can point back at the parent, so the
|
|
266
|
+
// walk remembers what it has seen.
|
|
267
|
+
const seen = new Set();
|
|
268
|
+
const stack = parentComponent ? [parentComponent] : [];
|
|
269
|
+
while (stack.length) {
|
|
270
|
+
const component = stack.pop();
|
|
271
|
+
if (!component || typeof component !== "object" || seen.has(component)) {
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
seen.add(component);
|
|
275
|
+
for (const ref of [component.purl, component["bom-ref"]]) {
|
|
276
|
+
if (ref) {
|
|
277
|
+
refs.add(ref);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
if (Array.isArray(component.components)) {
|
|
281
|
+
stack.push(...component.components);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return refs;
|
|
285
|
+
}
|
|
286
|
+
|
|
139
287
|
/**
|
|
140
288
|
* Function to create bom string for Java jars
|
|
141
289
|
*
|
|
@@ -179,6 +327,8 @@ export async function createJarBom(path, options) {
|
|
|
179
327
|
false,
|
|
180
328
|
true,
|
|
181
329
|
);
|
|
330
|
+
} else if (options.useSbtCache) {
|
|
331
|
+
nsMapping = await collectSbtCacheDependencies();
|
|
182
332
|
}
|
|
183
333
|
if (path.endsWith(".jar")) {
|
|
184
334
|
jarFiles = [resolve(path)];
|
|
@@ -207,14 +357,17 @@ export async function createJarBom(path, options) {
|
|
|
207
357
|
if (dlist?.length) {
|
|
208
358
|
pkgList = pkgList.concat(dlist);
|
|
209
359
|
}
|
|
210
|
-
if (pkgList.length) {
|
|
211
|
-
pkgList = await getMvnMetadata(pkgList);
|
|
212
|
-
}
|
|
213
360
|
// Clean up
|
|
214
361
|
if (tempDir?.startsWith(getTmpDir())) {
|
|
215
362
|
safeRmSync(tempDir, { recursive: true, force: true });
|
|
216
363
|
}
|
|
217
364
|
}
|
|
365
|
+
// Metadata is resolved once for the whole scan. Inside the loop every jar
|
|
366
|
+
// re-resolved the packages of all the jars before it, and lookups that had
|
|
367
|
+
// failed were sent again each time.
|
|
368
|
+
if (pkgList.length) {
|
|
369
|
+
pkgList = await getMvnMetadata(pkgList);
|
|
370
|
+
}
|
|
218
371
|
pkgList = pkgList.concat(await convertJarNSToPackages(nsMapping));
|
|
219
372
|
return buildBomNSData(options, pkgList, "maven", {
|
|
220
373
|
src: path,
|
|
@@ -222,6 +375,37 @@ export async function createJarBom(path, options) {
|
|
|
222
375
|
});
|
|
223
376
|
}
|
|
224
377
|
|
|
378
|
+
/**
|
|
379
|
+
* Whether a project keeps source directories of its own. Used to keep the
|
|
380
|
+
* root project of an sbt build: a root with sources is usually the
|
|
381
|
+
* application itself and merely aggregates the modules for convenience,
|
|
382
|
+
* while a root without sources is a pure aggregator whose own resolution
|
|
383
|
+
* adds nothing.
|
|
384
|
+
*
|
|
385
|
+
* @param {string} basePath Directory of the sbt build
|
|
386
|
+
* @returns {boolean} True when the build root has main sources
|
|
387
|
+
*/
|
|
388
|
+
function sbtRootHasOwnSources(basePath) {
|
|
389
|
+
for (const language of ["scala", "java", "kotlin"]) {
|
|
390
|
+
if (safeExistsSync(join(basePath, "src", "main", language))) {
|
|
391
|
+
return true;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
// A Play application keeps its sources in app/ instead of src/main
|
|
395
|
+
const appDir = join(basePath, "app");
|
|
396
|
+
if (safeExistsSync(appDir)) {
|
|
397
|
+
try {
|
|
398
|
+
const entries = readdirSync(appDir, { withFileTypes: true });
|
|
399
|
+
return entries.some(
|
|
400
|
+
(entry) => entry.isDirectory() || entry.name.endsWith(".scala"),
|
|
401
|
+
);
|
|
402
|
+
} catch (_err) {
|
|
403
|
+
return false;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
return false;
|
|
407
|
+
}
|
|
408
|
+
|
|
225
409
|
/**
|
|
226
410
|
* Discover the real sbt project ids for a build by invoking sbt's own
|
|
227
411
|
* `projects` command. This is more reliable than scraping the build files with
|
|
@@ -229,19 +413,28 @@ export async function createJarBom(path, options) {
|
|
|
229
413
|
* (commented-out code, examples, values that merely resemble project defs) that
|
|
230
414
|
* can lead to hangs when those bogus scopes are later passed to `dependencyTree`.
|
|
231
415
|
*
|
|
416
|
+
* The root project is kept when it has sources of its own, since it is then
|
|
417
|
+
* the application rather than a pure aggregator.
|
|
418
|
+
*
|
|
232
419
|
* Falls back to the regex-based {@link discoverSbtProjects} heuristic when the
|
|
233
420
|
* sbt invocation fails or yields nothing useful.
|
|
234
421
|
*
|
|
235
422
|
* @param {string} basePath Directory of the sbt build
|
|
236
423
|
* @param {string} sbtCmd sbt executable
|
|
237
424
|
* @param {Object} env Environment for the spawned process
|
|
425
|
+
* @param {string[]} [launcherArgs] Launcher options placed before the sbt arguments
|
|
238
426
|
* @returns {string[]} List of sbt project ids
|
|
239
427
|
*/
|
|
240
|
-
export function discoverSbtProjectsFromCmd(
|
|
428
|
+
export function discoverSbtProjectsFromCmd(
|
|
429
|
+
basePath,
|
|
430
|
+
sbtCmd,
|
|
431
|
+
env,
|
|
432
|
+
launcherArgs = [],
|
|
433
|
+
) {
|
|
241
434
|
try {
|
|
242
|
-
const result =
|
|
435
|
+
const result = sbtSpawnSync(
|
|
243
436
|
sbtCmd,
|
|
244
|
-
["-batch", "-no-colors", '"projects"'],
|
|
437
|
+
[...launcherArgs, "-batch", "-no-colors", '"projects"'],
|
|
245
438
|
{
|
|
246
439
|
cwd: basePath,
|
|
247
440
|
shell: true,
|
|
@@ -250,13 +443,16 @@ export function discoverSbtProjectsFromCmd(basePath, sbtCmd, env) {
|
|
|
250
443
|
env,
|
|
251
444
|
encoding: "utf-8",
|
|
252
445
|
},
|
|
446
|
+
true,
|
|
253
447
|
);
|
|
254
448
|
if (result.status === 0 && !result.error) {
|
|
255
449
|
const { projects, root } = parseSbtProjects(result.stdout || "");
|
|
256
|
-
// Exclude the aggregating root project
|
|
257
|
-
//
|
|
258
|
-
//
|
|
259
|
-
const subprojects =
|
|
450
|
+
// Exclude the aggregating root project unless it has sources of its
|
|
451
|
+
// own: its dependencyTree is then the application's own dependencies
|
|
452
|
+
// and not a subset of the aggregated subprojects.
|
|
453
|
+
const subprojects = sbtRootHasOwnSources(basePath)
|
|
454
|
+
? projects
|
|
455
|
+
: projects.filter((p) => p !== root);
|
|
260
456
|
if (subprojects.length > 0) {
|
|
261
457
|
if (DEBUG_MODE) {
|
|
262
458
|
console.log(
|
|
@@ -301,6 +497,63 @@ export function discoverSbtProjectsFromCmd(basePath, sbtCmd, env) {
|
|
|
301
497
|
return discoverSbtProjects(basePath);
|
|
302
498
|
}
|
|
303
499
|
|
|
500
|
+
/**
|
|
501
|
+
* Read one pom.xml without running Maven: the module it declares and its
|
|
502
|
+
* direct dependencies. Versions resolve from the pom's properties and
|
|
503
|
+
* dependencyManagement and from parent poms inside the scanned directory.
|
|
504
|
+
* Versions that an imported BOM or a parent outside the directory manages
|
|
505
|
+
* stay empty, and transitive dependencies are unknown.
|
|
506
|
+
*
|
|
507
|
+
* No dependency edges are produced. The build-fidelity rules (BF-JVM-001,
|
|
508
|
+
* BF-GEN-001) recognise a pom read without Maven by its missing graph, and a
|
|
509
|
+
* graph of direct edges alone would pass for a resolved one.
|
|
510
|
+
*
|
|
511
|
+
* @param {string} pomFile pom.xml to read
|
|
512
|
+
* @param {string} rootPath Scanned directory
|
|
513
|
+
* @param {Map} [cache] Parent pom results shared by the poms of one scan
|
|
514
|
+
* @returns {{moduleComponent: Object|undefined, pkgList: Object[]}}
|
|
515
|
+
*/
|
|
516
|
+
export function readMavenModuleStatically(pomFile, rootPath, cache) {
|
|
517
|
+
let pomMap;
|
|
518
|
+
try {
|
|
519
|
+
pomMap = parsePomWithLocalParents(pomFile, rootPath, cache);
|
|
520
|
+
} catch (_err) {
|
|
521
|
+
return { moduleComponent: undefined, pkgList: [] };
|
|
522
|
+
}
|
|
523
|
+
const pkgList = [];
|
|
524
|
+
for (const dep of pomMap?.dependencies || []) {
|
|
525
|
+
const purl = mavenPurl(
|
|
526
|
+
dep.group || undefined,
|
|
527
|
+
dep.name,
|
|
528
|
+
dep.version || undefined,
|
|
529
|
+
dep.qualifiers,
|
|
530
|
+
);
|
|
531
|
+
pkgList.push(
|
|
532
|
+
purl ? { ...dep, purl, "bom-ref": decodeURIComponent(purl) } : dep,
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
const { artifactId, groupId, packaging, version } = pomMap?.properties || {};
|
|
536
|
+
const modulePurl = artifactId
|
|
537
|
+
? mavenPurl(groupId || undefined, artifactId, version || undefined, {
|
|
538
|
+
type: packaging || "jar",
|
|
539
|
+
})
|
|
540
|
+
: undefined;
|
|
541
|
+
if (!modulePurl) {
|
|
542
|
+
return { moduleComponent: undefined, pkgList };
|
|
543
|
+
}
|
|
544
|
+
const moduleComponent = {
|
|
545
|
+
group: groupId || "",
|
|
546
|
+
name: artifactId,
|
|
547
|
+
version: version || "",
|
|
548
|
+
qualifiers: { type: packaging || "jar" },
|
|
549
|
+
type: "application",
|
|
550
|
+
purl: modulePurl,
|
|
551
|
+
"bom-ref": decodeURIComponent(modulePurl),
|
|
552
|
+
properties: [{ name: "internal:SrcFile", value: pomFile }],
|
|
553
|
+
};
|
|
554
|
+
return { moduleComponent, pkgList };
|
|
555
|
+
}
|
|
556
|
+
|
|
304
557
|
/**
|
|
305
558
|
* Function to create bom string for Java projects
|
|
306
559
|
*
|
|
@@ -320,6 +573,8 @@ export async function createJavaBom(path, options) {
|
|
|
320
573
|
let tools;
|
|
321
574
|
let possible_misses = false;
|
|
322
575
|
let parallelPropTaskOut = "";
|
|
576
|
+
// Mill daemons already running belong to the user and outlive the scan
|
|
577
|
+
const millDaemonsBefore = findMillDaemons(path);
|
|
323
578
|
try {
|
|
324
579
|
// war/ear mode
|
|
325
580
|
if (path.endsWith(".war") || path.endsWith(".jar")) {
|
|
@@ -329,8 +584,10 @@ export async function createJavaBom(path, options) {
|
|
|
329
584
|
console.log(`Retrieving packages from ${path}`);
|
|
330
585
|
}
|
|
331
586
|
const tempDir = safeMkdtempSync(join(getTmpDir(), "war-deps-"));
|
|
332
|
-
|
|
333
|
-
|
|
587
|
+
// The nested jars are read from the extraction directory and their
|
|
588
|
+
// namespaces attached to each component there; the directory is
|
|
589
|
+
// removed afterwards, so no jar paths are kept for evinse.
|
|
590
|
+
pkgList = await extractJarArchive(path, tempDir);
|
|
334
591
|
if (pkgList.length) {
|
|
335
592
|
pkgList = await getMvnMetadata(pkgList);
|
|
336
593
|
}
|
|
@@ -456,8 +713,75 @@ export async function createJavaBom(path, options) {
|
|
|
456
713
|
}
|
|
457
714
|
const firstPom = pomFiles.length ? pomFiles[0] : undefined;
|
|
458
715
|
let mavenCmd = getMavenCommand(path, path, options);
|
|
716
|
+
// Executing Maven evaluates the pom's build section, loads `.mvn/`
|
|
717
|
+
// configuration such as jvm.config or extensions.xml, and resolves
|
|
718
|
+
// build extensions, so it belongs to the dependency-tooling surface
|
|
719
|
+
// disabled by --no-install-deps and the pre-build lifecycle. The scan
|
|
720
|
+
// then keeps only the direct dependencies each pom declares.
|
|
721
|
+
const mavenToolingAllowed = isProjectToolingAllowed(options);
|
|
722
|
+
if (!mavenToolingAllowed) {
|
|
723
|
+
reportProjectToolingSkipped(
|
|
724
|
+
options,
|
|
725
|
+
"jvm.maven.tooling-disabled",
|
|
726
|
+
"Skipping Maven since dependency installation is disabled. Only the direct dependencies declared in pom.xml files will be used.",
|
|
727
|
+
{
|
|
728
|
+
ecosystem: "java",
|
|
729
|
+
tool: "maven",
|
|
730
|
+
impact: "transitive-deps",
|
|
731
|
+
detail:
|
|
732
|
+
"Dependency installation is disabled, so Maven was not run and only the direct dependencies parsed from pom.xml are available.",
|
|
733
|
+
},
|
|
734
|
+
);
|
|
735
|
+
}
|
|
736
|
+
// Poms read without Maven: what their local parents define is shared
|
|
737
|
+
// across the scan, and each pom adds its module and direct dependencies.
|
|
738
|
+
const staticPomCache = new Map();
|
|
739
|
+
const addStaticMavenModule = (pomFile) => {
|
|
740
|
+
const staticModule = readMavenModuleStatically(
|
|
741
|
+
pomFile,
|
|
742
|
+
path,
|
|
743
|
+
staticPomCache,
|
|
744
|
+
);
|
|
745
|
+
if (staticModule.pkgList.length) {
|
|
746
|
+
pkgList = pkgList.concat(staticModule.pkgList);
|
|
747
|
+
}
|
|
748
|
+
const moduleComponent = staticModule.moduleComponent;
|
|
749
|
+
if (!moduleComponent) {
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
if (!Object.keys(parentComponent).length) {
|
|
753
|
+
parentComponent = moduleComponent;
|
|
754
|
+
parentComponent.components = [];
|
|
755
|
+
} else if (
|
|
756
|
+
parentComponent["bom-ref"] !== moduleComponent["bom-ref"] &&
|
|
757
|
+
!parentComponent.components?.some(
|
|
758
|
+
(c) => c["bom-ref"] === moduleComponent["bom-ref"],
|
|
759
|
+
)
|
|
760
|
+
) {
|
|
761
|
+
parentComponent.components = parentComponent.components || [];
|
|
762
|
+
parentComponent.components.push(moduleComponent);
|
|
763
|
+
}
|
|
764
|
+
};
|
|
765
|
+
// Pom files whose own tree a recursive run of their aggregator already
|
|
766
|
+
// returned. Each is skipped when its turn comes, so a reactor is run
|
|
767
|
+
// once from its aggregator instead of once per member.
|
|
768
|
+
const coveredPomFiles = new Set();
|
|
459
769
|
for (const f of pomFiles) {
|
|
460
770
|
const basePath = dirname(f);
|
|
771
|
+
if (coveredPomFiles.has(f)) {
|
|
772
|
+
if (DEBUG_MODE) {
|
|
773
|
+
console.log(
|
|
774
|
+
`Skipped ${basePath}: the recursive dependency:tree run of its aggregator already resolved it.`,
|
|
775
|
+
);
|
|
776
|
+
}
|
|
777
|
+
continue;
|
|
778
|
+
}
|
|
779
|
+
// Without Maven every pom is read on its own, Quarkus modules too:
|
|
780
|
+
// the plugin run that would have covered them does not happen.
|
|
781
|
+
if (!mavenToolingAllowed) {
|
|
782
|
+
addStaticMavenModule(f);
|
|
783
|
+
continue;
|
|
784
|
+
}
|
|
461
785
|
if (
|
|
462
786
|
isQuarkus &&
|
|
463
787
|
!options.deep &&
|
|
@@ -468,15 +792,37 @@ export async function createJavaBom(path, options) {
|
|
|
468
792
|
}
|
|
469
793
|
continue;
|
|
470
794
|
}
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
795
|
+
if (isBuildToolRateLimited("maven")) {
|
|
796
|
+
// Every further Maven run in this scan would pay the same back-off
|
|
797
|
+
// for the same empty answer, so Maven is not asked again. The
|
|
798
|
+
// direct dependencies the pom declares are kept, as for a pom whose
|
|
799
|
+
// run failed.
|
|
800
|
+
if (DEBUG_MODE) {
|
|
801
|
+
console.log(
|
|
802
|
+
`Parsing ${f} without Maven: the repository rate limited Maven in this scan.`,
|
|
803
|
+
);
|
|
804
|
+
}
|
|
805
|
+
addStaticMavenModule(f);
|
|
806
|
+
continue;
|
|
476
807
|
}
|
|
477
808
|
if (mavenCmd?.endsWith("mvn")) {
|
|
478
809
|
mavenCmd = getMavenCommand(basePath, path, options);
|
|
479
810
|
}
|
|
811
|
+
// A settings.xml beside the pom is passed as global settings, so the
|
|
812
|
+
// user's own settings.xml, with its mirrors and credentials, still
|
|
813
|
+
// applies on top of it.
|
|
814
|
+
const projectSettings = projectSettingsArgs(basePath, {
|
|
815
|
+
secureMode: isSecureModeActive(),
|
|
816
|
+
});
|
|
817
|
+
if (projectSettings.args.length) {
|
|
818
|
+
console.log(
|
|
819
|
+
`Passing ${projectSettings.settingsFile} to Maven as global settings (-gs), so your own settings.xml still applies. Name a settings file in MVN_ARGS to change this.`,
|
|
820
|
+
);
|
|
821
|
+
} else if (projectSettings.skipped === "secure-mode") {
|
|
822
|
+
console.log(
|
|
823
|
+
`Ignoring ${projectSettings.settingsFile} in secure mode. If you trust it, pass it with MVN_ARGS='--global-settings ${projectSettings.settingsFile}'.`,
|
|
824
|
+
);
|
|
825
|
+
}
|
|
480
826
|
// Should we attempt to resolve class names
|
|
481
827
|
if (options.resolveClass || options.deep) {
|
|
482
828
|
const tmpjarNSMapping = await collectMvnDependencies(
|
|
@@ -490,13 +836,18 @@ export async function createJavaBom(path, options) {
|
|
|
490
836
|
}
|
|
491
837
|
}
|
|
492
838
|
// Use the cyclonedx maven plugin if there is no preference for maven deps tree
|
|
493
|
-
if (!useMavenDepsTree) {
|
|
839
|
+
if (!useMavenDepsTree && !isBuildToolRateLimited("maven")) {
|
|
494
840
|
thoughtLog("The user wants me to use the cyclonedx-maven plugin.");
|
|
495
841
|
console.log(`Executing '${mavenCmd}' in`, basePath);
|
|
496
842
|
result = safeSpawnSync(mavenCmd, mvnArgs, {
|
|
497
843
|
cwd: basePath,
|
|
498
844
|
shell: isWin,
|
|
499
845
|
});
|
|
846
|
+
noteBuildToolRateLimit("maven", commandOutputText(result), {
|
|
847
|
+
command: `${basename(mavenCmd)} ${mvnArgs.join(" ")}`,
|
|
848
|
+
exitCode:
|
|
849
|
+
typeof result.status === "number" ? result.status : undefined,
|
|
850
|
+
});
|
|
500
851
|
// Check if the cyclonedx plugin created the required bom.json file
|
|
501
852
|
// Sometimes the plugin fails silently for complex maven projects
|
|
502
853
|
bomJsonFiles = getAllFiles(
|
|
@@ -522,8 +873,20 @@ export async function createJavaBom(path, options) {
|
|
|
522
873
|
) {
|
|
523
874
|
const tempRoot = getTmpDir();
|
|
524
875
|
const tempDir = safeMkdtempSync(join(tempRoot, "cdxgen-mvn-"));
|
|
525
|
-
|
|
526
|
-
|
|
876
|
+
// The recursive run writes one tree per reactor module into these
|
|
877
|
+
// directories, so the aggregator's run returns every module.
|
|
878
|
+
const treeJsonDir = join(tempDir, "tree-json");
|
|
879
|
+
const treeTextDir = join(tempDir, "tree-text");
|
|
880
|
+
safeMkdirSync(treeJsonDir);
|
|
881
|
+
safeMkdirSync(treeTextDir);
|
|
882
|
+
const tempMvnTree = join(
|
|
883
|
+
treeJsonDir,
|
|
884
|
+
`${MAVEN_MODULE_TREE_STEM}.json`,
|
|
885
|
+
);
|
|
886
|
+
const tempMvnTreeText = join(
|
|
887
|
+
treeTextDir,
|
|
888
|
+
`${MAVEN_MODULE_TREE_STEM}.txt`,
|
|
889
|
+
);
|
|
527
890
|
const tempMvnParentTree = join(
|
|
528
891
|
tempDir,
|
|
529
892
|
"cdxgen-mvn-parent-tree.json",
|
|
@@ -543,14 +906,11 @@ export async function createJavaBom(path, options) {
|
|
|
543
906
|
addArgs = parseMavenArgs(readEnvironmentVariable("MVN_ARGS"));
|
|
544
907
|
mvnTreeArgs = mvnTreeArgs.concat(addArgs);
|
|
545
908
|
}
|
|
546
|
-
|
|
547
|
-
if (safeExistsSync(settingsXml)) {
|
|
548
|
-
mvnTreeArgs.push("-s");
|
|
549
|
-
mvnTreeArgs.push(settingsXml);
|
|
550
|
-
}
|
|
909
|
+
mvnTreeArgs = mvnTreeArgs.concat(projectSettings.args);
|
|
551
910
|
// For the first pom alone, we need to execute first in non-recursive mode to capture
|
|
552
911
|
// the parent component. Then, we execute all of them in recursive mode
|
|
553
|
-
|
|
912
|
+
// A rate limit the --deep copy-dependencies run met ends it too.
|
|
913
|
+
if (f === firstPom && !isBuildToolRateLimited("maven")) {
|
|
554
914
|
thoughtLog(
|
|
555
915
|
"What is the parent component here? Let's use maven command to find out.",
|
|
556
916
|
);
|
|
@@ -560,14 +920,19 @@ export async function createJavaBom(path, options) {
|
|
|
560
920
|
`-DoutputFile=${tempMvnParentTree}`,
|
|
561
921
|
"-DoutputType=json",
|
|
562
922
|
];
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
923
|
+
findParentComponentArgs = findParentComponentArgs.concat(
|
|
924
|
+
addArgs,
|
|
925
|
+
projectSettings.args,
|
|
926
|
+
);
|
|
567
927
|
result = safeSpawnSync(mavenCmd, findParentComponentArgs, {
|
|
568
928
|
cwd: basePath,
|
|
569
929
|
shell: isWin,
|
|
570
930
|
});
|
|
931
|
+
noteBuildToolRateLimit("maven", commandOutputText(result), {
|
|
932
|
+
command: `${basename(mavenCmd)} ${findParentComponentArgs.join(" ")}`,
|
|
933
|
+
exitCode:
|
|
934
|
+
typeof result.status === "number" ? result.status : undefined,
|
|
935
|
+
});
|
|
571
936
|
// If json is empty or unparseable, fallback to text parsing
|
|
572
937
|
let emptyJson = !safeExistsSync(tempMvnParentTree);
|
|
573
938
|
if (safeExistsSync(tempMvnParentTree)) {
|
|
@@ -583,16 +948,15 @@ export async function createJavaBom(path, options) {
|
|
|
583
948
|
}
|
|
584
949
|
}
|
|
585
950
|
}
|
|
586
|
-
if (
|
|
951
|
+
if (
|
|
952
|
+
!isBuildToolRateLimited("maven") &&
|
|
953
|
+
(result.status !== 0 || result.error || emptyJson)
|
|
954
|
+
) {
|
|
587
955
|
findParentComponentArgs = [
|
|
588
956
|
"dependency:tree",
|
|
589
957
|
"-N",
|
|
590
958
|
`-DoutputFile=${tempMvnParentTreeText}`,
|
|
591
|
-
];
|
|
592
|
-
if (addArgs.length) {
|
|
593
|
-
findParentComponentArgs =
|
|
594
|
-
findParentComponentArgs.concat(addArgs);
|
|
595
|
-
}
|
|
959
|
+
].concat(addArgs, projectSettings.args);
|
|
596
960
|
result = safeSpawnSync(mavenCmd, findParentComponentArgs, {
|
|
597
961
|
cwd: basePath,
|
|
598
962
|
shell: isWin,
|
|
@@ -632,49 +996,60 @@ export async function createJavaBom(path, options) {
|
|
|
632
996
|
`Executing '${basename(mavenCmd)} dependency:tree ...' in ${basePath}`,
|
|
633
997
|
);
|
|
634
998
|
}
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
if (safeExistsSync(tempMvnTree)) {
|
|
641
|
-
const mvnTreeString = readFileSync(tempMvnTree, {
|
|
642
|
-
encoding: "utf-8",
|
|
643
|
-
});
|
|
644
|
-
const parsedList = parseMavenTreeJson(mvnTreeString, f);
|
|
645
|
-
if (!parsedList?.pkgList?.length) {
|
|
646
|
-
emptyJson = true;
|
|
647
|
-
// Remove the invalid json file so text output is preferred below.
|
|
648
|
-
if (safeExistsSync(tempMvnTree)) {
|
|
649
|
-
safeUnlinkSync(tempMvnTree);
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
if (result.status !== 0 || result.error || emptyJson) {
|
|
654
|
-
mvnTreeArgs = [
|
|
655
|
-
"dependency:tree",
|
|
656
|
-
`-DoutputFile=${tempMvnTreeText}`,
|
|
657
|
-
];
|
|
658
|
-
if (addArgs.length) {
|
|
659
|
-
mvnTreeArgs = mvnTreeArgs.concat(addArgs);
|
|
660
|
-
}
|
|
661
|
-
if (safeExistsSync(settingsXml)) {
|
|
662
|
-
mvnTreeArgs.push("-s");
|
|
663
|
-
mvnTreeArgs.push(settingsXml);
|
|
664
|
-
}
|
|
999
|
+
if (isBuildToolRateLimited("maven")) {
|
|
1000
|
+
// The parent or plugin run already met the rate limit, so the
|
|
1001
|
+
// tree run is skipped rather than repeated for the same answer.
|
|
1002
|
+
result = { status: 1 };
|
|
1003
|
+
} else {
|
|
665
1004
|
result = safeSpawnSync(mavenCmd, mvnTreeArgs, {
|
|
666
1005
|
cwd: basePath,
|
|
667
1006
|
shell: isWin,
|
|
668
1007
|
});
|
|
1008
|
+
noteBuildToolRateLimit("maven", commandOutputText(result), {
|
|
1009
|
+
command: `${basename(mavenCmd)} ${mvnTreeArgs.join(" ")}`,
|
|
1010
|
+
exitCode:
|
|
1011
|
+
typeof result.status === "number" ? result.status : undefined,
|
|
1012
|
+
});
|
|
1013
|
+
const emptyJson = !readMavenModuleTrees(
|
|
1014
|
+
treeJsonDir,
|
|
1015
|
+
f,
|
|
1016
|
+
).trees.some(
|
|
1017
|
+
(tree) =>
|
|
1018
|
+
parseMavenTreeJson(tree.text, tree.pomFile)?.pkgList?.length,
|
|
1019
|
+
);
|
|
1020
|
+
if (emptyJson) {
|
|
1021
|
+
// Remove the invalid json trees so the text output is
|
|
1022
|
+
// preferred below.
|
|
1023
|
+
safeRmSync(treeJsonDir, { recursive: true, force: true });
|
|
1024
|
+
}
|
|
1025
|
+
if (
|
|
1026
|
+
!isBuildToolRateLimited("maven") &&
|
|
1027
|
+
(result.status !== 0 || result.error || emptyJson)
|
|
1028
|
+
) {
|
|
1029
|
+
mvnTreeArgs = [
|
|
1030
|
+
"dependency:tree",
|
|
1031
|
+
`-DoutputFile=${tempMvnTreeText}`,
|
|
1032
|
+
].concat(addArgs, projectSettings.args);
|
|
1033
|
+
result = safeSpawnSync(mavenCmd, mvnTreeArgs, {
|
|
1034
|
+
cwd: basePath,
|
|
1035
|
+
shell: isWin,
|
|
1036
|
+
});
|
|
1037
|
+
noteBuildToolRateLimit("maven", commandOutputText(result), {
|
|
1038
|
+
command: `${basename(mavenCmd)} ${mvnTreeArgs.join(" ")}`,
|
|
1039
|
+
exitCode:
|
|
1040
|
+
typeof result.status === "number"
|
|
1041
|
+
? result.status
|
|
1042
|
+
: undefined,
|
|
1043
|
+
});
|
|
1044
|
+
}
|
|
669
1045
|
}
|
|
670
1046
|
if (result.status !== 0 || result.error) {
|
|
671
1047
|
possible_misses = true;
|
|
672
1048
|
// Our approach to recursively invoking the maven plugin for each sub-module is bound to result in failures
|
|
673
1049
|
// These could be due to a range of reasons that are covered below.
|
|
674
1050
|
if (
|
|
675
|
-
|
|
676
|
-
DEBUG_MODE ||
|
|
677
|
-
PREFER_MAVEN_DEPS_TREE
|
|
1051
|
+
!isBuildToolRateLimited("maven") &&
|
|
1052
|
+
(pomFiles.length === 1 || DEBUG_MODE || PREFER_MAVEN_DEPS_TREE)
|
|
678
1053
|
) {
|
|
679
1054
|
if (result.stdout) {
|
|
680
1055
|
console.log(result.stdout);
|
|
@@ -767,22 +1142,20 @@ export async function createJavaBom(path, options) {
|
|
|
767
1142
|
thoughtLog(
|
|
768
1143
|
"**MAVEN**: There appear to be build errors, so the SBOM will be incomplete.",
|
|
769
1144
|
);
|
|
770
|
-
|
|
771
|
-
const dlist = pomMap?.dependencies || [];
|
|
772
|
-
if (dlist.length) {
|
|
773
|
-
pkgList = pkgList.concat(dlist);
|
|
774
|
-
}
|
|
1145
|
+
addStaticMavenModule(f);
|
|
775
1146
|
} else {
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
1147
|
+
// The text trees exist only when the JSON run failed and was
|
|
1148
|
+
// retried. Each reactor module has its own tree, and a member
|
|
1149
|
+
// whose tree is here is not run again on its own.
|
|
1150
|
+
let moduleTrees = readMavenModuleTrees(treeTextDir, f);
|
|
1151
|
+
if (!moduleTrees.trees.length) {
|
|
1152
|
+
moduleTrees = readMavenModuleTrees(treeJsonDir, f);
|
|
1153
|
+
}
|
|
1154
|
+
for (const modulePom of moduleTrees.covered) {
|
|
1155
|
+
coveredPomFiles.add(modulePom);
|
|
1156
|
+
}
|
|
1157
|
+
for (const tree of moduleTrees.trees) {
|
|
1158
|
+
const parsedList = parseMavenTree(tree.text, tree.pomFile);
|
|
786
1159
|
const dlist = parsedList.pkgList || [];
|
|
787
1160
|
const tmpParentComponent = dlist.splice(0, 1)[0];
|
|
788
1161
|
if (tmpParentComponent) {
|
|
@@ -1046,8 +1419,7 @@ export async function createJavaBom(path, options) {
|
|
|
1046
1419
|
// `gradle properties` evaluates the project's build scripts, so it is
|
|
1047
1420
|
// part of the dependency-tooling surface gated by --no-install-deps
|
|
1048
1421
|
// (and the pre-build lifecycle).
|
|
1049
|
-
const gradleToolingAllowed =
|
|
1050
|
-
options.installDeps !== false || options.introspect === true;
|
|
1422
|
+
const gradleToolingAllowed = isProjectToolingAllowed(options);
|
|
1051
1423
|
if (gradleToolingAllowed) {
|
|
1052
1424
|
parallelPropTaskOut = executeParallelGradleProperties(
|
|
1053
1425
|
gradleRootPath,
|
|
@@ -1219,6 +1591,14 @@ export async function createJavaBom(path, options) {
|
|
|
1219
1591
|
);
|
|
1220
1592
|
const allOutputs = [];
|
|
1221
1593
|
for (const gradleArg of gradleArguments) {
|
|
1594
|
+
if (isBuildToolRateLimited("gradle")) {
|
|
1595
|
+
if (DEBUG_MODE) {
|
|
1596
|
+
console.log(
|
|
1597
|
+
"Skipping the remaining gradle dependency tasks: the repository rate limited gradle in this scan.",
|
|
1598
|
+
);
|
|
1599
|
+
}
|
|
1600
|
+
break;
|
|
1601
|
+
}
|
|
1222
1602
|
if (DEBUG_MODE) {
|
|
1223
1603
|
console.log(
|
|
1224
1604
|
`Executing ${gradleCmd} with arguments ${gradleArg.join(" ").substring(0, 150)}... in ${gradleRootPath}`,
|
|
@@ -1234,6 +1614,13 @@ export async function createJavaBom(path, options) {
|
|
|
1234
1614
|
cwd: gradleRootPath,
|
|
1235
1615
|
shell: isWin,
|
|
1236
1616
|
});
|
|
1617
|
+
// The dependencies task exits 0 even when resolution failed under a
|
|
1618
|
+
// rate limit, so the output decides, not the status.
|
|
1619
|
+
noteBuildToolRateLimit("gradle", commandOutputText(sresult), {
|
|
1620
|
+
command: `${basename(gradleCmd)} ${finalGradleArg.join(" ")}`,
|
|
1621
|
+
exitCode:
|
|
1622
|
+
typeof sresult.status === "number" ? sresult.status : undefined,
|
|
1623
|
+
});
|
|
1237
1624
|
if (sresult.status !== 0 || sresult.error) {
|
|
1238
1625
|
if (options.failOnError || DEBUG_MODE) {
|
|
1239
1626
|
console.error(sresult.stdout, sresult.stderr);
|
|
@@ -1644,8 +2031,6 @@ export async function createJavaBom(path, options) {
|
|
|
1644
2031
|
`${options.multiProject ? "**/" : ""}build.sbt.lock`,
|
|
1645
2032
|
options,
|
|
1646
2033
|
);
|
|
1647
|
-
const tempCacheDir = safeMkdtempSync(join(getTmpDir(), "sbt-cache-"));
|
|
1648
|
-
safeMkdirSync(tempCacheDir, { recursive: true });
|
|
1649
2034
|
if (
|
|
1650
2035
|
sbtProjects?.length &&
|
|
1651
2036
|
isPackageManagerAllowed(
|
|
@@ -1662,7 +2047,7 @@ export async function createJavaBom(path, options) {
|
|
|
1662
2047
|
pkgList = pkgList.concat(dlist);
|
|
1663
2048
|
}
|
|
1664
2049
|
}
|
|
1665
|
-
} else if (
|
|
2050
|
+
} else if (!isProjectToolingAllowed(options)) {
|
|
1666
2051
|
// Executing sbt evaluates the project's build definition (arbitrary
|
|
1667
2052
|
// Scala), so it belongs to the dependency-tooling surface disabled
|
|
1668
2053
|
// by --no-install-deps and the pre-build lifecycle.
|
|
@@ -1711,20 +2096,46 @@ export async function createJavaBom(path, options) {
|
|
|
1711
2096
|
}
|
|
1712
2097
|
}
|
|
1713
2098
|
safeWriteSync(tempSbtPlugins, sbtPluginDefinition);
|
|
1714
|
-
|
|
2099
|
+
// sbt resolves into the user's own caches, so a project that has been
|
|
2100
|
+
// built resolves without the network. The dependency trees download
|
|
2101
|
+
// the jar of every dependency they list, which is all --deep reads.
|
|
1715
2102
|
const env = { ...process.env };
|
|
1716
|
-
// We need to collect the jars from the cache
|
|
1717
|
-
if (options.deep) {
|
|
1718
|
-
sbtExtraArgs = " updateClassifiers";
|
|
1719
|
-
env["COURSIER_CACHE"] = tempCacheDir;
|
|
1720
|
-
env["SBT_IVY_HOME"] = tempCacheDir;
|
|
1721
|
-
}
|
|
1722
2103
|
for (const i in sbtProjects) {
|
|
2104
|
+
if (isBuildToolRateLimited("sbt")) {
|
|
2105
|
+
// Every remaining sbt build in this scan resolves from the same
|
|
2106
|
+
// rate limited repository.
|
|
2107
|
+
if (DEBUG_MODE) {
|
|
2108
|
+
console.log(
|
|
2109
|
+
`Skipping ${sbtProjects[i]}: the repository rate limited sbt in this scan.`,
|
|
2110
|
+
);
|
|
2111
|
+
}
|
|
2112
|
+
break;
|
|
2113
|
+
}
|
|
1723
2114
|
const basePath = sbtProjects[i];
|
|
1724
2115
|
const dlFile = join(tempDir, `dl-${i}.tmp`);
|
|
2116
|
+
// The sbt version of the project itself decides the invocation
|
|
2117
|
+
// form: sbt 2 both joins separate command-line arguments into a
|
|
2118
|
+
// single command line and no longer offers `dependencyTree / toFile`.
|
|
2119
|
+
const projectSbtVersion = determineSbtVersion(basePath) || sbtVersion;
|
|
2120
|
+
const isSbt2 = isSbt2Version(projectSbtVersion);
|
|
2121
|
+
// The sbt 2 launcher runs a thin client by default. When a server is
|
|
2122
|
+
// already running for the build, an IDE's for example, the client
|
|
2123
|
+
// prints neither the project list nor the trees, so sbt runs
|
|
2124
|
+
// in-process instead. sbt.bat has no `--server` option.
|
|
2125
|
+
const sbtLauncherArgs = isSbt2 && !isWin ? ["--server"] : [];
|
|
2126
|
+
const sbtServerFile = join(
|
|
2127
|
+
basePath,
|
|
2128
|
+
"project",
|
|
2129
|
+
"target",
|
|
2130
|
+
"active.json",
|
|
2131
|
+
);
|
|
2132
|
+
const sbtServerWasRunning = isSbt2 && safeExistsSync(sbtServerFile);
|
|
1725
2133
|
let sbtArgs = [];
|
|
1726
2134
|
let pluginFile = null;
|
|
2135
|
+
let pluginAdded = false;
|
|
1727
2136
|
const subDlFiles = [];
|
|
2137
|
+
const failedSubprojects = [];
|
|
2138
|
+
let subprojects = [];
|
|
1728
2139
|
if (standalonePluginFile) {
|
|
1729
2140
|
sbtArgs = [
|
|
1730
2141
|
`-addPluginSbtFile=${tempSbtPlugins}`,
|
|
@@ -1738,15 +2149,23 @@ export async function createJavaBom(path, options) {
|
|
|
1738
2149
|
// `dependencyTree` fan-out yields a far more complete tree than a
|
|
1739
2150
|
// single root resolution (which only captures the root project's own
|
|
1740
2151
|
// libraryDependencies). See https://github.com/cdxgen/cdxgen/issues/4291
|
|
1741
|
-
|
|
2152
|
+
subprojects = discoverSbtProjectsFromCmd(
|
|
1742
2153
|
basePath,
|
|
1743
2154
|
SBT_CMD,
|
|
1744
2155
|
env,
|
|
2156
|
+
sbtLauncherArgs,
|
|
1745
2157
|
);
|
|
1746
|
-
if (
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
2158
|
+
if (isSbt2) {
|
|
2159
|
+
const trees = subprojects.length
|
|
2160
|
+
? subprojects.map((sp) => ({
|
|
2161
|
+
project: sp,
|
|
2162
|
+
file: join(tempDir, `dl-${i}-${sp}.tmp`),
|
|
2163
|
+
}))
|
|
2164
|
+
: [{ file: dlFile }];
|
|
2165
|
+
subDlFiles.push(...trees.map((t) => t.file));
|
|
2166
|
+
sbtArgs = [sbt2DependencyTreeCommand(trees)];
|
|
2167
|
+
} else if (subprojects.length > 0 && useSlashSyntax) {
|
|
2168
|
+
sbtArgs = ["'set ThisBuild / asciiGraphWidth := 800'"];
|
|
1750
2169
|
for (const sp of subprojects) {
|
|
1751
2170
|
const subDlFile = join(tempDir, `dl-${i}-${sp}.tmp`);
|
|
1752
2171
|
subDlFiles.push(subDlFile);
|
|
@@ -1756,21 +2175,27 @@ export async function createJavaBom(path, options) {
|
|
|
1756
2175
|
}
|
|
1757
2176
|
} else if (useSlashSyntax) {
|
|
1758
2177
|
sbtArgs = [
|
|
1759
|
-
`'set ThisBuild / asciiGraphWidth := 800'
|
|
2178
|
+
`'set ThisBuild / asciiGraphWidth := 800' "dependencyTree / toFile ${dlFile} --force"`,
|
|
1760
2179
|
];
|
|
1761
2180
|
subDlFiles.push(dlFile);
|
|
1762
2181
|
} else {
|
|
1763
2182
|
sbtArgs = [
|
|
1764
|
-
`'set asciiGraphWidth in ThisBuild := 800'
|
|
2183
|
+
`'set asciiGraphWidth in ThisBuild := 800' "dependencyTree::toFile ${dlFile} --force"`,
|
|
1765
2184
|
];
|
|
1766
2185
|
subDlFiles.push(dlFile);
|
|
1767
2186
|
}
|
|
1768
|
-
|
|
2187
|
+
// sbt 2 enables its dependency tree plugin by default, so the
|
|
2188
|
+
// build's plugins.sbt stays untouched and the build definition is
|
|
2189
|
+
// not recompiled for the scan.
|
|
2190
|
+
if (!isSbt2) {
|
|
2191
|
+
pluginFile = addPlugin(basePath, sbtPluginDefinition);
|
|
2192
|
+
pluginAdded = true;
|
|
2193
|
+
}
|
|
1769
2194
|
}
|
|
1770
2195
|
// Run sbt in non-interactive batch mode so it never blocks waiting for
|
|
1771
2196
|
// input on stdin (which can otherwise appear as an indefinite hang) and
|
|
1772
2197
|
// does not emit ANSI colour codes into the dependency tree output.
|
|
1773
|
-
sbtArgs = ["-batch", "-no-colors", ...sbtArgs];
|
|
2198
|
+
sbtArgs = [...sbtLauncherArgs, "-batch", "-no-colors", ...sbtArgs];
|
|
1774
2199
|
console.log(
|
|
1775
2200
|
"Executing",
|
|
1776
2201
|
SBT_CMD,
|
|
@@ -1781,16 +2206,31 @@ export async function createJavaBom(path, options) {
|
|
|
1781
2206
|
tempSbtgDir,
|
|
1782
2207
|
);
|
|
1783
2208
|
// Note that the command has to be invoked with `shell: true` to properly execut sbt
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
2209
|
+
// sbt 2 is the exception: its ;-joined command argument contains a
|
|
2210
|
+
// semicolon, so on POSIX the launcher is spawned directly and the
|
|
2211
|
+
// argument reaches sbt verbatim.
|
|
2212
|
+
const result = sbtSpawnSync(
|
|
2213
|
+
SBT_CMD,
|
|
2214
|
+
sbtArgs,
|
|
2215
|
+
{
|
|
2216
|
+
cwd: basePath,
|
|
2217
|
+
shell: isWin || !isSbt2 || standalonePluginFile,
|
|
2218
|
+
// The sbt commands below embed their own shell quoting, which the
|
|
2219
|
+
// shell must strip for sbt to see separate commands.
|
|
2220
|
+
cdxgenRawShellArgs: true,
|
|
2221
|
+
env,
|
|
2222
|
+
},
|
|
2223
|
+
!isSbt2 || standalonePluginFile,
|
|
2224
|
+
);
|
|
2225
|
+
noteBuildToolRateLimit("sbt", commandOutputText(result), {
|
|
2226
|
+
command: `${SBT_CMD} ${sbtArgs.join(" ")}`,
|
|
2227
|
+
exitCode:
|
|
2228
|
+
typeof result.status === "number" ? result.status : undefined,
|
|
1791
2229
|
});
|
|
1792
2230
|
if (result.status !== 0 || result.error) {
|
|
1793
|
-
|
|
2231
|
+
if (!isBuildToolRateLimited("sbt")) {
|
|
2232
|
+
console.error(result.stdout, result.stderr);
|
|
2233
|
+
}
|
|
1794
2234
|
recordDegradation("jvm.sbt.no-lockfile", {
|
|
1795
2235
|
ecosystem: "java",
|
|
1796
2236
|
tool: "sbt",
|
|
@@ -1801,15 +2241,20 @@ export async function createJavaBom(path, options) {
|
|
|
1801
2241
|
detail:
|
|
1802
2242
|
"The sbt dependencyTree command did not succeed, so the declared sbt dependencies are the only evidence available.",
|
|
1803
2243
|
});
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
2244
|
+
if (!isBuildToolRateLimited("sbt")) {
|
|
2245
|
+
console.log(
|
|
2246
|
+
"1. Check if scala and sbt is installed and available in PATH. Only scala 2.10 + sbt 0.13.6+, 2.12 + sbt 1.0+ and sbt 2.x are supported for now.",
|
|
2247
|
+
);
|
|
2248
|
+
// sbt 2 brings its own dependency tree, so no plugin is added.
|
|
2249
|
+
if (!isSbt2) {
|
|
2250
|
+
console.log(
|
|
2251
|
+
"2. Check if the plugin net.virtual-void:sbt-dependency-graph 0.10.0-RC1 can be used in the environment",
|
|
2252
|
+
);
|
|
2253
|
+
}
|
|
2254
|
+
console.log(
|
|
2255
|
+
`${isSbt2 ? 2 : 3}. Consider creating a lockfile using sbt-dependency-lock plugin. See https://github.com/stringbean/sbt-dependency-lock`,
|
|
2256
|
+
);
|
|
2257
|
+
}
|
|
1813
2258
|
deferFailOnError(options, {
|
|
1814
2259
|
ecosystem: "java",
|
|
1815
2260
|
tool: "sbt",
|
|
@@ -1819,13 +2264,95 @@ export async function createJavaBom(path, options) {
|
|
|
1819
2264
|
detail: "the sbt dependencyTree command failed",
|
|
1820
2265
|
});
|
|
1821
2266
|
}
|
|
1822
|
-
|
|
2267
|
+
// One failing subproject (for example an unresolvable SNAPSHOT)
|
|
2268
|
+
// fails the combined command and would zero the whole SBOM. Retry
|
|
2269
|
+
// each subproject on its own and keep the trees that succeed. A rate
|
|
2270
|
+
// limited repository ends the retries as well: each would download
|
|
2271
|
+
// the same artifacts against the same 429.
|
|
2272
|
+
if (
|
|
2273
|
+
!standalonePluginFile &&
|
|
2274
|
+
subprojects.length > 0 &&
|
|
2275
|
+
(result.status !== 0 || result.error) &&
|
|
2276
|
+
!isBuildToolRateLimited("sbt")
|
|
2277
|
+
) {
|
|
2278
|
+
for (const sp of subprojects) {
|
|
2279
|
+
const subDlFile = join(tempDir, `dl-${i}-${sp}.tmp`);
|
|
2280
|
+
if (safeExistsSync(subDlFile)) {
|
|
2281
|
+
// The combined run produced this tree before it failed.
|
|
2282
|
+
continue;
|
|
2283
|
+
}
|
|
2284
|
+
if (DEBUG_MODE) {
|
|
2285
|
+
console.log(`Retrying the sbt tree of ${sp} on its own.`);
|
|
2286
|
+
}
|
|
2287
|
+
let retryArgs;
|
|
2288
|
+
if (isSbt2) {
|
|
2289
|
+
retryArgs = [
|
|
2290
|
+
...sbtLauncherArgs,
|
|
2291
|
+
"-batch",
|
|
2292
|
+
"-no-colors",
|
|
2293
|
+
sbt2DependencyTreeCommand([{ project: sp, file: subDlFile }]),
|
|
2294
|
+
];
|
|
2295
|
+
} else if (useSlashSyntax) {
|
|
2296
|
+
retryArgs = [
|
|
2297
|
+
"-batch",
|
|
2298
|
+
"-no-colors",
|
|
2299
|
+
"'set ThisBuild / asciiGraphWidth := 800'",
|
|
2300
|
+
`"${sp}/dependencyTree / toFile ${subDlFile} --force"`,
|
|
2301
|
+
];
|
|
2302
|
+
} else {
|
|
2303
|
+
continue;
|
|
2304
|
+
}
|
|
2305
|
+
sbtSpawnSync(
|
|
2306
|
+
SBT_CMD,
|
|
2307
|
+
retryArgs,
|
|
2308
|
+
{
|
|
2309
|
+
cwd: basePath,
|
|
2310
|
+
shell: isWin || !isSbt2,
|
|
2311
|
+
cdxgenRawShellArgs: true,
|
|
2312
|
+
env,
|
|
2313
|
+
},
|
|
2314
|
+
!isSbt2,
|
|
2315
|
+
);
|
|
2316
|
+
// The tree file is already listed for parsing, so a retry only
|
|
2317
|
+
// has to produce it.
|
|
2318
|
+
if (!safeExistsSync(subDlFile)) {
|
|
2319
|
+
failedSubprojects.push(sp);
|
|
2320
|
+
}
|
|
2321
|
+
}
|
|
2322
|
+
if (failedSubprojects.length) {
|
|
2323
|
+
console.log(
|
|
2324
|
+
`The sbt dependency trees of ${failedSubprojects.join(", ")} could not be resolved.`,
|
|
2325
|
+
);
|
|
2326
|
+
}
|
|
2327
|
+
}
|
|
2328
|
+
// Where the thin client still runs, it leaves the server it started
|
|
2329
|
+
// behind. A server that was running before the scan is not ours to
|
|
2330
|
+
// stop.
|
|
2331
|
+
if (isSbt2 && !sbtServerWasRunning && safeExistsSync(sbtServerFile)) {
|
|
2332
|
+
sbtSpawnSync(SBT_CMD, ["-batch", "-no-colors", "shutdown"], {
|
|
2333
|
+
cwd: basePath,
|
|
2334
|
+
shell: isWin,
|
|
2335
|
+
env,
|
|
2336
|
+
});
|
|
2337
|
+
}
|
|
2338
|
+
// Only a plugins.sbt cdxgen amended is restored: without a backup,
|
|
2339
|
+
// the cleanup removes the file as one cdxgen created.
|
|
2340
|
+
if (pluginAdded) {
|
|
1823
2341
|
cleanupPlugin(basePath, pluginFile);
|
|
1824
2342
|
}
|
|
1825
2343
|
const sbtSubprojectRoots = [];
|
|
1826
2344
|
for (const subDlFile of subDlFiles) {
|
|
1827
2345
|
if (safeExistsSync(subDlFile)) {
|
|
1828
|
-
|
|
2346
|
+
// The trees are temporary files that go away with the scan, so
|
|
2347
|
+
// the components name the build definition they were resolved
|
|
2348
|
+
// from instead: build.sbt, or for a build written only in
|
|
2349
|
+
// project/*.scala, the build.properties beside it.
|
|
2350
|
+
const sbtBuildFile =
|
|
2351
|
+
[
|
|
2352
|
+
join(basePath, "build.sbt"),
|
|
2353
|
+
join(basePath, "project", "build.properties"),
|
|
2354
|
+
].find((f) => safeExistsSync(f)) || basePath;
|
|
2355
|
+
const retMap = await parseSbtTree(subDlFile, sbtBuildFile);
|
|
1829
2356
|
if (retMap.pkgList?.length) {
|
|
1830
2357
|
const tmpParentComponent = retMap.pkgList.splice(0, 1)[0];
|
|
1831
2358
|
tmpParentComponent.type = "application";
|
|
@@ -1873,19 +2400,38 @@ export async function createJavaBom(path, options) {
|
|
|
1873
2400
|
"bom-ref": decodeURIComponent(rootPurl),
|
|
1874
2401
|
};
|
|
1875
2402
|
parentComponent = rootComponent;
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
2403
|
+
// Merged rather than appended, so the root lists every subproject
|
|
2404
|
+
// once, and a root project with sources of its own, whose tree is
|
|
2405
|
+
// the synthetic root itself, neither depends on itself nor gets a
|
|
2406
|
+
// second dependency entry.
|
|
2407
|
+
dependencies = mergeDependencies(
|
|
2408
|
+
dependencies,
|
|
2409
|
+
[
|
|
2410
|
+
{
|
|
2411
|
+
ref: rootComponent["bom-ref"],
|
|
2412
|
+
dependsOn: sbtSubprojectRoots
|
|
2413
|
+
.map((c) => c["bom-ref"])
|
|
2414
|
+
.filter(Boolean),
|
|
2415
|
+
},
|
|
2416
|
+
],
|
|
2417
|
+
rootComponent,
|
|
2418
|
+
);
|
|
1882
2419
|
} else if (sbtSubprojectRoots.length > 0 && !parentComponent) {
|
|
1883
2420
|
parentComponent = sbtSubprojectRoots[0];
|
|
1884
2421
|
}
|
|
2422
|
+
if (failedSubprojects.length && parentComponent?.name) {
|
|
2423
|
+
parentComponent.properties ??= [];
|
|
2424
|
+
parentComponent.properties.push({
|
|
2425
|
+
name: "cdx:sbt:failedProjects",
|
|
2426
|
+
value: failedSubprojects.join(", "),
|
|
2427
|
+
});
|
|
2428
|
+
}
|
|
1885
2429
|
}
|
|
1886
2430
|
|
|
1887
|
-
//
|
|
1888
|
-
|
|
2431
|
+
// The trees were parsed inside the loop, so neither directory is
|
|
2432
|
+
// needed any more.
|
|
2433
|
+
safeRmSync(tempDir, { recursive: true, force: true });
|
|
2434
|
+
safeRmSync(tempSbtgDir, { recursive: true, force: true });
|
|
1889
2435
|
} // else
|
|
1890
2436
|
|
|
1891
2437
|
if (DEBUG_MODE) {
|
|
@@ -1893,7 +2439,10 @@ export async function createJavaBom(path, options) {
|
|
|
1893
2439
|
}
|
|
1894
2440
|
// Should we attempt to resolve class names
|
|
1895
2441
|
if (options.resolveClass || options.deep) {
|
|
1896
|
-
const tmpjarNSMapping = await
|
|
2442
|
+
const tmpjarNSMapping = await collectSbtDependencyJars(
|
|
2443
|
+
pkgList,
|
|
2444
|
+
jarNSMapping,
|
|
2445
|
+
);
|
|
1897
2446
|
if (tmpjarNSMapping && Object.keys(tmpjarNSMapping).length) {
|
|
1898
2447
|
jarNSMapping = { ...jarNSMapping, ...tmpjarNSMapping };
|
|
1899
2448
|
}
|
|
@@ -1917,16 +2466,10 @@ export async function createJavaBom(path, options) {
|
|
|
1917
2466
|
}
|
|
1918
2467
|
}
|
|
1919
2468
|
}
|
|
1920
|
-
if (tempCacheDir?.startsWith(getTmpDir())) {
|
|
1921
|
-
safeRmSync(tempCacheDir, {
|
|
1922
|
-
recursive: true,
|
|
1923
|
-
force: true,
|
|
1924
|
-
});
|
|
1925
|
-
}
|
|
1926
2469
|
|
|
1927
2470
|
if (
|
|
1928
2471
|
millFiles?.length &&
|
|
1929
|
-
(options
|
|
2472
|
+
isProjectToolingAllowed(options) &&
|
|
1930
2473
|
isPackageManagerAllowed(
|
|
1931
2474
|
"mill",
|
|
1932
2475
|
["bazel", "sbt", "gradle", "maven"],
|
|
@@ -1940,6 +2483,12 @@ export async function createJavaBom(path, options) {
|
|
|
1940
2483
|
options,
|
|
1941
2484
|
);
|
|
1942
2485
|
const millCmd = getMillCommand(millRootPath, options);
|
|
2486
|
+
// Mill 1.0 replaced the ivyDepsTree task with showMvnDepsTree
|
|
2487
|
+
const millVersion = getMillVersion(millRootPath, millCmd);
|
|
2488
|
+
const millTreeTask =
|
|
2489
|
+
millVersion != null && gte(millVersion, "1.0.0")
|
|
2490
|
+
? "showMvnDepsTree"
|
|
2491
|
+
: "ivyDepsTree";
|
|
1943
2492
|
const millCommonArgs = [
|
|
1944
2493
|
"--color",
|
|
1945
2494
|
"false",
|
|
@@ -1951,7 +2500,7 @@ export async function createJavaBom(path, options) {
|
|
|
1951
2500
|
if (!["true", "1"].includes(readEnvironmentVariable("MILL_USE_SERVER"))) {
|
|
1952
2501
|
millCommonArgs.unshift("--no-server");
|
|
1953
2502
|
}
|
|
1954
|
-
const millArgs = [...millCommonArgs,
|
|
2503
|
+
const millArgs = [...millCommonArgs, `__.${millTreeTask}`];
|
|
1955
2504
|
if (DEBUG_MODE) {
|
|
1956
2505
|
console.log("Executing", millCmd, "in", millRootPath);
|
|
1957
2506
|
}
|
|
@@ -1960,6 +2509,11 @@ export async function createJavaBom(path, options) {
|
|
|
1960
2509
|
shell: isWin,
|
|
1961
2510
|
maxBuffer: MAX_BUFFER * 10,
|
|
1962
2511
|
});
|
|
2512
|
+
noteBuildToolRateLimit("mill", commandOutputText(sresult), {
|
|
2513
|
+
command: `${basename(millCmd)} ${millArgs.join(" ")}`,
|
|
2514
|
+
exitCode:
|
|
2515
|
+
typeof sresult.status === "number" ? sresult.status : undefined,
|
|
2516
|
+
});
|
|
1963
2517
|
if (sresult.status !== 0 || sresult.error) {
|
|
1964
2518
|
if (options.failOnError || DEBUG_MODE) {
|
|
1965
2519
|
console.error(sresult.stdout, sresult.stderr);
|
|
@@ -1969,10 +2523,14 @@ export async function createJavaBom(path, options) {
|
|
|
1969
2523
|
tool: "mill",
|
|
1970
2524
|
exitCode:
|
|
1971
2525
|
typeof sresult.status === "number" ? sresult.status : undefined,
|
|
1972
|
-
detail: "the mill
|
|
2526
|
+
detail: "the mill dependency tree command failed",
|
|
1973
2527
|
});
|
|
1974
2528
|
}
|
|
1975
|
-
const millResolveArgs = [
|
|
2529
|
+
const millResolveArgs = [
|
|
2530
|
+
...millCommonArgs,
|
|
2531
|
+
"resolve",
|
|
2532
|
+
`__.${millTreeTask}`,
|
|
2533
|
+
];
|
|
1976
2534
|
if (DEBUG_MODE) {
|
|
1977
2535
|
console.log(
|
|
1978
2536
|
"Executing",
|
|
@@ -1982,6 +2540,9 @@ export async function createJavaBom(path, options) {
|
|
|
1982
2540
|
millRootPath,
|
|
1983
2541
|
);
|
|
1984
2542
|
}
|
|
2543
|
+
// The resolve run only lists the build's tasks and downloads nothing,
|
|
2544
|
+
// so it runs even after a rate limit: the trees of the modules that did
|
|
2545
|
+
// resolve under --keep-going are read through it.
|
|
1985
2546
|
sresult = safeSpawnSync(millCmd, millResolveArgs, {
|
|
1986
2547
|
cwd: millRootPath,
|
|
1987
2548
|
shell: isWin,
|
|
@@ -2004,6 +2565,8 @@ export async function createJavaBom(path, options) {
|
|
|
2004
2565
|
const modules = sstdout
|
|
2005
2566
|
.trim()
|
|
2006
2567
|
.split("\n")
|
|
2568
|
+
// Mill 1.x prints headers around the module list
|
|
2569
|
+
.filter((a) => a.endsWith(`.${millTreeTask}`))
|
|
2007
2570
|
.map((a) => a.substring(0, a.lastIndexOf(".")))
|
|
2008
2571
|
.filter((a) =>
|
|
2009
2572
|
["true", "1"].includes(readEnvironmentVariable("MILL_EXCLUDE_TEST"))
|
|
@@ -2016,7 +2579,13 @@ export async function createJavaBom(path, options) {
|
|
|
2016
2579
|
relations.set(parentComponent["bom-ref"], []);
|
|
2017
2580
|
for (const module of modules) {
|
|
2018
2581
|
moduleBomRefs.push(
|
|
2019
|
-
parseMillDependency(
|
|
2582
|
+
parseMillDependency(
|
|
2583
|
+
module,
|
|
2584
|
+
packages,
|
|
2585
|
+
relations,
|
|
2586
|
+
millRootPath,
|
|
2587
|
+
`${millTreeTask}.log`,
|
|
2588
|
+
),
|
|
2020
2589
|
);
|
|
2021
2590
|
}
|
|
2022
2591
|
for (const module of moduleBomRefs) {
|
|
@@ -2069,6 +2638,34 @@ export async function createJavaBom(path, options) {
|
|
|
2069
2638
|
}
|
|
2070
2639
|
}
|
|
2071
2640
|
}
|
|
2641
|
+
// scala-cli projects declare everything with //> using directives in
|
|
2642
|
+
// their sources and have none of the build files handled above.
|
|
2643
|
+
if (
|
|
2644
|
+
!pomFiles?.length &&
|
|
2645
|
+
!gradleFiles?.length &&
|
|
2646
|
+
!millFiles?.length &&
|
|
2647
|
+
!sbtProjects.length &&
|
|
2648
|
+
isPackageManagerAllowed(
|
|
2649
|
+
"scala-cli",
|
|
2650
|
+
["bazel", "sbt", "gradle", "maven", "mill"],
|
|
2651
|
+
options,
|
|
2652
|
+
)
|
|
2653
|
+
) {
|
|
2654
|
+
const dlist = collectScalaCliComponents(path, options);
|
|
2655
|
+
if (dlist?.length) {
|
|
2656
|
+
thoughtLog(
|
|
2657
|
+
`**SCALA-CLI**: Found ${dlist.length} declared dependencies.`,
|
|
2658
|
+
);
|
|
2659
|
+
pkgList = pkgList.concat(dlist);
|
|
2660
|
+
if (!parentComponent?.name) {
|
|
2661
|
+
parentComponent = createDefaultParentComponent(
|
|
2662
|
+
path,
|
|
2663
|
+
"maven",
|
|
2664
|
+
options,
|
|
2665
|
+
);
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2668
|
+
}
|
|
2072
2669
|
} catch (err) {
|
|
2073
2670
|
if (!isDeferredFailOnError(err)) {
|
|
2074
2671
|
throw err;
|
|
@@ -2078,11 +2675,47 @@ export async function createJavaBom(path, options) {
|
|
|
2078
2675
|
thoughtLog(
|
|
2079
2676
|
"Completing the java BOM with the components collected before the deferred failure.",
|
|
2080
2677
|
);
|
|
2678
|
+
} finally {
|
|
2679
|
+
// A build this scan ran can have started a Mill daemon of its own
|
|
2680
|
+
// without --no-server; stop those even when a later step failed.
|
|
2681
|
+
stopMillDaemons(path, millDaemonsBefore, options);
|
|
2682
|
+
}
|
|
2683
|
+
// A Scala.js build bundles npm packages: scalajs-bundler installs them
|
|
2684
|
+
// under target, and a bundler workspace keeps its lock file beside the
|
|
2685
|
+
// build. Their npm components join the BOM so module imports can be
|
|
2686
|
+
// attributed to them, and the project depends on the workspaces' direct
|
|
2687
|
+
// dependencies.
|
|
2688
|
+
try {
|
|
2689
|
+
const scalajsNpm = await collectScalaJsNpmComponents(
|
|
2690
|
+
path,
|
|
2691
|
+
pkgList,
|
|
2692
|
+
options,
|
|
2693
|
+
);
|
|
2694
|
+
if (scalajsNpm.components.length) {
|
|
2695
|
+
pkgList = pkgList.concat(scalajsNpm.components);
|
|
2696
|
+
const npmEdges = [...scalajsNpm.dependencies];
|
|
2697
|
+
if (parentComponent?.["bom-ref"] && scalajsNpm.roots.length) {
|
|
2698
|
+
npmEdges.push({
|
|
2699
|
+
ref: parentComponent["bom-ref"],
|
|
2700
|
+
dependsOn: scalajsNpm.roots,
|
|
2701
|
+
});
|
|
2702
|
+
}
|
|
2703
|
+
dependencies = mergeDependencies(dependencies, npmEdges, parentComponent);
|
|
2704
|
+
}
|
|
2705
|
+
} catch (err) {
|
|
2706
|
+
if (DEBUG_MODE) {
|
|
2707
|
+
console.log("Unable to collect the Scala.js npm dependencies", err);
|
|
2708
|
+
}
|
|
2081
2709
|
}
|
|
2082
2710
|
// Deduplication and metadata belong to whatever was collected, so they run
|
|
2083
2711
|
// on the deferred path too.
|
|
2084
2712
|
pkgList = trimComponents(pkgList);
|
|
2085
|
-
|
|
2713
|
+
// --deep collects namespaces; it does not opt in to remote licence lookups,
|
|
2714
|
+
// which FETCH_LICENSE controls. The project's own modules are never looked
|
|
2715
|
+
// up remotely: they are not published.
|
|
2716
|
+
pkgList = await getMvnMetadata(pkgList, jarNSMapping, false, {
|
|
2717
|
+
skipPurls: ownComponentRefs(parentComponent),
|
|
2718
|
+
});
|
|
2086
2719
|
return buildBomNSData(options, pkgList, "maven", {
|
|
2087
2720
|
src: path,
|
|
2088
2721
|
nsMapping: jarNSMapping,
|