Skip to content

Commit

Permalink
refactor(vcs): Enable to provide VCS-specific configuration options
Browse files Browse the repository at this point in the history
 While VCS implementations are already plugins, they are not yet
 configurable. VCS implementations require common configurations
 (e.g., `revision`, `recursive`) and should support also
 VCS-specific configurations if they are consumed via their API.
 This allows to add functionality to individual VCS implementations
 without the need to implement them for all of them.

 This refactoring keeps the common configurations attributes as
 they are, while VCS-specific configurations are stored
 generically in an `options` attribute.

Fixes oss-review-toolkit#8556.

Signed-off-by: Wolfgang Klenk <[email protected]>
  • Loading branch information
wkl3nk committed Nov 4, 2024
1 parent f8a0c39 commit 39ac3ec
Show file tree
Hide file tree
Showing 11 changed files with 170 additions and 13 deletions.
9 changes: 7 additions & 2 deletions downloader/src/main/kotlin/Downloader.kt
Original file line number Diff line number Diff line change
Expand Up @@ -270,8 +270,13 @@ class Downloader(private val config: DownloaderConfiguration) {
return RepositoryProvenance(pkg.vcsProcessed, pkg.vcsProcessed.revision)
}

val vcsConfig = config.getVersionControlSystemConfiguration(applicableVcs.type)
if (vcsConfig != null) {
logger.info { "Using ${applicableVcs.type}-specific configuration $vcsConfig." }
}

