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

com.rallyhealth.sbt.versioning.GitFetcher.scala Maven / Gradle / Ivy

package bleep.plugin.versioning

import ryddig.Logger

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration.Duration
import scala.concurrent.{Await, Future, TimeoutException}
import scala.sys.process.Process
import scala.util.control.NonFatal

/** Utility code to fetch history from Git. */
object GitFetcher {

  case class FetchResult(remoteName: String, tagName: String)

  // Example Match: * [new tag]         v0.2.7     -> v0.2.7
  private val tagResultRegex = """ \* \[new tag\][ ]+([\S]+).*""".r

  /** Fetches tags from all remote sources.
    *
    * @param remotes
    *   Remote sources whose contents should be fetched AFTER fetching the list of tags.
    */
  def fetchRemotes(timeout: Duration)(implicit logger: Logger): Seq[FetchResult] = {
    val outputLogger = new BufferingProcessLogger
    val processResult = Process("git remote") ! outputLogger

    processResult match {
      case 0 =>
        logger.debug("Fetching remote sources...")
        val remotes = outputLogger.stdout

        val tagsToFetch = remotes.filter(remotes.contains)
        if (tagsToFetch.nonEmpty) {
          logger.info("Fetching tags from: " + tagsToFetch.mkString(", "))
          tagsToFetch.toSeq.flatMap(remote => fetchTagsFromRemote(remote, timeout)(logger))
        } else {
          logger.debug("No tags to fetch")
          Seq.empty[FetchResult]
        }

      case exitCode =>
        logger.error(s"Fetching remotes failed enumerating remotes [git exitCode=$exitCode]")
        Seq.empty[FetchResult]
    }
  }

  private def fetchTagsFromRemote(remote: String, timeout: Duration)(implicit logger: Logger): Seq[FetchResult] = {

    val outputLogger = new BufferingProcessLogger
    val process = Process(s"git fetch $remote --tags").run(outputLogger)
    val resultFuture = Future {
      if (process.exitValue() == 0) {
        outputLogger.stderr.toSeq.filter(_.contains("[new tag]")).flatMap {
          case tagResultRegex(tag) =>
            logger.debug(s"Fetched from remote=$remote tag=$tag")
            Some(FetchResult(remote, tag))
          case line =>
            logger.warn(s"Unable to parse git result=$line, skipping")
            None
        }
      } else {
        logger.error(s"Fetching remote=$remote failed [git exitCode=${process.exitValue()}]")
        Seq.empty[FetchResult]
      }
    }

    try {
      val result = Await.result(resultFuture, timeout)
      logger.debug(s"Successfully fetched $remote")
      result
    } catch {
      case _: TimeoutException =>
        process.destroy()
        logger.error(s"Fetching remote=$remote timed out [git exitCode=${process.exitValue()}]")
        Seq.empty
      case NonFatal(exc) =>
        logger.error(s"Fetching remote=$remote failed [git exitCode=${process.exitValue()}]", exc)
        Seq.empty
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy