-
Notifications
You must be signed in to change notification settings - Fork 3.1k
/
Copy pathVersionUtil.scala
202 lines (178 loc) · 9.45 KB
/
VersionUtil.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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
package scala.build
import sbt._
import Keys._
import java.util.{Date, Locale, Properties, TimeZone}
import java.io.{File, FileInputStream, StringWriter}
import java.text.SimpleDateFormat
import java.time.Instant
import java.time.format.DateTimeFormatter.ISO_DATE_TIME
import scala.collection.JavaConverters._
import BuildSettings.autoImport._
object VersionUtil {
lazy val copyrightString = settingKey[String]("Copyright string.")
lazy val shellBannerString = settingKey[String]("Shell welcome banner string.")
lazy val versionProperties = settingKey[Versions]("Version properties.")
lazy val gitProperties = settingKey[GitProperties]("Current git information")
lazy val buildCharacterPropertiesFile = settingKey[File]("The file which gets generated by generateBuildCharacterPropertiesFile")
lazy val generateVersionPropertiesFile = taskKey[File]("Generate version properties file.")
lazy val generateBuildCharacterPropertiesFile = taskKey[File]("Generate buildcharacter.properties file.")
lazy val extractBuildCharacterPropertiesFile = taskKey[File]("Extract buildcharacter.properties file from bootstrap scala-compiler.")
lazy val globalVersionSettings = Seq[Setting[_]](
// Set the version properties globally (they are the same for all projects)
Global / versionProperties := versionPropertiesImpl.value,
gitProperties := gitPropertiesImpl.value,
Global / version := versionProperties.value.mavenVersion
)
lazy val generatePropertiesFileSettings = Seq[Setting[_]](
copyrightString := "Copyright 2002-2025, LAMP/EPFL and Lightbend, Inc. dba Akka",
shellBannerString := """
| ________ ___ / / ___
| / __/ __// _ | / / / _ |
| __\ \/ /__/ __ |/ /__/ __ |
| /____/\___/_/ |_/____/_/ | |
| |/ %s""".stripMargin.linesIterator.mkString("%n"),
Compile / resourceGenerators += generateVersionPropertiesFile.map(file => Seq(file)).taskValue,
generateVersionPropertiesFile := generateVersionPropertiesFileImpl.value
)
lazy val generateBuildCharacterFileSettings = Seq[Setting[_]](
buildCharacterPropertiesFile := ((ThisBuild / baseDirectory).value / "buildcharacter.properties"),
generateBuildCharacterPropertiesFile := generateBuildCharacterPropertiesFileImpl.value
)
case class Versions(canonicalVersion: String, mavenBase: String, mavenSuffix: String, osgiVersion: String, commitSha: String, commitDate: String, isRelease: Boolean) {
val githubTree =
if(isRelease) "v" + mavenVersion
else if(commitSha != "unknown") commitSha
else "master"
def mavenVersion: String = mavenBase + mavenSuffix
override def toString = s"Canonical: $canonicalVersion, Maven: $mavenVersion, OSGi: $osgiVersion, github: $githubTree"
def toMap: Map[String, String] = Map(
"version.number" -> canonicalVersion,
"maven.version.number" -> mavenVersion,
"osgi.version.number" -> osgiVersion
)
}
case class GitProperties(date: String, sha: String)
private lazy val gitPropertiesImpl: Def.Initialize[GitProperties] = Def.setting {
val log = sLog.value
val (dateObj, sha) = {
try {
// Use JGit to get the commit date and SHA
import org.eclipse.jgit.revwalk.RevWalk
import org.eclipse.jgit.storage.file.FileRepositoryBuilder
val db = new FileRepositoryBuilder().findGitDir.build
val head = db.resolve("HEAD")
if (head eq null) {
import scala.sys.process._
try {
// Workaround lack of git worktree support in JGit https://bugs.eclipse.org/bugs/show_bug.cgi?id=477475
val sha = List("git", "rev-parse", "HEAD").!!.trim
val commitDateIso = List("git", "log", "-1", "--format=%cI", "HEAD").!!.trim
val date = Date.from(ISO_DATE_TIME.parse(commitDateIso, Instant.from _))
(date, sha.substring(0, 7))
} catch {
case ex: Exception =>
ex.printStackTrace()
log.info("No git HEAD commit found -- Using current date and 'unknown' SHA")
(new Date, "unknown")
}
} else {
val commit = new RevWalk(db).parseCommit(head)
(new Date(commit.getCommitTime.toLong * 1000L), commit.getName.substring(0, 7))
}
} catch {
case ex: Exception =>
log.error("Could not determine commit date + SHA: " + ex)
log.trace(ex)
(new Date, "unknown")
}
}
val date = {
val df = new SimpleDateFormat("yyyyMMdd-HHmmss", Locale.ENGLISH)
df.setTimeZone(TimeZone.getTimeZone("UTC"))
df.format(dateObj)
}
GitProperties(date, sha)
}
/** Compute the canonical, Maven and OSGi version number from `baseVersion` and `baseVersionSuffix`.
* Examples of the generated versions:
*
* ("2.11.8", "SNAPSHOT" ) -> ("2.11.8-20151215-133023-7559aed", "2.11.8-bin-SNAPSHOT", "2.11.8.v20151215-133023-7559aed")
* ("2.11.8", "SHA-SNAPSHOT") -> ("2.11.8-20151215-133023-7559aed", "2.11.8-bin-7559aed-SNAPSHOT", "2.11.8.v20151215-133023-7559aed")
* ("2.11.8", "SHA" ) -> ("2.11.8-7559aed", "2.11.8-bin-7559aed", "2.11.8.v20151215-133023-7559aed")
* ("2.11.0", "SHA" ) -> ("2.11.0-7559aed", "2.11.0-pre-7559aed", "2.11.0.v20151215-133023-7559aed")
* ("2.11.8", "" ) -> ("2.11.8", "2.11.8", "2.11.8.v20151215-133023-VFINAL-7559aed")
* ("2.11.8", "M3" ) -> ("2.11.8-M3", "2.11.8-M3", "2.11.8.v20151215-133023-M3-7559aed")
* ("2.11.8", "RC4" ) -> ("2.11.8-RC4", "2.11.8-RC4", "2.11.8.v20151215-133023-RC4-7559aed")
* ("2.11.8-RC4", "SPLIT" ) -> ("2.11.8-RC4", "2.11.8-RC4", "2.11.8.v20151215-133023-RC4-7559aed")
*
* A `baseVersionSuffix` of "SNAPSHOT" is the default, which is used for local snapshot builds. The PR validation
* job uses "SHA-SNAPSHOT". A proper version number for an integration build can be computed with "SHA". An empty
* suffix is used for releases. All other suffix values are treated as RC / milestone builds. The special suffix
* value "SPLIT" is used to split the real suffix off from `baseVersion` instead and then apply the usual logic. */
private lazy val versionPropertiesImpl: Def.Initialize[Versions] = Def.setting {
val (date, sha) = (gitProperties.value.date, gitProperties.value.sha)
val (base, suffix) = {
val (b, s) = (baseVersion.value, baseVersionSuffix.value)
if(s == "SPLIT") {
val split = """([\w+.]+)(-[\w+.-]+)??""".r
val split(b2, sOrNull) = b
(b2, Option(sOrNull).map(_.drop(1)).getOrElse(""))
} else (b, s)
}
val Patch = """\d+\.\d+\.(\d+)""".r
def cross = base match {
case Patch(p) if p.toInt > 0 => "bin"
case _ => "pre"
}
val (canonicalV, mavenSuffix, osgiV, release) = suffix match {
case "SNAPSHOT" => (s"$base-$date-$sha", s"-$cross-SNAPSHOT", s"$base.v$date-$sha", false)
case "SHA-SNAPSHOT" => (s"$base-$date-$sha", s"-$cross-$sha-SNAPSHOT", s"$base.v$date-$sha", false)
case "SHA-TEST-SNAPSHOT" => (s"$base-$date-$sha", s"-$cross-$sha-TEST-SNAPSHOT", s"$base.v$date-$sha", false)
case "SHA" => (s"$base-$sha", s"-$cross-$sha", s"$base.v$date-$sha", false)
case "" => (s"$base", "", s"$base.v$date-VFINAL-$sha", true)
case _ => (s"$base-$suffix", s"-$suffix", s"$base.v$date-$suffix-$sha", true)
}
Versions(canonicalV, base, mavenSuffix, osgiV, sha, date, release)
}
private lazy val generateVersionPropertiesFileImpl: Def.Initialize[Task[File]] = Def.task {
writeProps(versionProperties.value.toMap ++ Seq(
"copyright.string" -> copyrightString.value,
"shell.banner" -> shellBannerString.value
),
(Compile / resourceManaged).value / s"${thisProject.value.id}.properties")
}
private lazy val generateBuildCharacterPropertiesFileImpl: Def.Initialize[Task[File]] = Def.task {
val v = versionProperties.value
writeProps(v.toMap ++ versionProps ++ Map(
"maven.version.base" -> v.mavenBase,
"maven.version.suffix" -> v.mavenSuffix
), buildCharacterPropertiesFile.value)
}
private def writeProps(m: Map[String, String], propFile: File): File = {
// Like:
// IO.write(props, null, propFile)
// But with deterministic key ordering and no timestamp
val fullWriter = new StringWriter()
for (k <- m.keySet.toVector.sorted) {
val writer = new StringWriter()
val props = new Properties()
props.put(k, m(k))
props.store(writer, null)
writer.toString.linesIterator.drop(1).foreach{line => fullWriter.write(line); fullWriter.write("\n")}
}
IO.write(propFile, fullWriter.toString)
propFile
}
private[build] def readProps(f: File): Map[String, String] = {
val props = new Properties()
val in = new FileInputStream(f)
try props.load(in)
finally in.close()
props.asScala.toMap
}
/** The global versions.properties data */
lazy val versionProps: Map[String, String] = {
val versionProps = readProps(file("versions.properties"))
versionProps.map { case (k, v) => (k, sys.props.getOrElse(k, v)) } // allow sys props to override versions.properties
}
}