diff --git a/downloader/src/main/kotlin/VersionControlSystem.kt b/downloader/src/main/kotlin/VersionControlSystem.kt index ccaea97f572d3..683d2e1ae56ea 100644 --- a/downloader/src/main/kotlin/VersionControlSystem.kt +++ b/downloader/src/main/kotlin/VersionControlSystem.kt @@ -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 @@ -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 @@ -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 by lazy { - LOADER.iterator().asSequence().toSortedSet(compareByDescending { it.priority }) + val ALL by lazy { + Plugin.getAll().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) } @@ -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) } } @@ -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 { @@ -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. */ @@ -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 @@ -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) } /** @@ -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") } diff --git a/downloader/src/test/kotlin/VersionControlSystemTest.kt b/downloader/src/test/kotlin/VersionControlSystemTest.kt index c3cea605b566b..3beeee5a56900 100644 --- a/downloader/src/test/kotlin/VersionControlSystemTest.kt +++ b/downloader/src/test/kotlin/VersionControlSystemTest.kt @@ -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 = emptyList() ) : VersionControlSystem(tool) { override fun getVersion(): String = "0" diff --git a/plugins/commands/downloader/src/main/kotlin/DownloaderCommand.kt b/plugins/commands/downloader/src/main/kotlin/DownloaderCommand.kt index 3b9cdafa0fce2..82f1f799c755c 100644 --- a/plugins/commands/downloader/src/main/kotlin/DownloaderCommand.kt +++ b/plugins/commands/downloader/src/main/kotlin/DownloaderCommand.kt @@ -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( diff --git a/plugins/version-control-systems/git/src/main/kotlin/Git.kt b/plugins/version-control-systems/git/src/main/kotlin/Git.kt index f0e65ad337117..1c328fed816d6 100644 --- a/plugins/version-control-systems/git/src/main/kotlin/Git.kt +++ b/plugins/version-control-systems/git/src/main/kotlin/Git.kt @@ -99,7 +99,7 @@ class Git : VersionControlSystem(), CommandLineTool { private val versionRegex = Regex("[Gg]it [Vv]ersion (?[\\d.a-z-]+)(\\s.+)?") - override val type = VcsType.GIT + override val type = VcsType.GIT.toString() override val priority = 100 override val latestRevisionNames = listOf("HEAD", "@") @@ -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 ) diff --git a/plugins/version-control-systems/git/src/main/kotlin/GitRepo.kt b/plugins/version-control-systems/git/src/main/kotlin/GitRepo.kt index 58e51a00b854b..c6a4273e446f8 100644 --- a/plugins/version-control-systems/git/src/main/kotlin/GitRepo.kt +++ b/plugins/version-control-systems/git/src/main/kotlin/GitRepo.kt @@ -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", "@") @@ -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 { diff --git a/plugins/version-control-systems/mercurial/src/main/kotlin/Mercurial.kt b/plugins/version-control-systems/mercurial/src/main/kotlin/Mercurial.kt index f605a78cf294a..fcf134bb20fd9 100644 --- a/plugins/version-control-systems/mercurial/src/main/kotlin/Mercurial.kt +++ b/plugins/version-control-systems/mercurial/src/main/kotlin/Mercurial.kt @@ -47,7 +47,7 @@ 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") @@ -55,7 +55,8 @@ class Mercurial : VersionControlSystem(MercurialCommand) { 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 diff --git a/plugins/version-control-systems/subversion/src/main/kotlin/Subversion.kt b/plugins/version-control-systems/subversion/src/main/kotlin/Subversion.kt index 4d33454451c41..5341ed2127674 100644 --- a/plugins/version-control-systems/subversion/src/main/kotlin/Subversion.kt +++ b/plugins/version-control-systems/subversion/src/main/kotlin/Subversion.kt @@ -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") @@ -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 {