All Downloads are FREE. Search and download functionalities are using the official Maven repository.

run.qontract.core.git.GitOperations.kt Maven / Gradle / Ivy

package run.qontract.core.git

import io.ktor.http.*
import org.eclipse.jgit.api.Git
import org.eclipse.jgit.transport.CredentialsProvider
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider
import run.qontract.core.Constants.Companion.DEFAULT_QONTRACT_CONFIG_IN_CURRENT_DIRECTORY
import run.qontract.core.pattern.parsedJSON
import run.qontract.core.utilities.GitRepo
import run.qontract.core.utilities.getTransportCallingCallback
import run.qontract.core.value.JSONObjectValue
import run.qontract.core.value.Value
import java.io.File

fun clone(workingDirectory: File, gitRepo: GitRepo): File {
    val cloneDirectory = gitRepo.directoryRelativeTo(workingDirectory)

    resetCloneDirectory(cloneDirectory)
    clone(gitRepo.gitRepositoryURL, cloneDirectory)

    return cloneDirectory
}

private fun clone(gitRepositoryURI: String, cloneDirectory: File) {
    jgitClone(gitRepositoryURI, cloneDirectory) { exception ->
        println("""Falling back to git command after getting error from jgit (${exception.javaClass.name}: ${exception.message})""")
        SystemGit(cloneDirectory.parent, "-").clone(gitRepositoryURI, cloneDirectory)
    }
}

private fun resetCloneDirectory(cloneDirectory: File) {
    println("Resetting ${cloneDirectory.absolutePath}")
    if (cloneDirectory.exists())
        cloneDirectory.deleteRecursively()
    cloneDirectory.mkdirs()
}

private fun jgitClone(gitRepositoryURI: String, cloneDirectory: File, onFailure: (exception: Throwable) -> Unit) {
    try {
        val cloneCommand = Git.cloneRepository().apply {
            setTransportConfigCallback(getTransportCallingCallback())
            setURI(gitRepositoryURI)
            setDirectory(cloneDirectory)
        }

        val accessToken = getPersonalAccessToken()

        if (accessToken != null) {
            val credentialsProvider: CredentialsProvider = UsernamePasswordCredentialsProvider(accessToken, "")
            cloneCommand.setCredentialsProvider(credentialsProvider)
        } else {
            val ciBearerToken = getBearerToken()

            if (ciBearerToken != null) {
                cloneCommand.setTransportConfigCallback(getTransportCallingCallback(ciBearerToken.encodeOAuth()))
            }
        }

        cloneCommand.call()
    } catch (e: Throwable) {
        onFailure(e)
    }
}

fun loadFromPath(json: Value?, path: List): Value? {
    if (json !is JSONObjectValue)
        return null

    return when(path.size) {
        0 -> null
        1 -> json.jsonObject[path.first()]
        else -> loadFromPath(json.jsonObject[path.first()], path.drop(1))
    }
}

fun getBearerToken(): String? {
    val qontractConfigFile = File(DEFAULT_QONTRACT_CONFIG_IN_CURRENT_DIRECTORY)

    return when {
        qontractConfigFile.exists() ->
            readQontractConfig(qontractConfigFile).let { qontractConfig ->
                readBearerFromEnvVariable(qontractConfig) ?: readBearerFromFile(qontractConfig)
            }
        else -> null.also {
            println("$DEFAULT_QONTRACT_CONFIG_IN_CURRENT_DIRECTORY not found")
            println("Current working directory is ${File(".").absolutePath}")
        }
    }
}

private fun readBearerFromEnvVariable(qontractConfig: Value): String? {
    return loadFromPath(qontractConfig, listOf("auth", "bearer-environment-variable"))?.toStringValue()?.let { bearerName ->
        println("Found bearer name $bearerName")

        System.getenv(bearerName).also {
            if(it != null) println("$bearerName is not empty")
        }
    }
}

private fun readBearerFromFile(qontractConfig: Value): String? {
    return loadFromPath(qontractConfig, listOf("auth", "bearer-file"))?.toStringValue()?.let { bearerFileName ->
        println("Found bearer file name $bearerFileName")

        File("./$bearerFileName").takeIf { it.exists() }?.let {
            println("Found bearer file")
            it.readText().trim()
        }
    }
}

//TODO: Why do we have a qontract.json in home dir, it should be in project root
fun getPersonalAccessToken(): String? {
    val homeDir = File(System.getProperty("user.home"))

    if(homeDir.exists()) {
        val configFile = homeDir.resolve("qontract.json")

        if(configFile.exists()) {
            val qontractConfig = readQontractConfig(configFile)

            val azureAccessTokenKey = "azure-access-token"
            if (qontractConfig is JSONObjectValue && qontractConfig.jsonObject.containsKey(azureAccessTokenKey)) {
                return qontractConfig.getString(azureAccessTokenKey)
            }
        }
    }

    return null
}

private fun readQontractConfig(qontractConfigFile: File) = parsedJSON(qontractConfigFile.readText())




© 2015 - 2025 Weber Informatics LLC | Privacy Policy