Migrate the plugin to support SBT 2.
This commit is contained in:
parent
8de0ac74e1
commit
3c7156a01b
14 changed files with 366 additions and 224 deletions
|
|
@ -54,7 +54,8 @@ values for `release` are:
|
|||
- `patch`
|
||||
|
||||
```
|
||||
sbt -Drelease=minor publish
|
||||
sbt setVersion minor release
|
||||
sbt publish
|
||||
```
|
||||
|
||||
## Supported Setting Keys
|
||||
|
|
|
|||
65
build.sbt
65
build.sbt
|
|
@ -1,39 +1,21 @@
|
|||
ThisBuild / scalaVersion := "3.8.4"
|
||||
ThisBuild / organizationName := "garrity software"
|
||||
ThisBuild / organization := "gs"
|
||||
ThisBuild / versionScheme := Some("semver-spec")
|
||||
|
||||
externalResolvers := Seq(
|
||||
"Garrity Software Releases" at "https://maven.garrity.co/gs",
|
||||
"Garrity Software Maven Mirror" at "https://maven.garrity.co/releases"
|
||||
)
|
||||
/* externalResolvers := Seq( "Garrity Software Releases" at
|
||||
* "https://maven.garrity.co/gs", "Garrity Software Maven Mirror" at
|
||||
* "https://maven.garrity.co/releases" ) */
|
||||
|
||||
resolvers += Resolver.mavenLocal
|
||||
|
||||
val ProjectName: String = "sbt-gs-semver"
|
||||
val Description: String = "SBT 1.9.0+ plugin for Git-based semantic versioning."
|
||||
val Description: String = "SBT 2.0.0+ plugin for Git-based semantic versioning."
|
||||
|
||||
def getProperty[A](
|
||||
name: String,
|
||||
conv: String => A
|
||||
): Option[A] =
|
||||
Option(System.getProperty(name)).map(conv)
|
||||
val CurrentVersion: String = "0.4.0-SNAPSHOT"
|
||||
|
||||
val VersionProperty: String = "version"
|
||||
val ReleaseProperty: String = "release"
|
||||
|
||||
lazy val InputVersion: Option[String] =
|
||||
getProperty(VersionProperty, identity)
|
||||
|
||||
lazy val IsRelease: Boolean =
|
||||
getProperty(ReleaseProperty, _.toBoolean).getOrElse(false)
|
||||
|
||||
lazy val Modifier: String =
|
||||
if (IsRelease) "" else "-SNAPSHOT"
|
||||
|
||||
val DefaultVersion: String = "0.1.0-SNAPSHOT"
|
||||
|
||||
lazy val SelectedVersion: String =
|
||||
InputVersion
|
||||
.map(v => s"$v$Modifier")
|
||||
.getOrElse(DefaultVersion)
|
||||
def isRelease(): Boolean =
|
||||
!CurrentVersion.contains("SNAPSHOT")
|
||||
|
||||
lazy val publishSettings = Seq(
|
||||
publishMavenStyle := true,
|
||||
|
|
@ -47,14 +29,16 @@ lazy val publishSettings = Seq(
|
|||
),
|
||||
description := Description,
|
||||
licenses := List(
|
||||
"MIT" -> url("https://garrity.co/MIT.html")
|
||||
"MIT" -> url(
|
||||
s"https://git.garrity.co/garrity-software/$ProjectName/LICENSE"
|
||||
)
|
||||
),
|
||||
homepage := Some(
|
||||
url(s"https://git.garrity.co/garrity-software/$ProjectName")
|
||||
),
|
||||
publishTo := {
|
||||
val repo = "https://maven.garrity.co/"
|
||||
if (!IsRelease)
|
||||
if (!isRelease())
|
||||
Some("Garrity Software Maven" at repo + "snapshots")
|
||||
else
|
||||
Some("Garrity Software Maven" at repo + "gs")
|
||||
|
|
@ -66,21 +50,26 @@ lazy val root = Project(ProjectName, file("."))
|
|||
.settings(publishSettings)
|
||||
.settings(
|
||||
name := ProjectName,
|
||||
version := SelectedVersion,
|
||||
version := CurrentVersion,
|
||||
libraryDependencies ++= Seq(
|
||||
"com.lihaoyi" %% "os-lib" % "0.9.3"
|
||||
),
|
||||
pluginCrossBuild / sbtVersion := {
|
||||
scalaBinaryVersion.value match {
|
||||
// Minimum SBT version compatible with this plugin.
|
||||
case "2.12" => "1.9.0"
|
||||
}
|
||||
"2.0.0"
|
||||
},
|
||||
scalacOptions := Seq(
|
||||
"-unchecked",
|
||||
"-encoding",
|
||||
"utf8",
|
||||
"-deprecation",
|
||||
"-feature",
|
||||
"-encoding",
|
||||
"utf8"
|
||||
"-explain",
|
||||
"-unchecked",
|
||||
"-explain-types",
|
||||
"-language:strictEquality",
|
||||
"-Wunused:implicits",
|
||||
"-Wunused:explicits",
|
||||
"-Wunused:imports",
|
||||
"-Wunused:locals",
|
||||
"-Wunused:privates"
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
sbt.version=1.9.9
|
||||
sbt.version=2.0.3
|
||||
|
|
|
|||
43
src/main/scala/gs/BuildType.scala
Normal file
43
src/main/scala/gs/BuildType.scala
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
package gs
|
||||
|
||||
/** Defines build behavior.
|
||||
*
|
||||
* - `snapshot`: Produce a snapshot, appends `-SNAPSHOT` to the version.
|
||||
* - `release`: Produce a release build. No version string augmentations.
|
||||
*
|
||||
* @param name
|
||||
* The formal name of the build type.
|
||||
*/
|
||||
sealed abstract class BuildType(val name: String):
|
||||
|
||||
override def equals(obj: Any): Boolean =
|
||||
obj match
|
||||
case other: BuildType => name == other.name
|
||||
case _ => false
|
||||
|
||||
override def hashCode(): Int = name.hashCode()
|
||||
|
||||
override def toString(): String = name
|
||||
|
||||
object BuildType:
|
||||
given CanEqual[BuildType, BuildType] = CanEqual.derived
|
||||
|
||||
case object Release extends BuildType("release")
|
||||
case object Snapshot extends BuildType("snapshot")
|
||||
|
||||
def parse(candidate: String): Option[BuildType] =
|
||||
candidate.trim().toLowerCase() match {
|
||||
case Release.name => Some(Release)
|
||||
case Snapshot.name => Some(Snapshot)
|
||||
case _ => None
|
||||
}
|
||||
|
||||
def parse(
|
||||
candidate: String,
|
||||
defaultValue: BuildType
|
||||
): BuildType =
|
||||
parse(candidate).getOrElse(defaultValue)
|
||||
|
||||
def isValid(candidate: String): Boolean =
|
||||
candidate.equalsIgnoreCase(Release.name)
|
||||
|| candidate.equalsIgnoreCase(Snapshot.name)
|
||||
|
|
@ -1,9 +1,6 @@
|
|||
package gs
|
||||
|
||||
import java.io.File
|
||||
import scala.util.Try
|
||||
|
||||
object Git {
|
||||
object Git:
|
||||
|
||||
/** Get the latest tag on this repository and attempt to parse it as a
|
||||
* [[SemVer]]. This value is retrieved using the `git` command. If for some
|
||||
|
|
@ -16,7 +13,7 @@ object Git {
|
|||
* @return
|
||||
* The latest semantic version according to the current Git repository.
|
||||
*/
|
||||
def getLatestSemVer(): SemVer = {
|
||||
def getLatestSemVer(): SemVer =
|
||||
// Suppress all error output. Suppress exceptions for the failed process.
|
||||
// Capture the standard output and parse it if the command succeeds.
|
||||
val result = os
|
||||
|
|
@ -38,6 +35,3 @@ object Git {
|
|||
val processOutput = result.out.text().trim()
|
||||
SemVer.parse(processOutput).getOrElse(SemVer.DefaultVersion)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
package gs
|
||||
|
||||
case class InvalidSemVerException(candidate: String) extends RuntimeException {
|
||||
|
||||
override def getMessage(): String =
|
||||
s"The candidate SemVer '$candidate' is not valid."
|
||||
|
||||
}
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
package gs
|
||||
|
||||
import sbt._
|
||||
|
||||
/** Helper for managing all SBT command line properties (`-Dproperty=value`).
|
||||
*/
|
||||
object PluginProperties {
|
||||
|
||||
/** Helper to extract the value from `-Dproperty=value`.
|
||||
*
|
||||
* @param name
|
||||
* The property name.
|
||||
* @param conv
|
||||
* The conversion function to the output type.
|
||||
* @return
|
||||
* The converted value, or `None` if no value exists.
|
||||
*/
|
||||
def getProperty[A](
|
||||
name: String,
|
||||
conv: String => A
|
||||
): Option[A] =
|
||||
Option(System.getProperty(name)).map(conv)
|
||||
|
||||
/** Use one of the following to calculate an incremented release version:
|
||||
*
|
||||
* - `sbt -Drelease=major`
|
||||
* - `sbt -Drelease=minor`
|
||||
* - `sbt -Drelease=patch`
|
||||
*/
|
||||
val ReleaseProperty: String = "release"
|
||||
|
||||
/** If true, or no release type is set, this build is treated as a
|
||||
* pre-release, and the suffix `-SNAPSHOT` is appended to the version.
|
||||
*/
|
||||
val SnapshotProperty: String = "snapshot"
|
||||
|
||||
/** The value of `-Drelease=<value>`, parsed and validated. Influences the
|
||||
* final calculated [[SemVer]].
|
||||
*
|
||||
* @return
|
||||
* The version passed as input to SBT.
|
||||
*/
|
||||
def getReleaseType(): Option[ReleaseType] =
|
||||
getProperty(
|
||||
ReleaseProperty,
|
||||
raw =>
|
||||
ReleaseType.parse(raw) match {
|
||||
case scala.util.Success(value) => value
|
||||
case scala.util.Failure(e) => throw e
|
||||
}
|
||||
)
|
||||
|
||||
/** The value of `-Dsnapshot=<value>`, parsed and validated. If parsing
|
||||
* fails or no value is present, `false` is returned. Influences whether
|
||||
* the pre-release suffix (`-SNAPSHOT`) is appended to the version.
|
||||
*
|
||||
* @return
|
||||
* `true` if `-Dsnapshot=true` is specified at the command line, `false`
|
||||
* otherwise.
|
||||
*/
|
||||
def isSnapshot(): Boolean =
|
||||
getProperty(SnapshotProperty, raw => raw.trim().equalsIgnoreCase("true"))
|
||||
.getOrElse(false)
|
||||
|
||||
}
|
||||
|
|
@ -1,25 +1,47 @@
|
|||
package gs
|
||||
|
||||
import scala.util.Failure
|
||||
import scala.util.Success
|
||||
import scala.util.Try
|
||||
/** Defines versioning behavior.
|
||||
*
|
||||
* - `major`: Increment the major version. `1.2.3 => 2.0.0`
|
||||
* - `minor`: Increment the minor version. `1.2.3 => 1.3.0`
|
||||
* - `patch`: Increment the patch version. `1.2.3 => 1.2.4`
|
||||
*
|
||||
* @param name
|
||||
* The formal name of the release type.
|
||||
*/
|
||||
sealed abstract class ReleaseType(val name: String):
|
||||
|
||||
sealed abstract class ReleaseType(val name: String)
|
||||
override def equals(obj: Any): Boolean =
|
||||
obj match
|
||||
case other: ReleaseType => name == other.name
|
||||
case _ => false
|
||||
|
||||
override def hashCode(): Int = name.hashCode()
|
||||
|
||||
override def toString(): String = name
|
||||
|
||||
object ReleaseType:
|
||||
given CanEqual[ReleaseType, ReleaseType] = CanEqual.derived
|
||||
|
||||
object ReleaseType {
|
||||
case object Major extends ReleaseType("major")
|
||||
case object Minor extends ReleaseType("minor")
|
||||
case object Patch extends ReleaseType("patch")
|
||||
|
||||
def parse(candidate: String): Try[ReleaseType] =
|
||||
def parse(candidate: String): Option[ReleaseType] =
|
||||
candidate.trim().toLowerCase() match {
|
||||
case Major.name => Success(Major)
|
||||
case Minor.name => Success(Minor)
|
||||
case Patch.name => Success(Patch)
|
||||
case _ =>
|
||||
Failure(
|
||||
new IllegalArgumentException(s"Invalid release type: '$candidate'")
|
||||
)
|
||||
case Major.name => Some(Major)
|
||||
case Minor.name => Some(Minor)
|
||||
case Patch.name => Some(Patch)
|
||||
case _ => None
|
||||
}
|
||||
|
||||
}
|
||||
def parse(
|
||||
candidate: String,
|
||||
defaultValue: ReleaseType
|
||||
): ReleaseType =
|
||||
parse(candidate).getOrElse(defaultValue)
|
||||
|
||||
def isValid(candidate: String): Boolean =
|
||||
candidate.equalsIgnoreCase(Major.name)
|
||||
|| candidate.equalsIgnoreCase(Minor.name)
|
||||
|| candidate.equalsIgnoreCase(Patch.name)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package gs
|
||||
|
||||
import scala.util.matching.Regex
|
||||
import sjsonnew.*
|
||||
import sjsonnew.BasicJsonProtocol.*
|
||||
|
||||
/** Simplified representation of SemVer. Only supports major, minor and patch
|
||||
* versions. Not intended to be used as a generalized SemVer implementation.
|
||||
|
|
@ -35,10 +37,10 @@ case class SemVer(
|
|||
* @return
|
||||
* The string rendition of this version.
|
||||
*/
|
||||
def render(isSnapshot: Boolean): String = {
|
||||
val version = toString()
|
||||
if (isSnapshot) version + "-SNAPSHOT" else version
|
||||
}
|
||||
def render(buildType: BuildType): String =
|
||||
buildType match
|
||||
case BuildType.Release => s"$major.$minor.$patch"
|
||||
case BuildType.Snapshot => s"$major.$minor.$patch-SNAPSHOT"
|
||||
|
||||
/** @return
|
||||
* Copy of this SemVer with the major version incremented by 1.
|
||||
|
|
@ -63,18 +65,58 @@ case class SemVer(
|
|||
* Copy of this SemVer with the patch version incremented by 1.
|
||||
*/
|
||||
def incrementPatch(): SemVer = copy(patch = this.patch + 1)
|
||||
|
||||
def applyReleaseType(releaseType: ReleaseType): SemVer =
|
||||
releaseType match
|
||||
case ReleaseType.Major => incrementMajor()
|
||||
case ReleaseType.Minor => incrementMinor()
|
||||
case ReleaseType.Patch => incrementPatch()
|
||||
|
||||
}
|
||||
|
||||
object SemVer {
|
||||
|
||||
/** Version 0.1.0
|
||||
object SemVer:
|
||||
/** Version 0.1.0 -- the default version.
|
||||
*/
|
||||
val DefaultVersion: SemVer = SemVer(0, 1, 0)
|
||||
|
||||
given CanEqual[SemVer, SemVer] = CanEqual.derived
|
||||
|
||||
// Required for SBT serialization.
|
||||
given JsonFormat[SemVer] = new JsonFormat[SemVer] {
|
||||
|
||||
override def write[J](
|
||||
obj: SemVer,
|
||||
builder: Builder[J]
|
||||
): Unit =
|
||||
builder.beginObject()
|
||||
builder.addField("major", obj.major)
|
||||
builder.addField("minor", obj.minor)
|
||||
builder.addField("patch", obj.patch)
|
||||
builder.endObject()
|
||||
|
||||
override def read[J](
|
||||
jsOpt: Option[J],
|
||||
unbuilder: Unbuilder[J]
|
||||
): SemVer =
|
||||
jsOpt match
|
||||
case Some(js) =>
|
||||
unbuilder.beginObject(js)
|
||||
val major = unbuilder.readField[Int]("major")
|
||||
val minor = unbuilder.readField[Int]("minor")
|
||||
val patch = unbuilder.readField[Int]("patch")
|
||||
unbuilder.endObject()
|
||||
SemVer(major, minor, patch)
|
||||
case None =>
|
||||
deserializationError("Expected JsObject but found None")
|
||||
|
||||
}
|
||||
|
||||
private val SemVerPattern: Regex =
|
||||
"^(0|(?:[1-9][0-9]*))\\.(0|(?:[1-9][0-9]*))\\.(0|(?:[1-9][0-9]*))$".r
|
||||
|
||||
/** Attempt to parse the input string as a [[SemVer]].
|
||||
*
|
||||
* This does not handle or produce snapshot versions.
|
||||
*
|
||||
* @param candidate
|
||||
* The candidate string to parse.
|
||||
|
|
@ -82,11 +124,8 @@ object SemVer {
|
|||
* The parsed [[SemVer]], or `None` if parsing failed.
|
||||
*/
|
||||
def parse(candidate: String): Option[SemVer] =
|
||||
candidate match {
|
||||
candidate match
|
||||
case SemVerPattern(major, minor, patch) =>
|
||||
Some(SemVer(major.toInt, minor.toInt, patch.toInt))
|
||||
case _ =>
|
||||
None
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
19
src/main/scala/gs/SemVerCalculator.scala
Normal file
19
src/main/scala/gs/SemVerCalculator.scala
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
package gs
|
||||
|
||||
object SemVerCalculator:
|
||||
|
||||
def update(
|
||||
baseVersion: SemVer,
|
||||
releaseType: ReleaseType,
|
||||
buildType: BuildType
|
||||
): (SemVer, String) =
|
||||
val updated = baseVersion.applyReleaseType(releaseType)
|
||||
updated -> updated.render(buildType)
|
||||
|
||||
def updateGit(
|
||||
releaseType: ReleaseType,
|
||||
buildType: BuildType
|
||||
): (SemVer, String) =
|
||||
update(Git.getLatestSemVer(), releaseType, buildType)
|
||||
|
||||
end SemVerCalculator
|
||||
|
|
@ -1,10 +1,17 @@
|
|||
package gs
|
||||
|
||||
import sbt._
|
||||
import sbt.*
|
||||
|
||||
/** Defines all setting and task keys for the GS SemVer SBT Plugin.
|
||||
*/
|
||||
object SemVerKeys {
|
||||
object SemVerKeys:
|
||||
|
||||
/** SBT Setting for the calculated SemVer string, to be used for all project
|
||||
* versions.
|
||||
*/
|
||||
lazy val semVer = settingKey[String](
|
||||
"Rendered version calculated by the SemVer plugin."
|
||||
)
|
||||
|
||||
/** SBT Setting for the latest known project version (before this build).
|
||||
*
|
||||
|
|
@ -19,30 +26,9 @@ object SemVerKeys {
|
|||
|
||||
/** SBT Setting for the **selected** project version.
|
||||
*
|
||||
* Use this value to set your project version:
|
||||
*
|
||||
* {{{
|
||||
* version := semVerSelected.value
|
||||
* }}}
|
||||
*
|
||||
* This value should be used as the version for all project artifacts. It is
|
||||
* automatically derived based on the `semVerCurrent` setting, the value of
|
||||
* the `release` property, and the value of the `snapshot` property:
|
||||
*
|
||||
* Consider the example current version of `1.2.3`:
|
||||
*
|
||||
* - `-Drelease=major`: `semVerSelected = 2.0.0`
|
||||
* - `-Drelease=major`, `-Dsnapshot=true`: `semVerSelected =
|
||||
* 2.0.0-SNAPSHOT`
|
||||
* - `-Drelease=minor`: `semVerSelected = 1.3.0`
|
||||
* - `-Drelease=minor`, `-Dsnapshot=true`: `semVerSelected =
|
||||
* 1.3.0-SNAPSHOT`
|
||||
* - `-Drelease=patch`: `semVerSelected = 1.2.4`
|
||||
* - `-Drelease=patch`, `-Dsnapshot=true`: `semVerSelected =
|
||||
* 1.2.4-SNAPSHOT`
|
||||
* - `release` not set: `semVerSelected = 1.2.4-SNAPSHOT`
|
||||
* Use the `semVer` setting to assign project versions.
|
||||
*/
|
||||
lazy val semVerSelected = settingKey[String](
|
||||
lazy val semVerSelected = settingKey[SemVer](
|
||||
"Version selected for the current build. Depends on the release type and current version."
|
||||
)
|
||||
|
||||
|
|
@ -79,5 +65,3 @@ object SemVerKeys {
|
|||
lazy val semVerWriteVersionToFile = taskKey[Unit](
|
||||
"Write the selected SemVer to a file."
|
||||
)
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,76 +3,77 @@ package gs
|
|||
import java.nio.charset.StandardCharsets
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Paths
|
||||
import sbt._
|
||||
import sbt.*
|
||||
import sbt.Keys.*
|
||||
|
||||
object SemVerPlugin extends AutoPlugin {
|
||||
object SemVerPlugin extends AutoPlugin:
|
||||
override def trigger = allRequirements
|
||||
|
||||
val autoImport = SemVerKeys
|
||||
|
||||
import autoImport._
|
||||
import autoImport.*
|
||||
|
||||
val DefaultOutputFile: String = ".version"
|
||||
object Defaults:
|
||||
/** The version is persisted to this file when the
|
||||
* `semVerWriteVersionToFile` task is invoked.
|
||||
*/
|
||||
val VersionOutputFile: String = ".version"
|
||||
end Defaults
|
||||
|
||||
// Perform all version calculations and expose as a variable.
|
||||
lazy val semVerDefaults: Seq[Setting[_]] = {
|
||||
// The latest semantic version relevant to this project, calculated by
|
||||
// looking at the latest Git tag.
|
||||
val latestSemVer =
|
||||
Git.getLatestSemVer()
|
||||
/** Default settings used to initialize projects.
|
||||
*
|
||||
* Calculates the default semantic version based on the repository state (git
|
||||
* tags) and configured release type and build type.
|
||||
*/
|
||||
lazy val semVerDefaults: Seq[Setting[?]] = {
|
||||
val latestSemVer = Git.getLatestSemVer()
|
||||
val releaseType = SemVerProps.getReleaseType()
|
||||
val buildType = SemVerProps.getBuildType()
|
||||
val calculated = SemVerCalculator.update(
|
||||
baseVersion = latestSemVer,
|
||||
releaseType = releaseType,
|
||||
buildType = buildType
|
||||
)
|
||||
|
||||
// The selected release type (if any), determined by looking at the SBT
|
||||
// command line and checking if `-Drelease=<release-type>` was specified.
|
||||
val releaseType =
|
||||
PluginProperties.getReleaseType()
|
||||
|
||||
// See whether this is a pre-release build or not. If it is (if this value
|
||||
// is true), the pre-release suffix `-SNAPSHOT` will be appended to the
|
||||
// calculated version.
|
||||
val isSnapshot =
|
||||
PluginProperties.isSnapshot() || releaseType.isEmpty
|
||||
|
||||
// Calculate the appropriate semantic version for this build of the project.
|
||||
val selectedSemVer = releaseType match {
|
||||
case Some(ReleaseType.Major) => latestSemVer.incrementMajor()
|
||||
case Some(ReleaseType.Minor) => latestSemVer.incrementMinor()
|
||||
case Some(ReleaseType.Patch) => latestSemVer.incrementPatch()
|
||||
case None =>
|
||||
// Note: this doesn't play nicely with repositories that have
|
||||
// pre-release versions before 0.1.0.
|
||||
if (latestSemVer == SemVer.DefaultVersion)
|
||||
latestSemVer
|
||||
else
|
||||
latestSemVer.incrementPatch()
|
||||
}
|
||||
|
||||
// Expose the relevant values as setting keys.
|
||||
Seq(
|
||||
commands += SetVersion.command,
|
||||
semVer := calculated._2,
|
||||
semVerCurrent := latestSemVer.toString(),
|
||||
semVerSelected := selectedSemVer.render(isSnapshot),
|
||||
semVerMajor := selectedSemVer.major,
|
||||
semVerSnapshot := isSnapshot,
|
||||
semVerOutputFile := Some(DefaultOutputFile)
|
||||
semVerSelected := calculated._1,
|
||||
semVerSnapshot := buildType == BuildType.Snapshot,
|
||||
semVerMajor := calculated._1.major,
|
||||
semVerOutputFile := Some(Defaults.VersionOutputFile),
|
||||
version := calculated._2
|
||||
)
|
||||
}
|
||||
|
||||
// Automatically exposed globally.
|
||||
override lazy val globalSettings: Seq[Setting[_]] = semVerDefaults
|
||||
/** @inheritDocs
|
||||
*/
|
||||
override lazy val globalSettings: Seq[Setting[?]] = semVerDefaults
|
||||
|
||||
// Add the custom task.
|
||||
override lazy val buildSettings: Seq[Setting[_]] = Seq(
|
||||
semVerInfo := {
|
||||
val log = Keys.streams.value.log
|
||||
/** @inheritDocs
|
||||
*/
|
||||
override lazy val buildSettings: Seq[Setting[?]] = Seq(
|
||||
// Display information about the build.
|
||||
// Do not cache this task, so that the updated version is reported each
|
||||
// time it is called.
|
||||
semVerInfo := Def.uncached {
|
||||
val log = streams.value.log
|
||||
log.info(s"[SemVer] Current: ${semVerCurrent.value}")
|
||||
log.info(s"[SemVer] Selected: ${semVerSelected.value}")
|
||||
log.info(s"[SemVer] Snapshot: ${semVerSnapshot.value}")
|
||||
log.info(s"[SemVer] Final String: ${semVer.value}")
|
||||
log.info(s"[SemVer] Project Version: ${version.value}")
|
||||
},
|
||||
semVerWriteVersionToFile := {
|
||||
val outputFile = semVerOutputFile.value.getOrElse(DefaultOutputFile)
|
||||
// Dump the build information to file.
|
||||
// Do not cache this task, so that it can run multiple times within the same
|
||||
// session.
|
||||
semVerWriteVersionToFile := Def.uncached {
|
||||
val outputFile =
|
||||
semVerOutputFile.value.getOrElse(Defaults.VersionOutputFile)
|
||||
Files.write(
|
||||
Paths.get(outputFile),
|
||||
semVerSelected.value.getBytes(StandardCharsets.UTF_8)
|
||||
semVerSelected.value.toString().getBytes(StandardCharsets.UTF_8)
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
|
|
|
|||
20
src/main/scala/gs/SemVerProps.scala
Normal file
20
src/main/scala/gs/SemVerProps.scala
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
package gs
|
||||
|
||||
object SemVerProps:
|
||||
|
||||
val SemVerReleaseType: String = "semver.release_type"
|
||||
val SemVerBuildType: String = "semver.build_type"
|
||||
|
||||
def getReleaseType(): ReleaseType =
|
||||
sys.props
|
||||
.get(SemVerReleaseType)
|
||||
.flatMap(ReleaseType.parse)
|
||||
.getOrElse(ReleaseType.Patch)
|
||||
|
||||
def getBuildType(): BuildType =
|
||||
sys.props
|
||||
.get(SemVerBuildType)
|
||||
.flatMap(BuildType.parse)
|
||||
.getOrElse(BuildType.Snapshot)
|
||||
|
||||
end SemVerProps
|
||||
103
src/main/scala/gs/SetVersion.scala
Normal file
103
src/main/scala/gs/SetVersion.scala
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
package gs
|
||||
|
||||
import sbt.*
|
||||
import sbt.Keys.*
|
||||
|
||||
/** Custom command used to update semantic versions for the project.
|
||||
* Recalculates versions using the input [[ReleaseType]] and [[BuildType]].
|
||||
*/
|
||||
object SetVersion:
|
||||
|
||||
/** The unique name of the command. Used to invoke the command using SBT.
|
||||
*
|
||||
* For example: `sbt:project-name> setVersion patch snapshot`
|
||||
*/
|
||||
val commandName: String = "setVersion"
|
||||
|
||||
/** CLI help documentation for this command.
|
||||
*/
|
||||
val detailedHelpString: String =
|
||||
"""|setVersion [release-type] [build-type]
|
||||
|
|
||||
| Reconfigures the build version using SemVer. Based on the latest
|
||||
| version as detected from Git tags. If no Git tags exist, the default
|
||||
| version 0.1.0 is used as a baseline.
|
||||
|
|
||||
| - [release-type] one of major|minor|patch (default=patch)
|
||||
| + major: 1.2.3 becomes 2.0.0
|
||||
| + minor: 1.2.3 becomes 1.3.0
|
||||
| + patch: 1.2.3 becomes 1.2.4
|
||||
| - [build-type] one of release|snapshot (default=snapshot)
|
||||
| + release: The SemVer is emitted as-is.
|
||||
| + snapshot: The SemVer has the string "-SNAPSHOT" appended.
|
||||
|
|
||||
| Snapshot builds look like 1.2.3-SNAPSHOT.
|
||||
|""".stripMargin
|
||||
|
||||
val help: Help = Help(
|
||||
name = commandName,
|
||||
briefHelp = (commandName, "Reconfigures the build version using SemVer."),
|
||||
detail = detailedHelpString
|
||||
)
|
||||
|
||||
/** The Command implementation. Added to projects via `semVerDefaults` within
|
||||
* the [[SemVerPlugin]].
|
||||
*/
|
||||
val command: Command = Command.args(commandName, commandName, help) {
|
||||
(
|
||||
state,
|
||||
args
|
||||
) =>
|
||||
val logger = state.log
|
||||
val (calculated, rendered, rt, bt) = calculateVersion(args)
|
||||
|
||||
logger.info(
|
||||
s"Calculated new SemVer: '$rendered' [Release Type = $rt, Build Type = $bt]"
|
||||
)
|
||||
|
||||
import SemVerKeys.*
|
||||
|
||||
// "Update" the project state by appending new values to the session DAG.
|
||||
Project
|
||||
.extract(state)
|
||||
.appendWithSession(
|
||||
settings = Seq(
|
||||
ThisBuild / version := rendered,
|
||||
ThisBuild / semVer := rendered,
|
||||
ThisBuild / semVerSelected := calculated,
|
||||
ThisBuild / semVerMajor := calculated.major,
|
||||
ThisBuild / semVerSnapshot := bt == BuildType.Snapshot
|
||||
),
|
||||
state = state
|
||||
)
|
||||
}
|
||||
|
||||
private def calculateVersion(args: Seq[String])
|
||||
: (SemVer, String, ReleaseType, BuildType) =
|
||||
// These are the current values cached in state. Depending on the
|
||||
// arguments to this command, these properties may be overridden.
|
||||
val currentReleaseType = SemVerProps.getReleaseType()
|
||||
val currentBuildType = SemVerProps.getBuildType()
|
||||
|
||||
val (newReleaseType, newBuildType) = args match {
|
||||
case r :: b :: _ =>
|
||||
(
|
||||
ReleaseType.parse(r, currentReleaseType),
|
||||
BuildType.parse(b, currentBuildType)
|
||||
)
|
||||
case r :: Nil =>
|
||||
(ReleaseType.parse(r, currentReleaseType), currentBuildType)
|
||||
case Nil => (currentReleaseType, currentBuildType)
|
||||
}
|
||||
|
||||
sys.props(SemVerProps.SemVerReleaseType) = newReleaseType.name
|
||||
sys.props(SemVerProps.SemVerBuildType) = newBuildType.name
|
||||
|
||||
val calculated = SemVerCalculator.updateGit(
|
||||
releaseType = newReleaseType,
|
||||
buildType = newBuildType
|
||||
)
|
||||
|
||||
(calculated._1, calculated._2, newReleaseType, newBuildType)
|
||||
|
||||
end SetVersion
|
||||
Loading…
Add table
Reference in a new issue