From e1a026ac3412dca98a6c6be583272fdb3ca2e43b Mon Sep 17 00:00:00 2001 From: Sebastian Schuberth Date: Mon, 20 Jan 2025 15:55:10 +0100 Subject: [PATCH] feat(node): Keep created `node_modules` directories until after resolution This fixes the case where a Gradle analysis (that is configured to run after e.g. a Yarn analysis) needs to access Gradle projects inside the `node_modules` directory created by a React-Native build. Signed-off-by: Sebastian Schuberth --- .../node/src/main/kotlin/npm/Npm.kt | 22 ++++++++++------- .../node/src/main/kotlin/pnpm/Pnpm.kt | 22 ++++++++++------- .../node/src/main/kotlin/yarn/Yarn.kt | 24 ++++++++++++------- .../common/src/main/kotlin/DirectoryStash.kt | 2 +- 4 files changed, 44 insertions(+), 26 deletions(-) diff --git a/plugins/package-managers/node/src/main/kotlin/npm/Npm.kt b/plugins/package-managers/node/src/main/kotlin/npm/Npm.kt index 42a064e5a74d4..79fbb653c95f8 100644 --- a/plugins/package-managers/node/src/main/kotlin/npm/Npm.kt +++ b/plugins/package-managers/node/src/main/kotlin/npm/Npm.kt @@ -38,10 +38,10 @@ import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManagerType import org.ossreviewtoolkit.plugins.packagemanagers.node.PackageJson import org.ossreviewtoolkit.plugins.packagemanagers.node.parsePackageJson import org.ossreviewtoolkit.utils.common.CommandLineTool +import org.ossreviewtoolkit.utils.common.DirectoryStash import org.ossreviewtoolkit.utils.common.Os import org.ossreviewtoolkit.utils.common.ProcessCapture import org.ossreviewtoolkit.utils.common.collectMessages -import org.ossreviewtoolkit.utils.common.stashDirectories import org.ossreviewtoolkit.utils.common.withoutPrefix import org.semver4j.RangesList @@ -83,18 +83,26 @@ class Npm( ) = Npm(type, analysisRoot, analyzerConfig, repoConfig) } + private lateinit var stash: DirectoryStash + private val legacyPeerDeps = options[OPTION_LEGACY_PEER_DEPS].toBoolean() private val npmViewCache = mutableMapOf() private val handler = NpmDependencyHandler(projectType, this::getRemotePackageDetails) override val graphBuilder by lazy { DependencyGraphBuilder(handler) } - override fun resolveDependencies(definitionFile: File, labels: Map): List = - stashDirectories(definitionFile.resolveSibling("node_modules")).use { - resolveDependencies(definitionFile) - } + override fun beforeResolution(definitionFiles: List) { + NpmCommand.checkVersion() + + val directories = definitionFiles.mapTo(mutableSetOf()) { it.resolveSibling("node_modules") } + stash = DirectoryStash(directories) + } - private fun resolveDependencies(definitionFile: File): List { + override fun afterResolution(definitionFiles: List) { + stash.close() + } + + override fun resolveDependencies(definitionFile: File, labels: Map): List { val workingDir = definitionFile.parentFile val issues = installDependencies(workingDir).toMutableList() @@ -129,8 +137,6 @@ class Npm( ).let { listOf(it) } } - override fun beforeResolution(definitionFiles: List) = NpmCommand.checkVersion() - private fun listModules(workingDir: File, issues: MutableList): ModuleInfo { val listProcess = NpmCommand.run(workingDir, "list", "--depth", "Infinity", "--json", "--long") issues += listProcess.extractNpmIssues() diff --git a/plugins/package-managers/node/src/main/kotlin/pnpm/Pnpm.kt b/plugins/package-managers/node/src/main/kotlin/pnpm/Pnpm.kt index a6b3c0755d9de..cf01c1cc0cfcb 100644 --- a/plugins/package-managers/node/src/main/kotlin/pnpm/Pnpm.kt +++ b/plugins/package-managers/node/src/main/kotlin/pnpm/Pnpm.kt @@ -33,8 +33,8 @@ import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManagerType import org.ossreviewtoolkit.plugins.packagemanagers.node.PackageJson import org.ossreviewtoolkit.plugins.packagemanagers.node.parsePackageJson import org.ossreviewtoolkit.utils.common.CommandLineTool +import org.ossreviewtoolkit.utils.common.DirectoryStash import org.ossreviewtoolkit.utils.common.Os -import org.ossreviewtoolkit.utils.common.stashDirectories import org.semver4j.RangesList import org.semver4j.RangesListFactory @@ -64,17 +64,25 @@ class Pnpm( ) = Pnpm(type, analysisRoot, analyzerConfig, repoConfig) } + private lateinit var stash: DirectoryStash + private val packageDetailsCache = mutableMapOf() private val handler = PnpmDependencyHandler(projectType, this::getRemotePackageDetails) override val graphBuilder by lazy { DependencyGraphBuilder(handler) } - override fun resolveDependencies(definitionFile: File, labels: Map): List = - stashDirectories(definitionFile.resolveSibling("node_modules")).use { - resolveDependencies(definitionFile) - } + override fun beforeResolution(definitionFiles: List) { + PnpmCommand.checkVersion() + + val directories = definitionFiles.mapTo(mutableSetOf()) { it.resolveSibling("node_modules") } + stash = DirectoryStash(directories) + } - private fun resolveDependencies(definitionFile: File): List { + override fun afterResolution(definitionFiles: List) { + stash.close() + } + + override fun resolveDependencies(definitionFile: File, labels: Map): List { val workingDir = definitionFile.parentFile installDependencies(workingDir) @@ -133,8 +141,6 @@ class Pnpm( workingDir = workingDir ).requireSuccess() - override fun beforeResolution(definitionFiles: List) = PnpmCommand.checkVersion() - internal fun getRemotePackageDetails(packageName: String): PackageJson? { packageDetailsCache[packageName]?.let { return it } diff --git a/plugins/package-managers/node/src/main/kotlin/yarn/Yarn.kt b/plugins/package-managers/node/src/main/kotlin/yarn/Yarn.kt index 929e6e6bcdf9c..e751a42b1fac8 100644 --- a/plugins/package-managers/node/src/main/kotlin/yarn/Yarn.kt +++ b/plugins/package-managers/node/src/main/kotlin/yarn/Yarn.kt @@ -51,6 +51,7 @@ import org.ossreviewtoolkit.plugins.packagemanagers.node.PackageJson import org.ossreviewtoolkit.plugins.packagemanagers.node.parsePackageJson import org.ossreviewtoolkit.plugins.packagemanagers.node.splitNamespaceAndName import org.ossreviewtoolkit.utils.common.CommandLineTool +import org.ossreviewtoolkit.utils.common.DirectoryStash import org.ossreviewtoolkit.utils.common.DiskCache import org.ossreviewtoolkit.utils.common.Os import org.ossreviewtoolkit.utils.common.alsoIfNull @@ -59,7 +60,6 @@ import org.ossreviewtoolkit.utils.common.fieldNamesOrEmpty import org.ossreviewtoolkit.utils.common.isSymbolicLink import org.ossreviewtoolkit.utils.common.mebibytes import org.ossreviewtoolkit.utils.common.realFile -import org.ossreviewtoolkit.utils.common.stashDirectories import org.ossreviewtoolkit.utils.common.textValueOrEmpty import org.ossreviewtoolkit.utils.ort.ortDataDirectory @@ -106,6 +106,8 @@ open class Yarn( ) = Yarn(type, analysisRoot, analyzerConfig, repoConfig) } + private lateinit var stash: DirectoryStash + /** Cache for submodules identified by its moduleDir absolutePath */ private val submodulesCache = ConcurrentHashMap>() @@ -131,19 +133,23 @@ open class Yarn( } } - override fun beforeResolution(definitionFiles: List) = YarnCommand.checkVersion() + override fun beforeResolution(definitionFiles: List) { + YarnCommand.checkVersion() - override fun resolveDependencies(definitionFile: File, labels: Map): List { - val workingDir = definitionFile.parentFile + val directories = definitionFiles.mapTo(mutableSetOf()) { it.resolveSibling("node_modules") } + stash = DirectoryStash(directories) + } - return try { - stashDirectories(workingDir.resolve("node_modules")).use { - resolveDependenciesInternal(definitionFile) - } + override fun afterResolution(definitionFiles: List) { + stash.close() + } + + override fun resolveDependencies(definitionFile: File, labels: Map): List = + try { + resolveDependenciesInternal(definitionFile) } finally { rawModuleInfoCache.clear() } - } /** * An internally used data class with information about a module retrieved from the module's package.json. This diff --git a/utils/common/src/main/kotlin/DirectoryStash.kt b/utils/common/src/main/kotlin/DirectoryStash.kt index fa1458784c981..16ea815967f1a 100644 --- a/utils/common/src/main/kotlin/DirectoryStash.kt +++ b/utils/common/src/main/kotlin/DirectoryStash.kt @@ -41,7 +41,7 @@ fun stashDirectories(vararg directories: File): Closeable = DirectoryStash(setOf * created at the location of an original directory is deleted before the original state is restored. If a specified * directory did not exist on initialization, it will also not exist on close. */ -private class DirectoryStash(directories: Set) : Closeable { +class DirectoryStash(directories: Set) : Closeable { private val stashedDirectories: Map = directories.associateWith { originalDir -> // Check this on each iteration instead of filtering beforehand to properly handle parent / child directories. if (originalDir.isDirectory) {