Skip to content

Commit

Permalink
refactor(version-control-systems): Extract Git's CommandLineTool
Browse files Browse the repository at this point in the history
Follow the pattern of `MercurialCommand` [1] and introduce a `GitCommand`.
Besides providing a better overview, this favors composition over
inheritance to emphasize that ORT's `Git` class is not a
`CommandLineTool`, but uses a `CommandLineTool`. Also, this allows to run
the Git CLI statically without the need to create a `Git` instance.

[1]: 5531820

Signed-off-by: Sebastian Schuberth <sebastian@doubleopen.org>
  • Loading branch information
sschuberth committed Apr 23, 2024
1 parent 7a3732f commit 3cfe66e
Show file tree
Hide file tree
Showing 5 changed files with 28 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import io.kotest.matchers.should
import org.ossreviewtoolkit.analyzer.create
import org.ossreviewtoolkit.analyzer.resolveSingleProject
import org.ossreviewtoolkit.model.toYaml
import org.ossreviewtoolkit.plugins.versioncontrolsystems.git.Git
import org.ossreviewtoolkit.plugins.versioncontrolsystems.git.GitCommand
import org.ossreviewtoolkit.utils.common.Os
import org.ossreviewtoolkit.utils.common.ProcessCapture
import org.ossreviewtoolkit.utils.test.ExpensiveTag
Expand Down Expand Up @@ -61,7 +61,7 @@ class GradleFunTest : StringSpec() {

override suspend fun afterSpec(spec: Spec) {
// Reset the Gradle wrapper files to the committed state.
Git().run(projectDir, "checkout", "gradle/", "gradlew*")
GitCommand.run(projectDir, "checkout", "gradle/", "gradlew*")
}

init {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import io.kotest.matchers.should
import org.ossreviewtoolkit.analyzer.create
import org.ossreviewtoolkit.analyzer.resolveSingleProject
import org.ossreviewtoolkit.model.toYaml
import org.ossreviewtoolkit.plugins.versioncontrolsystems.git.Git
import org.ossreviewtoolkit.plugins.versioncontrolsystems.git.GitCommand
import org.ossreviewtoolkit.utils.common.Os
import org.ossreviewtoolkit.utils.common.ProcessCapture
import org.ossreviewtoolkit.utils.test.ExpensiveTag
Expand Down Expand Up @@ -61,7 +61,7 @@ class GradleFunTest : StringSpec() {

override suspend fun afterSpec(spec: Spec) {
// Reset the Gradle wrapper files to the committed state.
Git().run(projectDir, "checkout", "gradle/", "gradlew*")
GitCommand.run(projectDir, "checkout", "gradle/", "gradlew*")
}

init {
Expand Down
6 changes: 3 additions & 3 deletions plugins/package-managers/sbt/src/funTest/kotlin/SbtFunTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import io.kotest.matchers.shouldBe

import org.ossreviewtoolkit.analyzer.analyze
import org.ossreviewtoolkit.model.toYaml
import org.ossreviewtoolkit.plugins.versioncontrolsystems.git.Git
import org.ossreviewtoolkit.plugins.versioncontrolsystems.git.GitCommand
import org.ossreviewtoolkit.utils.test.getAssetFile
import org.ossreviewtoolkit.utils.test.matchExpectedResult
import org.ossreviewtoolkit.utils.test.patchActualResult
Expand All @@ -36,7 +36,7 @@ class SbtFunTest : StringSpec({
val expectedResult = matchExpectedResult(expectedResultFile, definitionFile)

// Clean any previously generated POM files / target directories.
Git().run(definitionFile.parentFile, "clean", "-fd")
GitCommand.run(definitionFile.parentFile, "clean", "-fd")

val ortResult = analyze(definitionFile.parentFile, packageManagers = setOf(Sbt.Factory()))

Expand All @@ -49,7 +49,7 @@ class SbtFunTest : StringSpec({
val expectedResult = matchExpectedResult(expectedResultFile, definitionFile)

// Clean any previously generated POM files / target directories.
Git().run(definitionFile.parentFile, "clean", "-fd")
GitCommand.run(definitionFile.parentFile, "clean", "-fd")

val ortResult = analyze(definitionFile.parentFile, packageManagers = setOf(Sbt.Factory()))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ class GitFunTest : WordSpec({
val revision = branches.getValue(branch)

git.updateWorkingTree(workingTree, branch)
git.run("reset", "--hard", "HEAD~1", workingDir = repoDir)
GitCommand.run("reset", "--hard", "HEAD~1", workingDir = repoDir)

git.updateWorkingTree(workingTree, branch) shouldBeSuccess branch
workingTree.getRevision() shouldBe revision
Expand Down
38 changes: 20 additions & 18 deletions plugins/version-control-systems/git/src/main/kotlin/Git.kt
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,22 @@ private val REPOSITORY_URL_PREFIX_REPLACEMENTS = listOf(
"git://" to "https://"
)

class Git : VersionControlSystem(), CommandLineTool {
object GitCommand : CommandLineTool {
private val versionRegex = Regex("[Gg]it [Vv]ersion (?<version>[\\d.a-z-]+)(\\s.+)?")

override fun command(workingDir: File?) = "git"

// Require at least Git 2.29 on the client side as it has protocol "v2" enabled by default.
override fun getVersionRequirement(): RangesList = RangesListFactory.create(">=2.29")

override fun transformVersion(output: String): String =
versionRegex.matchEntire(output.lineSequence().first())?.let { match ->
@Suppress("UnsafeCallOnNullableType")
match.groups["version"]!!.value
}.orEmpty()
}

class Git : VersionControlSystem() {
companion object {
init {
// Make sure that JGit uses the exact same authentication information as ORT itself. This addresses
Expand Down Expand Up @@ -99,30 +114,17 @@ class Git : VersionControlSystem(), CommandLineTool {
}
}

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

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

override fun command(workingDir: File?) = "git"

override fun getVersion() = getVersion(null)

// Require at least Git 2.29 on the client side as it has protocol "v2" enabled by default.
override fun getVersionRequirement(): RangesList = RangesListFactory.create(">=2.29")
override fun getVersion() = GitCommand.getVersion(null)

override fun getDefaultBranchName(url: String): String {
val refs = Git.lsRemoteRepository().setRemote(url).callAsMap()
return (refs["HEAD"] as? SymbolicRef)?.target?.name?.removePrefix("refs/heads/") ?: "master"
}

override fun transformVersion(output: String): String =
versionRegex.matchEntire(output.lineSequence().first())?.let { match ->
@Suppress("UnsafeCallOnNullableType")
match.groups["version"]!!.value
}.orEmpty()

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

override fun isApplicableUrlInternal(vcsUrl: String): Boolean =
Expand Down Expand Up @@ -238,7 +240,7 @@ class Git : VersionControlSystem(), CommandLineTool {
}.mapCatching { fetchResult ->
// TODO: Migrate this to JGit once sparse checkout (https://bugs.eclipse.org/bugs/show_bug.cgi?id=383772) is
// implemented. Also see the "reset" call below.
run("checkout", revision, workingDir = workingTree.workingDir)
GitCommand.run("checkout", revision, workingDir = workingTree.workingDir)

// In case of a non-fixed revision (branch or tag) reset the working tree to ensure that the previously
// fetched changes are applied.
Expand All @@ -262,7 +264,7 @@ class Git : VersionControlSystem(), CommandLineTool {
"Requested revision '$revision' not found in refs advertised by the server."
}

run("reset", "--hard", resolvedRevision, workingDir = workingTree.workingDir)
GitCommand.run("reset", "--hard", resolvedRevision, workingDir = workingTree.workingDir)
}

revision
Expand All @@ -288,7 +290,7 @@ class Git : VersionControlSystem(), CommandLineTool {
}
}

private fun WorkingTree.runGit(vararg args: String) = run(*args, workingDir = workingDir)
private fun WorkingTree.runGit(vararg args: String) = GitCommand.run(*args, workingDir = workingDir)
}

/**
Expand Down

0 comments on commit 3cfe66e

Please sign in to comment.