val workingTree = try {
applicableVcs.download(pkg, outputDirectory, config.allowMovingRevisions, recursive)
applicableVcs.download(pkg, outputDirectory, config.allowMovingRevisions, recursive, vcsConfig)
} catch (e: DownloadException) {
// TODO: Introduce something like a "strict" mode and only do these kind of fallbacks in non-strict mode.
val vcsUrlNoCredentials = pkg.vcsProcessed.url.replaceCredentialsInUri()
Expand All @@ -285,7 +290,7 @@ class Downloader(private val config: DownloaderConfiguration) {
outputDirectory.safeDeleteRecursively(baseDirectory = outputDirectory)

val fallbackPkg = pkg.copy(vcsProcessed = pkg.vcsProcessed.copy(url = vcsUrlNoCredentials))
applicableVcs.download(fallbackPkg, outputDirectory, config.allowMovingRevisions, recursive)
applicableVcs.download(fallbackPkg, outputDirectory, config.allowMovingRevisions, recursive, vcsConfig)
} else {
throw e
}
Expand Down
16 changes: 11 additions & 5 deletions downloader/src/main/kotlin/VersionControlSystem.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import org.ossreviewtoolkit.model.Package
import org.ossreviewtoolkit.model.VcsInfo
import org.ossreviewtoolkit.model.VcsType
import org.ossreviewtoolkit.model.config.LicenseFilePatterns
import org.ossreviewtoolkit.model.config.VersionControlSystemConfiguration
import org.ossreviewtoolkit.model.orEmpty
import org.ossreviewtoolkit.utils.common.CommandLineTool
import org.ossreviewtoolkit.utils.common.Plugin
Expand Down Expand Up @@ -223,6 +224,7 @@ abstract class VersionControlSystem(
* Download the source code as specified by the [pkg] information to [targetDir]. [allowMovingRevisions] toggles
* whether to allow downloads using symbolic names that point to moving revisions, like Git branches. If [recursive]
* is `true`, any nested repositories (like Git submodules or Mercurial subrepositories) are downloaded, too.
* VCS-specific configurations can be supplied via [vcsConfiguration].
*
* @return An object describing the downloaded working tree.
*
Expand All @@ -232,7 +234,8 @@ abstract class VersionControlSystem(
pkg: Package,
targetDir: File,
allowMovingRevisions: Boolean = false,
recursive: Boolean = true
recursive: Boolean = true,
vcsConfiguration: VersionControlSystemConfiguration? = null
): WorkingTree {
val workingTree = try {
initWorkingTree(targetDir, pkg.vcsProcessed)
Expand All @@ -248,7 +251,7 @@ abstract class VersionControlSystem(

for ((index, revision) in revisionCandidates.withIndex()) {
logger.info { "Trying revision candidate '$revision' (${index + 1} of ${revisionCandidates.size})..." }
results += updateWorkingTree(workingTree, revision, pkg.vcsProcessed.path, recursive)
results += updateWorkingTree(workingTree, revision, pkg.vcsProcessed.path, recursive, vcsConfiguration)
if (results.last().isSuccess) break
}

Expand Down Expand Up @@ -380,14 +383,17 @@ abstract class VersionControlSystem(

/**
* Update the [working tree][workingTree] by checking out the given [revision], optionally limited to the given
* [path] and [recursively][recursive] updating any nested working trees. Return a [Result] that encapsulates the
* originally requested [revision] on success, or the occurred exception on failure.
* [path] and [recursively][recursive] updating any nested working trees.
* Optionally, a VCS-specific configuration can be supplied via [vcsConfiguration].
* Return a [Result] that encapsulates the originally requested [revision] on success,
* or the occurred exception on failure.
*/
abstract fun updateWorkingTree(
workingTree: WorkingTree,
revision: String,
path: String = "",
recursive: Boolean = false
recursive: Boolean = false,
vcsConfiguration: VersionControlSystemConfiguration? = null
): Result<String>

/**
Expand Down
4 changes: 3 additions & 1 deletion downloader/src/test/kotlin/VersionControlSystemTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import java.lang.UnsupportedOperationException
import org.ossreviewtoolkit.model.Package
import org.ossreviewtoolkit.model.VcsInfo
import org.ossreviewtoolkit.model.VcsType
import org.ossreviewtoolkit.model.config.VersionControlSystemConfiguration
import org.ossreviewtoolkit.plugins.versioncontrolsystems.git.Git
import org.ossreviewtoolkit.utils.common.CommandLineTool

Expand Down Expand Up @@ -168,6 +169,7 @@ private class VersionControlSystemTestImpl(
workingTree: WorkingTree,
revision: String,
path: String,
recursive: Boolean
recursive: Boolean,
vcsConfiguration: VersionControlSystemConfiguration?
): Result<String> = Result.failure(UnsupportedOperationException("Unexpected invocation."))
}
28 changes: 27 additions & 1 deletion model/src/main/kotlin/config/DownloaderConfiguration.kt
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,35 @@ data class DownloaderConfiguration(
* Configuration of the considered source code origins and their priority order. This must not be empty and not
* contain any duplicates.
*/
val sourceCodeOrigins: List<SourceCodeOrigin> = listOf(SourceCodeOrigin.VCS, SourceCodeOrigin.ARTIFACT)
val sourceCodeOrigins: List<SourceCodeOrigin> = listOf(SourceCodeOrigin.VCS, SourceCodeOrigin.ARTIFACT),

/**
* Version control system specific configurations. The key needs to match VCS type,
* e.g. "Git" for the Git version control system.
*/
val versionControlSystems: Map<String, VersionControlSystemConfiguration>? = null
) {
/**
* A copy of [versionControlSystems] with case-insensitive keys.
*/
private val versionControlSystemsCaseInsensitive: Map<String, VersionControlSystemConfiguration>? =
versionControlSystems?.toSortedMap(String.CASE_INSENSITIVE_ORDER)

init {
sourceCodeOrigins.requireNotEmptyNoDuplicates()

val duplicateVersionControlSystems =
versionControlSystems?.keys.orEmpty() - versionControlSystemsCaseInsensitive?.keys?.toSet().orEmpty()

require(duplicateVersionControlSystems.isEmpty()) {
"The following version control systems have duplicate configuration: " +
"${duplicateVersionControlSystems.joinToString()}."
}
}

/**
* Get a [VersionControlSystemConfiguration] from [versionControlSystems].
* The difference to accessing the map directly is that [vcsType] can be case-insensitive.
*/
fun getVersionControlSystemConfiguration(vcsType: String) = versionControlSystemsCaseInsensitive?.get(vcsType)
}
35 changes: 35 additions & 0 deletions model/src/main/kotlin/config/VersionControlSystemConfiguration.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* 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.model.config

import com.fasterxml.jackson.annotation.JsonInclude

import org.ossreviewtoolkit.utils.common.Options

/**
* The configuration for a Version Control System (VCS).
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
data class VersionControlSystemConfiguration(
/**
* Custom configuration options. See the documentation of the respective class for available options.
*/
val options: Options? = null
)
11 changes: 11 additions & 0 deletions model/src/main/resources/reference.yml
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,17 @@ ort:

sourceCodeOrigins: [VCS, ARTIFACT]

# Optional VCS-specific configuration options.
versionControlSystems:
Git:
options:
# Depth of the commit history to fetch when updating submodules
submoduleHistoryDepth: 10

# A flag to control whether nested submodules should be updated (true), or if only the submodules
# on the first layer should be considered (false).
updateNestedSubmodules: true

scanner:
skipConcluded: true
skipExcluded: true
Expand Down
54 changes: 54 additions & 0 deletions model/src/test/kotlin/config/DownloaderConfigurationTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* 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.model.config

import io.kotest.assertions.throwables.shouldThrow
import io.kotest.core.spec.style.WordSpec
import io.kotest.matchers.shouldBe

class DownloaderConfigurationTest : WordSpec({
"DownloaderConfiguration()" should {
"throw an exception on duplicate VCS configurations" {
shouldThrow<IllegalArgumentException> {
DownloaderConfiguration(
versionControlSystems = mapOf(
"Git" to VersionControlSystemConfiguration(),
"git" to VersionControlSystemConfiguration()
)
)
}
}
}

"getVersionControlSystemConfiguration" should {
"return configuration case-insensitively" {
val vcsConfiguration = VersionControlSystemConfiguration()
val downloaderConfiguration = DownloaderConfiguration(
versionControlSystems = mapOf(
"MyVCS" to vcsConfiguration
)
)

downloaderConfiguration.getVersionControlSystemConfiguration("MyVCS") shouldBe vcsConfiguration
downloaderConfiguration.getVersionControlSystemConfiguration("myvcs") shouldBe vcsConfiguration
downloaderConfiguration.getVersionControlSystemConfiguration("MYVCS") shouldBe vcsConfiguration
}
}
})
4 changes: 3 additions & 1 deletion plugins/version-control-systems/git/src/main/kotlin/Git.kt
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import org.ossreviewtoolkit.downloader.VersionControlSystem
import org.ossreviewtoolkit.downloader.WorkingTree
import org.ossreviewtoolkit.model.VcsInfo
import org.ossreviewtoolkit.model.VcsType
import org.ossreviewtoolkit.model.config.VersionControlSystemConfiguration
import org.ossreviewtoolkit.utils.common.CommandLineTool
import org.ossreviewtoolkit.utils.common.Os
import org.ossreviewtoolkit.utils.common.collectMessages
Expand Down Expand Up @@ -170,7 +171,8 @@ class Git : VersionControlSystem(GitCommand) {
workingTree: WorkingTree,
revision: String,
path: String,
recursive: Boolean
recursive: Boolean,
vcsConfiguration: VersionControlSystemConfiguration? // Git-specific configuration only
): Result<String> =
(workingTree as GitWorkingTree).useRepo {
Git(this).use { git ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import org.ossreviewtoolkit.downloader.VersionControlSystem
import org.ossreviewtoolkit.downloader.WorkingTree
import org.ossreviewtoolkit.model.VcsInfo
import org.ossreviewtoolkit.model.VcsType
import org.ossreviewtoolkit.model.config.VersionControlSystemConfiguration
import org.ossreviewtoolkit.model.utils.parseRepoManifestPath
import org.ossreviewtoolkit.utils.common.CommandLineTool
import org.ossreviewtoolkit.utils.common.Os
Expand Down Expand Up @@ -192,7 +193,8 @@ class GitRepo : VersionControlSystem(GitRepoCommand) {
workingTree: WorkingTree,
revision: String,
path: String,
recursive: Boolean
recursive: Boolean,
vcsConfiguration: VersionControlSystemConfiguration? // GitRepo-specific configuration only.
): Result<String> {
val manifestRevision = revision.takeUnless { it.isBlank() }
val manifestPath = workingTree.getInfo().url.parseRepoManifestPath()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import org.ossreviewtoolkit.downloader.VersionControlSystem
import org.ossreviewtoolkit.downloader.WorkingTree
import org.ossreviewtoolkit.model.VcsInfo
import org.ossreviewtoolkit.model.VcsType
import org.ossreviewtoolkit.model.config.VersionControlSystemConfiguration
import org.ossreviewtoolkit.utils.common.CommandLineTool
import org.ossreviewtoolkit.utils.common.ProcessCapture

Expand Down Expand Up @@ -93,7 +94,13 @@ class Mercurial : VersionControlSystem(MercurialCommand) {
return getWorkingTree(targetDir)
}

override fun updateWorkingTree(workingTree: WorkingTree, revision: String, path: String, recursive: Boolean) =
override fun updateWorkingTree(
workingTree: WorkingTree,
revision: String,
path: String,
recursive: Boolean,
vcsConfiguration: VersionControlSystemConfiguration? // Mercurial-specific configuration only
) =
runCatching {
// To safe network bandwidth, only pull exactly the revision we want. Do not use "-u" to update the
// working tree just yet, as Mercurial would only update if new changesets were pulled. But that might
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import org.ossreviewtoolkit.downloader.VersionControlSystem
import org.ossreviewtoolkit.downloader.WorkingTree
import org.ossreviewtoolkit.model.VcsInfo
import org.ossreviewtoolkit.model.VcsType
import org.ossreviewtoolkit.model.config.VersionControlSystemConfiguration
import org.ossreviewtoolkit.utils.common.collectMessages
import org.ossreviewtoolkit.utils.ort.OrtAuthenticator
import org.ossreviewtoolkit.utils.ort.OrtProxySelector
Expand Down Expand Up @@ -126,7 +127,13 @@ class Subversion : VersionControlSystem() {
return pathRevisions.single()
}

override fun updateWorkingTree(workingTree: WorkingTree, revision: String, path: String, recursive: Boolean) =
override fun updateWorkingTree(
workingTree: WorkingTree,
revision: String,
path: String,
recursive: Boolean,
vcsConfiguration: VersionControlSystemConfiguration? // Subversion-specific configuration only.
) =
runCatching {
// Note that the path should never be part of the URL as that would root the working tree at that path, but
// the path should be available in the working tree.
Expand Down

0 comments on commit 39ac3ec

Please sign in to comment.