-
Notifications
You must be signed in to change notification settings - Fork 163
/
Version.scala
159 lines (130 loc) · 5.55 KB
/
Version.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
package sbtrelease
import scala.util.matching.Regex
import util.control.Exception.*
object Version {
sealed trait Bump {
def bump: Version => Version
}
object Bump {
/**
* Strategy to always bump the major version by default. Ex. 1.0.0 would be bumped to 2.0.0
*/
case object Major extends Bump { def bump: Version => Version = _.bumpMajor }
/**
* Strategy to always bump the minor version by default. Ex. 1.0.0 would be bumped to 1.1.0
*/
case object Minor extends Bump { def bump: Version => Version = _.bumpMinor }
/**
* Strategy to always bump the bugfix version by default. Ex. 1.0.0 would be bumped to 1.0.1
*/
case object Bugfix extends Bump { def bump: Version => Version = _.bumpBugfix }
/**
* Strategy to always bump the nano version by default. Ex. 1.0.0.0 would be bumped to 1.0.0.1
*/
case object Nano extends Bump { def bump: Version => Version = _.bumpNano }
/**
* Strategy to always increment to the next version from smallest to greatest, including prerelease versions
* Ex:
* Major: 1 becomes 2
* Minor: 1.0 becomes 1.1
* Bugfix: 1.0.0 becomes 1.0.1
* Nano: 1.0.0.0 becomes 1.0.0.1
* Qualifier with version number: 1.0-RC1 becomes 1.0-RC2
* Qualifier without version number: 1.0-alpha becomes 1.0
*/
case object Next extends Bump { def bump: Version => Version = _.bumpNext }
/**
* Strategy to always increment to the next version from smallest to greatest, excluding prerelease versions
* Ex:
* Major: 1 becomes 2
* Minor: 1.0 becomes 1.1
* Bugfix: 1.0.0 becomes 1.0.1
* Nano: 1.0.0.0 becomes 1.0.0.1
* Qualifier with version number: 1.0-RC1 becomes 1.0
* Qualifier without version number: 1.0-alpha becomes 1.0
*/
case object NextStable extends Bump { def bump: Version => Version = _.bumpNextStable }
val default: Bump = Next
}
val VersionR: Regex = """([0-9]+)((?:\.[0-9]+)+)?([\.\-0-9a-zA-Z]*)?""".r
val PreReleaseQualifierR: Regex = """[\.-](?i:rc|m|alpha|beta)[\.-]?[0-9]*""".r
def apply(s: String): Option[Version] = {
allCatch opt {
val VersionR(maj, subs, qual) = s
// parse the subversions (if any) to a Seq[Int]
val subSeq: Seq[Int] = Option(subs) map { str =>
// split on . and remove empty strings
str.split('.').filterNot(_.trim.isEmpty).map(_.toInt).toSeq
} getOrElse Nil
Version(maj.toInt, subSeq, Option(qual).filterNot(_.isEmpty))
}
}
}
case class Version(major: Int, subversions: Seq[Int], qualifier: Option[String]) {
@deprecated("Use .bumpNext or .bumpNextStable instead")
def bump: Version = bumpNext
def bumpNext: Version = {
val bumpedPrereleaseVersionOpt = qualifier.collect {
case rawQualifier @ Version.PreReleaseQualifierR() =>
val qualifierEndsWithNumberRegex = """[0-9]*$""".r
val opt = for {
versionNumberQualifierStr <- qualifierEndsWithNumberRegex.findFirstIn(rawQualifier)
versionNumber <- Try(versionNumberQualifierStr.toInt)
.toRight(new Exception(s"Version number not parseable to a number. Version number received: $versionNumberQualifierStr"))
.toOption
newVersionNumber = versionNumber + 1
newQualifier = rawQualifier.replaceFirst(versionNumberQualifierStr, newVersionNumber.toString)
} yield Version(major, subversions, Some(newQualifier))
opt.getOrElse(this.withoutQualifier)
}
bumpNextGeneric(bumpedPrereleaseVersionOpt)
}
private def bumpNextGeneric(bumpedPrereleaseVersionOpt: Option[Version]): Version = {
def maybeBumpedLastSubversion = bumpSubversionOpt(subversions.length - 1)
def bumpedMajor = copy(major = major + 1)
bumpedPrereleaseVersionOpt
.orElse(maybeBumpedLastSubversion)
.getOrElse(bumpedMajor)
}
def bumpNextStable: Version = {
val bumpedPrereleaseVersionOpt = qualifier.collect {
case Version.PreReleaseQualifierR() => withoutQualifier
}
bumpNextGeneric(bumpedPrereleaseVersionOpt)
}
def bumpMajor: Version = copy(major = major + 1, subversions = Seq.fill(subversions.length)(0))
def bumpMinor: Version = maybeBumpSubversion(0)
def bumpBugfix: Version = maybeBumpSubversion(1)
def bumpNano: Version = maybeBumpSubversion(2)
def maybeBumpSubversion(idx: Int): Version = bumpSubversionOpt(idx) getOrElse this
private def bumpSubversionOpt(idx: Int) = {
val bumped = subversions.drop(idx)
val reset = bumped.drop(1).length
bumped.headOption map { head =>
val patch = (head+1) +: Seq.fill(reset)(0)
copy(subversions = subversions.patch(idx, patch, patch.length))
}
}
def bump(bumpType: Version.Bump): Version = bumpType.bump(this)
def withoutQualifier: Version = copy(qualifier = None)
def asSnapshot: Version = copy(qualifier = qualifier.map { qualifierStr =>
s"$qualifierStr-SNAPSHOT"
}.orElse(Some("-SNAPSHOT")))
def isSnapshot: Boolean = qualifier.exists { qualifierStr =>
val snapshotRegex = """(^.*)-SNAPSHOT$""".r
qualifierStr.matches(snapshotRegex.regex)
}
def withoutSnapshot: Version = copy(qualifier = qualifier.flatMap { qualifierStr =>
val snapshotRegex = """-SNAPSHOT""".r
val newQualifier = snapshotRegex.replaceFirstIn(qualifierStr, "")
if (newQualifier == qualifierStr) {
None
} else {
Some(newQualifier)
}
})
@deprecated("Use .unapply instead")
def string: String = unapply
def unapply: String = "" + major + mkString(subversions) + qualifier.getOrElse("")
private def mkString(parts: Seq[Int]) = parts.map("."+_).mkString
}