Skip to content

Commit

Permalink
refactor(VersionControlSystem): Implement the Plugin interface
Browse files Browse the repository at this point in the history
Align with all other ORT plugin types and implement the `Plugin`
interface. This will allow for some common handling of all types of
plugins in an upcoming change. Also see [1] for context.

[1]: #6507

Signed-off-by: Sebastian Schuberth <sebastian@doubleopen.org>
  • Loading branch information
sschuberth committed Nov 26, 2023
1 parent a176fc5 commit 19bfbe0
Show file tree
Hide file tree
Showing 7 changed files with 25 additions and 30 deletions.
30 changes: 11 additions & 19 deletions downloader/src/main/kotlin/VersionControlSystem.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ package org.ossreviewtoolkit.downloader

import java.io.File
import java.io.IOException
import java.util.ServiceLoader

import org.apache.logging.log4j.kotlin.logger

Expand All @@ -31,6 +30,7 @@ import org.ossreviewtoolkit.model.VcsType
import org.ossreviewtoolkit.model.config.LicenseFilePatterns
import org.ossreviewtoolkit.model.orEmpty
import org.ossreviewtoolkit.utils.common.CommandLineTool
import org.ossreviewtoolkit.utils.common.Plugin
import org.ossreviewtoolkit.utils.common.collectMessages
import org.ossreviewtoolkit.utils.common.uppercaseFirstChar
import org.ossreviewtoolkit.utils.ort.ORT_REPO_CONFIG_FILENAME
Expand All @@ -44,23 +44,20 @@ abstract class VersionControlSystem(
* the version control system is available.
*/
private val commandLineTool: CommandLineTool? = null
) {
) : Plugin {
companion object {
private val LOADER = ServiceLoader.load(VersionControlSystem::class.java)

/**
* The set of all available [Version Control Systems][VersionControlSystem] in the classpath, sorted by
* priority.
* All [version control systems][VersionControlSystem] available in the classpath, sorted by their priority.
*/
val ALL: Set<VersionControlSystem> by lazy {
LOADER.iterator().asSequence().toSortedSet(compareByDescending { it.priority })
val ALL by lazy {
Plugin.getAll<VersionControlSystem>().toList().sortedByDescending { (_, vcs) -> vcs.priority }.toMap()
}

/**
* Return the applicable VCS for the given [vcsType], or null if none is applicable.
*/
fun forType(vcsType: VcsType) =
ALL.find {
ALL.values.find {
it.isAvailable() && it.isApplicableType(vcsType)
}

Expand All @@ -85,7 +82,7 @@ abstract class VersionControlSystem(
when (val type = VcsHost.parseUrl(vcsUrl).type) {
VcsType.UNKNOWN -> {
// ...then eventually try to determine the type also dynamically.
ALL.find {
ALL.values.find {
it.isAvailable() && it.isApplicableUrl(vcsUrl)
}
}
Expand All @@ -111,7 +108,7 @@ abstract class VersionControlSystem(
return if (absoluteVcsDirectory in dirToVcsMap) {
dirToVcsMap[absoluteVcsDirectory]
} else {
ALL.asSequence().mapNotNull {
ALL.values.asSequence().mapNotNull {
if (it is CommandLineTool && !it.isInPath()) {
null
} else {
Expand Down Expand Up @@ -166,11 +163,6 @@ abstract class VersionControlSystem(
flatMap { listOf(it, it.uppercase(), it.uppercaseFirstChar()) }
}

/**
* The [VcsType] of this [VersionControlSystem].
*/
abstract val type: VcsType

/**
* The priority in which this VCS should be probed. A higher value means a higher priority.
*/
Expand Down Expand Up @@ -199,7 +191,7 @@ abstract class VersionControlSystem(
/**
* Return true if this VCS can handle the given [vcsType].
*/
fun isApplicableType(vcsType: VcsType) = vcsType == type
fun isApplicableType(vcsType: VcsType) = type in vcsType.aliases

/**
* Return true if this [VersionControlSystem] can be used to download from the provided [vcsUrl]. First, try to find
Expand All @@ -209,7 +201,7 @@ abstract class VersionControlSystem(
fun isApplicableUrl(vcsUrl: String): Boolean {
if (vcsUrl.isBlank() || vcsUrl.endsWith(".html")) return false

return VcsHost.parseUrl(vcsUrl).type == type || isApplicableUrlInternal(vcsUrl)
return isApplicableType(VcsHost.parseUrl(vcsUrl).type) || isApplicableUrlInternal(vcsUrl)
}

/**
Expand Down Expand Up @@ -363,7 +355,7 @@ abstract class VersionControlSystem(

addMetadataRevision(pkg.vcsProcessed.revision)

if (type == VcsType.GIT && pkg.vcsProcessed.revision == "master") {
if (type in VcsType.GIT.aliases && pkg.vcsProcessed.revision == "master") {
// Also try with Git's upcoming default branch name in case the repository is already using it.
addMetadataRevision("main")
}
Expand Down
2 changes: 1 addition & 1 deletion downloader/src/test/kotlin/VersionControlSystemTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ class VersionControlSystemTest : WordSpec({
*/
private class VersionControlSystemTestImpl(
tool: CommandLineTool?,
override val type: VcsType = VcsType.UNKNOWN,
override val type: String = VcsType.UNKNOWN.toString(),
override val latestRevisionNames: List<String> = emptyList()
) : VersionControlSystem(tool) {
override fun getVersion(): String = "0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,8 @@ class DownloaderCommand : OrtCommand(
Package.EMPTY.copy(id = dummyId, sourceArtifact = RemoteArtifact.EMPTY.copy(url = projectUrl))
} else {
val vcs = VersionControlSystem.forUrl(projectUrl)
val vcsType = vcsTypeOption?.let { VcsType.forName(it) } ?: (vcs?.type ?: VcsType.UNKNOWN)
val vcsType = listOfNotNull(vcsTypeOption, vcs?.type).map { VcsType.forName(it) }.firstOrNull()
?: VcsType.UNKNOWN
val vcsRevision = vcsRevisionOption ?: vcs?.getDefaultBranchName(projectUrl).orEmpty()

val vcsInfo = VcsInfo(
Expand Down
4 changes: 2 additions & 2 deletions plugins/version-control-systems/git/src/main/kotlin/Git.kt
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ class Git : VersionControlSystem(), CommandLineTool {

private val versionRegex = Regex("[Gg]it [Vv]ersion (?<version>[\\d.a-z-]+)(\\s.+)?")

override val type = VcsType.GIT
override val type = VcsType.GIT.toString()
override val priority = 100
override val latestRevisionNames = listOf("HEAD", "@")

Expand All @@ -123,7 +123,7 @@ class Git : VersionControlSystem(), CommandLineTool {
override fun getWorkingTree(vcsDirectory: File): WorkingTree =
GitWorkingTree(
workingDir = vcsDirectory,
vcsType = type,
vcsType = VcsType.forName(type),
repositoryUrlPrefixReplacements = REPOSITORY_URL_PREFIX_REPLACEMENTS
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ private data class Include(
)

class GitRepo : VersionControlSystem(), CommandLineTool {
override val type = VcsType.GIT_REPO
override val type = VcsType.GIT_REPO.toString()
override val priority = 50
override val latestRevisionNames = listOf("HEAD", "@")

Expand All @@ -91,16 +91,17 @@ class GitRepo : VersionControlSystem(), CommandLineTool {

override fun getWorkingTree(vcsDirectory: File): WorkingTree {
val repoRoot = vcsDirectory.searchUpwardsForSubdirectory(".repo")
val vcsType = VcsType.forName(type)

return if (repoRoot == null) {
object : GitWorkingTree(vcsDirectory, type) {
object : GitWorkingTree(vcsDirectory, vcsType) {
override fun isValid() = false
}
} else {
// GitRepo is special in that the workingDir points to the Git working tree of the manifest files, yet
// the root path is the directory containing the ".repo" directory. This way Git operations work on a valid
// Git repository, but path operations work relative to the path GitRepo was initialized in.
object : GitWorkingTree(repoRoot.resolve(".repo/manifests"), type) {
object : GitWorkingTree(repoRoot.resolve(".repo/manifests"), vcsType) {
// Return the path to the manifest as part of the VCS information, as that is required to recreate the
// working tree.
override fun getInfo(): VcsInfo {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,16 @@ object MercurialCommand : CommandLineTool {
}

class Mercurial : VersionControlSystem(MercurialCommand) {
override val type = VcsType.MERCURIAL
override val type = VcsType.MERCURIAL.toString()
override val priority = 20
override val latestRevisionNames = listOf("tip")

override fun getVersion() = MercurialCommand.getVersion(null)

override fun getDefaultBranchName(url: String) = "default"

override fun getWorkingTree(vcsDirectory: File): WorkingTree = MercurialWorkingTree(vcsDirectory, type)
override fun getWorkingTree(vcsDirectory: File): WorkingTree =
MercurialWorkingTree(vcsDirectory, VcsType.forName(type))

override fun isApplicableUrlInternal(vcsUrl: String) = ProcessCapture("hg", "identify", vcsUrl).isSuccess

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class Subversion : VersionControlSystem() {
setAuthenticationManager(ortAuthManager)
}

override val type = VcsType.SUBVERSION
override val type = VcsType.SUBVERSION.toString()
override val priority = 10
override val latestRevisionNames = listOf("HEAD")

Expand All @@ -67,7 +67,7 @@ class Subversion : VersionControlSystem() {
override fun getDefaultBranchName(url: String) = "trunk"

override fun getWorkingTree(vcsDirectory: File): WorkingTree =
SubversionWorkingTree(vcsDirectory, type, clientManager)
SubversionWorkingTree(vcsDirectory, VcsType.forName(type), clientManager)

override fun isApplicableUrlInternal(vcsUrl: String) =
try {
Expand Down

0 comments on commit 19bfbe0

Please sign in to comment.