Skip to content

Commit

Permalink
Add a setting to handle non v-prefixed tags (#108)
Browse files Browse the repository at this point in the history
Add a setting to handle non v-prefixed tags
  • Loading branch information
dwijnand authored Jun 16, 2019
2 parents ef33583 + a5db3bc commit 28c3133
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 28 deletions.
51 changes: 31 additions & 20 deletions src/main/scala/sbtdynver/DynVerPlugin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ object DynVerPlugin extends AutoPlugin {
val dynverSonatypeSnapshots = settingKey[Boolean]("Whether to append -SNAPSHOT to snapshot versions")
val dynverGitPreviousStableVersion = settingKey[Option[GitDescribeOutput]]("The last stable tag")
val dynverSeparator = settingKey[String]("The separator to use between tag and distance, and the hash and dirty timestamp")
val dynverVTagPrefix = settingKey[Boolean]("Whether or not tags have a 'v' prefix")
val dynverCheckVersion = taskKey[Boolean]("Checks if version and dynver match")
val dynverAssertVersion = taskKey[Unit]("Asserts if version and dynver match")
val dynverAssertTagVersion = taskKey[Unit]("Asserts if the version derives from git tags")
Expand All @@ -45,11 +46,12 @@ object DynVerPlugin extends AutoPlugin {
previousStableVersion := dynverGitPreviousStableVersion.value.previousVersion,

dynverCurrentDate := new Date,
dynverInstance := DynVer(Some((baseDirectory in ThisBuild).value), dynverSeparator.value),
dynverInstance := DynVer(Some(buildBase.value), dynverSeparator.value, dynverVTagPrefix.value),
dynverGitDescribeOutput := dynverInstance.value.getGitDescribeOutput(dynverCurrentDate.value),
dynverSonatypeSnapshots := false,
dynverGitPreviousStableVersion := dynverInstance.value.getGitPreviousStableTag,
dynverSeparator := DynVer.separator,
dynverVTagPrefix := DynVer.vTagPrefix,

dynver := {
val dynver = dynverInstance.value
Expand All @@ -71,6 +73,8 @@ object DynVerPlugin extends AutoPlugin {
)
}
)

private val buildBase = baseDirectory in ThisBuild
}

final case class GitRef(value: String)
Expand Down Expand Up @@ -125,26 +129,32 @@ final case class GitDescribeOutput(ref: GitRef, commitSuffix: GitCommitSuffix, d

object GitDescribeOutput extends ((GitRef, GitCommitSuffix, GitDirtySuffix) => GitDescribeOutput) {
private val OptWs = """[\s\n]*""" // doesn't \s include \n? why can't this call .r?
private val Tag = """(v[0-9][^+]*?)""".r
private val Distance = """\+([0-9]+)""".r
private val Sha = """([0-9a-f]{8})""".r
private val HEAD = """HEAD""".r
private val CommitSuffix = s"""($Distance-$Sha)""".r
private val TstampSuffix = """(\+[0-9]{8}-[0-9]{4})""".r

private val FromTag = s"""^$OptWs$Tag$CommitSuffix?$TstampSuffix?$OptWs$$""".r
private val FromSha = s"""^$OptWs$Sha$TstampSuffix?$OptWs$$""".r
private val FromHead = s"""^$OptWs$HEAD$TstampSuffix$OptWs$$""".r
private[sbtdynver] final class Parser(vTagPrefix: Boolean) {
private val Tag = (if (vTagPrefix) """(v[0-9][^+]*?)""" else """([0-9]+\.[^+]*?)""").r

private[sbtdynver] def parse: String ?=> GitDescribeOutput = {
case FromTag(tag, _, dist, sha, dirty) => parse0( tag, dist, sha, dirty)
case FromSha(sha, dirty) => parse0( sha, "0", "", dirty)
case FromHead(dirty) => parse0("HEAD", "0", "", dirty)
}
private val FromTag = s"""^$OptWs$Tag$CommitSuffix?$TstampSuffix?$OptWs$$""".r
private val FromSha = s"""^$OptWs$Sha$TstampSuffix?$OptWs$$""".r
private val FromHead = s"""^$OptWs$HEAD$TstampSuffix$OptWs$$""".r

private[sbtdynver] def parse: String ?=> GitDescribeOutput = {
case FromTag(tag, _, dist, sha, dirty) => parse0(v(tag), dist, sha, dirty)
case FromSha(sha, dirty) => parse0( sha, "0", "", dirty)
case FromHead(dirty) => parse0("HEAD", "0", "", dirty)
}

private def parse0(ref: String, dist: String, sha: String, dirty: String) = {
val commit = if (dist == null || sha == null) GitCommitSuffix(0, "") else GitCommitSuffix(dist.toInt, sha)
GitDescribeOutput(GitRef(ref), commit, GitDirtySuffix(if (dirty eq null) "" else dirty))
// If tags aren't v-prefixed, add the v back, so the rest of dynver works (e.g. GitRef#isTag)
private def v(s: String) = if (vTagPrefix) s else s"v$s"

private def parse0(ref: String, dist: String, sha: String, dirty: String) = {
val commit = if (dist == null || sha == null) GitCommitSuffix(0, "") else GitCommitSuffix(dist.toInt, sha)
GitDescribeOutput(GitRef(ref), commit, GitDirtySuffix(if (dirty eq null) "" else dirty))
}
}

implicit class OptGitDescribeOutputOps(val _x: Option[GitDescribeOutput]) extends AnyVal {
Expand All @@ -170,11 +180,13 @@ object GitDescribeOutput extends ((GitRef, GitCommitSuffix, GitDirtySuffix) => G
}

// sealed just so the companion object can extend it. Shouldn't've been a case class.
sealed case class DynVer(wd: Option[File], separator: String) {
import DynVer._

sealed case class DynVer(wd: Option[File], separator: String, vTagPrefix: Boolean) {
private def this(wd: Option[File], separator: String) = this(wd, separator, true)
private def this(wd: Option[File]) = this(wd, "+")

private val TagPattern = if (vTagPrefix) "v[0-9]*" else "[0-9]*"
private[sbtdynver] val parser = new GitDescribeOutput.Parser(vTagPrefix)

def version(d: Date): String = getGitDescribeOutput(d).versionWithSep(d, separator)
def sonatypeVersion(d: Date): String = getGitDescribeOutput(d).sonatypeVersionWithSep(d, separator)
def previousVersion : Option[String] = getGitPreviousStableTag.previousVersion
Expand All @@ -195,7 +207,7 @@ sealed case class DynVer(wd: Option[File], separator: String) {
val process = Process(s"git describe --long --tags --abbrev=8 --match $TagPattern --always --dirty=+${timestamp(d)}", wd)
Try(process !! impl.NoProcessLogger).toOption
.map(_.replaceAll("-([0-9]+)-g([0-9a-f]{8})", "+$1-$2"))
.map(GitDescribeOutput.parse)
.map(parser.parse)
.flatMap(output =>
if (output.hasNoTags) getDistanceToFirstCommit().map(dist =>
output.copy(commitSuffix = output.commitSuffix.copy(distance = dist))
Expand All @@ -211,7 +223,7 @@ sealed case class DynVer(wd: Option[File], separator: String) {
parentHash <- execAndHandleEmptyOutput("git --no-pager log --pretty=%H -n 1 HEAD^1")
// Find the closest tag of the parent commit
tag <- execAndHandleEmptyOutput(s"git describe --tags --abbrev=0 --match $TagPattern --always $parentHash")
out <- PartialFunction.condOpt(tag)(GitDescribeOutput.parse)
out <- PartialFunction.condOpt(tag)(parser.parse)
} yield out
}

Expand All @@ -227,8 +239,7 @@ sealed case class DynVer(wd: Option[File], separator: String) {
}

object DynVer extends DynVer(None) with (Option[File] => DynVer) {
private val TagPattern = "v[0-9]*"
override def apply(wd: Option[File]) = apply(wd, separator)
override def apply(wd: Option[File]) = apply(wd, separator, vTagPrefix)
}

object `package`
Expand Down
2 changes: 1 addition & 1 deletion src/test/scala/sbtdynver/GH020.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ import org.scalacheck._, Prop._

object GH020 extends Properties("GH020") {
property("Handles CF+LF (Windows)") =
GitDescribeOutput.parse("v0.7.0+3-e7a84ebc+20161120-1948\r\n") ?=
DynVer.parser.parse("v0.7.0+3-e7a84ebc+20161120-1948\r\n") ?=
GitDescribeOutput(GitRef("v0.7.0"), GitCommitSuffix(3, "e7a84ebc"), GitDirtySuffix("+20161120-1948"))
}
2 changes: 1 addition & 1 deletion src/test/scala/sbtdynver/GitDescribeOutputSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ object GitDescribeOutputSpec extends Properties("GitDescribeOutputSpec") {

def test(v: String, ref: String, dist: Int, sha: String, dirtySuffix: String) = {
val out = GitDescribeOutput(GitRef(ref), GitCommitSuffix(dist, sha), GitDirtySuffix(dirtySuffix))
property(s"parses $v") = GitDescribeOutput.parse(v) ?= out
property(s"parses $v") = DynVer.parser.parse(v) ?= out
}
}
16 changes: 10 additions & 6 deletions src/test/scala/sbtdynver/RepoStates.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,26 @@ import scala.collection.JavaConverters._
import org.eclipse.jgit.api._
import org.eclipse.jgit.merge.MergeStrategy

object RepoStates {
def notAGitRepo() = State()
object RepoStates extends RepoStates(vTagPrefix = true)

sealed class RepoStates(vTagPrefix: Boolean) {
def notAGitRepo() = new State()
def noCommits() = notAGitRepo().init()
def onCommit() = noCommits().commit().commit().commit()
def onCommitDirty() = onCommit().dirty()
def onTag(n: String = "v1.0.0") = onCommit().tag(n)
def onTag(n: String = "1.0.0") = onCommit().tag(vOptPrefix(n))
def onTagDirty() = onTag().dirty()
def onTagAndCommit() = onTag().commit()
def onTagAndCommitDirty() = onTagAndCommit().dirty()
def onTagAndSecondCommit() = onTagAndCommitDirty().commit()
def onSecondTag(n: String = "v2.0.0") = onTagAndSecondCommit().tag(n)
def onSecondTag(n: String = "2.0.0") = onTagAndSecondCommit().tag(vOptPrefix(n))

private def vOptPrefix(s: String) = if (s.startsWith("v")) s else if (vTagPrefix) s"v$s" else s

final case class State() {
final class State() {
val dir = doto(Files.createTempDirectory(s"dynver-test-").toFile)(_.deleteOnExit())
val date = new GregorianCalendar(2016, 8, 17).getTime
val dynver = DynVer(Some(dir))
val dynver = DynVer(Some(dir), DynVer.separator, vTagPrefix)

var git: Git = _
var sha: String = "undefined"
Expand Down
17 changes: 17 additions & 0 deletions src/test/scala/sbtdynver/TagPatternSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package sbtdynver

import org.scalacheck._, Prop._

object TagPatternVersionSpec extends Properties("TagPatternVersionSpec") {
val repoStates = new RepoStates(vTagPrefix = false)
import repoStates._

property("not a git repo") = notAGitRepo().version() ?= "HEAD+20160917-0000"
property("no commits") = noCommits().version() ?= "HEAD+20160917-0000"
property("on commit, w/o local changes") = onCommit().version() ?= "0.0.0+3-1234abcd"
property("on commit with local changes") = onCommitDirty().version() ?= "0.0.0+3-1234abcd+20160917-0000"
property("on tag 1.0.0, w/o local changes") = onTag().version() ?= "1.0.0"
property("on tag 1.0.0 with local changes") = onTagDirty().version() ?= "1.0.0+0-1234abcd+20160917-0000"
property("on tag 1.0.0 and 1 commit, w/o local changes") = onTagAndCommit().version() ?= "1.0.0+1-1234abcd"
property("on tag 1.0.0 and 1 commit with local changes") = onTagAndCommitDirty().version() ?= "1.0.0+1-1234abcd+20160917-0000"
}

0 comments on commit 28c3133

Please sign in to comment.