Skip to content

Commit

Permalink
refactor(bower): Migrate to the dependency graph API
Browse files Browse the repository at this point in the history
Signed-off-by: Sebastian Schuberth <[email protected]>
  • Loading branch information
sschuberth committed Dec 12, 2024
1 parent 69ace3b commit fe2776e
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class BowerFunTest : StringSpec({
val definitionFile = getAssetFile("projects/synthetic/bower/bower.json")
val expectedResultFile = getAssetFile("projects/synthetic/bower-expected-output.yml")

val result = create("Bower").resolveSingleProject(definitionFile)
val result = create("Bower").resolveSingleProject(definitionFile, resolveScopes = true)

result.toYaml() should matchExpectedResult(expectedResultFile, definitionFile)
}
Expand Down
108 changes: 20 additions & 88 deletions plugins/package-managers/bower/src/main/kotlin/Bower.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,11 @@ import java.util.LinkedList

import org.ossreviewtoolkit.analyzer.AbstractPackageManagerFactory
import org.ossreviewtoolkit.analyzer.PackageManager
import org.ossreviewtoolkit.analyzer.PackageManager.Companion.processProjectVcs
import org.ossreviewtoolkit.analyzer.parseAuthorString
import org.ossreviewtoolkit.downloader.VersionControlSystem
import org.ossreviewtoolkit.model.Identifier
import org.ossreviewtoolkit.model.Package
import org.ossreviewtoolkit.model.PackageReference
import org.ossreviewtoolkit.model.Project
import org.ossreviewtoolkit.analyzer.PackageManagerResult
import org.ossreviewtoolkit.model.ProjectAnalyzerResult
import org.ossreviewtoolkit.model.RemoteArtifact
import org.ossreviewtoolkit.model.Scope
import org.ossreviewtoolkit.model.VcsInfo
import org.ossreviewtoolkit.model.VcsType
import org.ossreviewtoolkit.model.collectDependencies
import org.ossreviewtoolkit.model.config.AnalyzerConfiguration
import org.ossreviewtoolkit.model.config.RepositoryConfiguration
import org.ossreviewtoolkit.model.utils.DependencyGraphBuilder
import org.ossreviewtoolkit.utils.common.CommandLineTool
import org.ossreviewtoolkit.utils.common.Os
import org.ossreviewtoolkit.utils.common.stashDirectories
Expand Down Expand Up @@ -71,6 +61,8 @@ class Bower(
) = Bower(type, analysisRoot, analyzerConfig, repoConfig)
}

private val graphBuilder = DependencyGraphBuilder(BowerDependencyHandler())

override fun beforeResolution(definitionFiles: List<File>) = BowerCommand.checkVersion()

override fun resolveDependencies(definitionFile: File, labels: Map<String, String>): List<ProjectAnalyzerResult> {
Expand All @@ -82,24 +74,27 @@ class Bower(
.getTransitiveDependencies()
.associateBy { checkNotNull(it.pkgMeta.name) }

val scopes = SCOPE_NAMES.mapTo(mutableSetOf()) { scopeName ->
Scope(
name = scopeName,
dependencies = projectPackageInfo.toPackageReferences(scopeName, packageInfoForName)
)
SCOPE_NAMES.forEach { scopeName ->
val dependencies = projectPackageInfo.getScopeDependencies(scopeName).map { dependencyName ->
// Bower leaves out a dependency entry for a child if there exists a similar entry to its parent
// entry with the exact same name and resolved target. This makes it necessary to retrieve the
// information about the subtree rooted at the parent from that other entry containing the full
// dependency information.
// See https://github.com/bower/bower/blob/6bc778d/lib/core/Manager.js#L557 and below.
projectPackageInfo.dependencies[dependencyName] ?: packageInfoForName.getValue(dependencyName)
}

graphBuilder.addDependencies(projectPackageInfo.toIdentifier(), scopeName, dependencies)
}

val project = projectPackageInfo.toProject(definitionFile, scopes)

val referencedPackages = scopes.collectDependencies()
val packages = packageInfoForName.values
.map { it.toPackage() }
.filterTo(mutableSetOf()) { it.id in referencedPackages }

return listOf(ProjectAnalyzerResult(project, packages))
val project = projectPackageInfo.toProject(definitionFile, SCOPE_NAMES)
return listOf(ProjectAnalyzerResult(project, emptySet()))
}
}

override fun createPackageManagerResult(projectResults: Map<File, List<ProjectAnalyzerResult>>) =
PackageManagerResult(projectResults, graphBuilder.build(), graphBuilder.packages())

private fun getProjectPackageInfo(workingDir: File): PackageInfo {
BowerCommand.run(workingDir, "--allow-root", "install").requireSuccess()
val json = BowerCommand.run(workingDir, "--allow-root", "list", "--json").requireSuccess().stdout
Expand Down Expand Up @@ -130,66 +125,3 @@ private fun PackageInfo.getTransitiveDependencies(): List<PackageInfo> {

return result
}

private fun PackageInfo.toId() =
Identifier(
type = "Bower",
namespace = "",
name = pkgMeta.name.orEmpty(),
version = pkgMeta.version.orEmpty()
)

private fun PackageInfo.toVcsInfo() =
VcsInfo(
type = VcsType.forName(pkgMeta.repository?.type.orEmpty()),
url = pkgMeta.repository?.url ?: pkgMeta.source.orEmpty(),
revision = pkgMeta.resolution?.commit ?: pkgMeta.resolution?.tag.orEmpty()
)

private fun PackageInfo.toPackage() =
Package(
id = toId(),
// See https://github.com/bower/spec/blob/master/json.md#authors.
authors = pkgMeta.authors.flatMap { parseAuthorString(it.name) }.mapNotNullTo(mutableSetOf()) { it.name },
declaredLicenses = setOfNotNull(pkgMeta.license?.takeUnless { it.isEmpty() }),
description = pkgMeta.description.orEmpty(),
homepageUrl = pkgMeta.homepage.orEmpty(),
binaryArtifact = RemoteArtifact.EMPTY,
sourceArtifact = RemoteArtifact.EMPTY, // TODO: implement me!
vcs = toVcsInfo()
)

private fun PackageInfo.toProject(definitionFile: File, scopes: Set<Scope>) =
with(toPackage()) {
Project(
id = id,
definitionFilePath = VersionControlSystem.getPathInfo(definitionFile).path,
authors = authors,
declaredLicenses = declaredLicenses,
vcs = vcs,
vcsProcessed = processProjectVcs(definitionFile.parentFile, vcs, homepageUrl),
homepageUrl = homepageUrl,
scopeDependencies = scopes
)
}

private fun PackageInfo.toPackageReferences(
scopeName: String,
packageInfoForName: Map<String, PackageInfo>
): Set<PackageReference> =
getScopeDependencies(scopeName).mapTo(mutableSetOf()) { name ->
// Bower leaves out a dependency entry for a child if there exists a similar entry to its parent entry
// with the exact same name and resolved target. This makes it necessary to retrieve the information
// about the subtree rooted at the parent from that other entry containing the full dependency
// information.
// See https://github.com/bower/bower/blob/6bc778d/lib/core/Manager.js#L557 and below.
val childInfo = dependencies[name] ?: packageInfoForName.getValue(name)

PackageReference(
id = childInfo.toId(),
dependencies = childInfo.toPackageReferences(
scopeName = SCOPE_NAME_DEPENDENCIES,
packageInfoForName = packageInfoForName
)
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* Copyright (C) 2024 The ORT Project Authors (see <https://github.com/oss-review-toolkit/ort/blob/main/NOTICE>)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
* License-Filename: LICENSE
*/

package org.ossreviewtoolkit.plugins.packagemanagers.bower

import java.io.File

import org.ossreviewtoolkit.analyzer.PackageManager.Companion.processProjectVcs
import org.ossreviewtoolkit.analyzer.parseAuthorString
import org.ossreviewtoolkit.downloader.VersionControlSystem
import org.ossreviewtoolkit.model.Identifier
import org.ossreviewtoolkit.model.Issue
import org.ossreviewtoolkit.model.Package
import org.ossreviewtoolkit.model.PackageLinkage
import org.ossreviewtoolkit.model.Project
import org.ossreviewtoolkit.model.RemoteArtifact
import org.ossreviewtoolkit.model.VcsInfo
import org.ossreviewtoolkit.model.VcsType
import org.ossreviewtoolkit.model.utils.DependencyHandler

internal class BowerDependencyHandler : DependencyHandler<PackageInfo> {
override fun identifierFor(dependency: PackageInfo) = dependency.toIdentifier()

override fun dependenciesFor(dependency: PackageInfo) = dependency.dependencies.values.toList()

override fun linkageFor(dependency: PackageInfo) = PackageLinkage.DYNAMIC

override fun createPackage(dependency: PackageInfo, issues: MutableCollection<Issue>) = dependency.toPackage()
}

internal fun PackageInfo.toIdentifier() =
Identifier(
type = "Bower",
namespace = "",
name = pkgMeta.name.orEmpty(),
version = pkgMeta.version.orEmpty()
)

private fun PackageInfo.toVcsInfo() =
VcsInfo(
type = VcsType.forName(pkgMeta.repository?.type.orEmpty()),
url = pkgMeta.repository?.url ?: pkgMeta.source.orEmpty(),
revision = pkgMeta.resolution?.commit ?: pkgMeta.resolution?.tag.orEmpty()
)

private fun PackageInfo.toPackage() =
Package(
id = toIdentifier(),
// See https://github.com/bower/spec/blob/master/json.md#authors.
authors = pkgMeta.authors.flatMap { parseAuthorString(it.name) }.mapNotNullTo(mutableSetOf()) { it.name },
declaredLicenses = setOfNotNull(pkgMeta.license?.takeUnless { it.isEmpty() }),
description = pkgMeta.description.orEmpty(),
homepageUrl = pkgMeta.homepage.orEmpty(),
binaryArtifact = RemoteArtifact.EMPTY,
sourceArtifact = RemoteArtifact.EMPTY, // TODO: implement me!
vcs = toVcsInfo()
)

internal fun PackageInfo.toProject(definitionFile: File, scopeNames: Set<String>) =
with(toPackage()) {
Project(
id = id,
definitionFilePath = VersionControlSystem.getPathInfo(definitionFile).path,
authors = authors,
declaredLicenses = declaredLicenses,
vcs = vcs,
vcsProcessed = processProjectVcs(definitionFile.parentFile, vcs, homepageUrl),
homepageUrl = homepageUrl,
scopeNames = scopeNames
)
}

0 comments on commit fe2776e

Please sign in to comment.