Skip to content

Commit

Permalink
add info command and various small fixes
Browse files Browse the repository at this point in the history
feat: add info command to list all variables
feat: add in Next, Tag and MessageFormat as formats
fix: next command doesn't include prefix by default
fix: error if configured config file doesn't exist
docs: various doc fixes
  • Loading branch information
idc101 committed Apr 6, 2020
1 parent 2e79c27 commit 298326d
Show file tree
Hide file tree
Showing 13 changed files with 189 additions and 102 deletions.
7 changes: 4 additions & 3 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import Dependencies._

ThisBuild / scalaVersion := "2.12.11"
ThisBuild / version := "0.1.0-SNAPSHOT"
ThisBuild / organization := "net.cardnell"
ThisBuild / scalaVersion := "2.12.11"
ThisBuild / version := "0.3.0"
ThisBuild / organization := "net.cardnell"

lazy val root = (project in file("."))
.settings(
name := "git-mkver",
scalacOptions += "-Ypartial-unification",
libraryDependencies += "org.typelevel" %% "cats-core" % "2.0.0",
libraryDependencies += "com.monovore" %% "decline" % "1.0.0",
libraryDependencies += "com.github.pathikrit" %% "better-files" % "3.8.0",
Expand Down
26 changes: 22 additions & 4 deletions docs/formats.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,37 @@ All replacements in format strings start with `{` and end with `}`. They are rec



## SemVer Formats

The following built in formats conform to the SemVer spec. They cannot be overriden.

| Format Token | Substitution |
| ------------- | ------------- |
| `Version` | `{x}.{y}.{z}` |
| `VersionPreRelease` | `{Version}-{PreRelease}` |
| `VersionBuildMetaData` | `{Version}+{BuildMetaData}` |
| `VersionPreReleaseBuildMetaData` | `{Version}-{PreRelease}+{BuildMetaData}` |


## Built-in Formats

| Format Token | Substitution |
| ------------- | ------------- |
| `Next` | Full Semantic Version |
| `Tag` | Full Semantic Version as a tag (includes the prefix) |
| `TagMessage` | Tag Message |
| `x` | Version major number |
| `z` | Version patch number |
| `y` | Version minor number |
| `br` | Branch name |
| `sh` | Short Hash |
| `hash` | Full Hash |
| `dd` | Day |
| `mm` | MonthValue, |
| `yyyy` | Year, |
| `mm` | Month |
| `yyyy` | Year |
| `bn` | Build No from build system |
| `tag` | Full tag |
| `pr` | Tag prefix |
| `tag?` | `true` if this branch is allowed to be tagged; `false` otherwise |
| `pr` | Tag prefix |
| `env.XXXX` | Environment Variables |

### Environment Variables
8 changes: 8 additions & 0 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,11 @@ pre-defined.
```bash
$ git mkver patch
```

## Info

If you want to see all variables or configuration you can use the `info` command:

```bash
$ git mkver info
```
4 changes: 3 additions & 1 deletion src/main/resources/reference.conf
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ defaults {
# the git tag must be a valid SemVer so the tag format must be one of:
# Version | VersionPreRelease | VersionBuildMetaData | VersionPreReleaseBuildMetaData
tagFormat: VersionBuildMetaData
# name of the pre-release e.g. alpha, beta, rc
preReleaseName: "rc"
# list of patches to be applied when `git mkver patch` is called
patches: [
# e.g. Helm
# e.g. HelmChart
]
# list of formats
formats: [
Expand Down
121 changes: 70 additions & 51 deletions src/main/scala/net/cardnell/mkver/AppConfig.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package net.cardnell.mkver

import java.io

import zio.IO
import zio.config._
import ConfigDescriptor._
Expand All @@ -9,6 +11,7 @@ import ConfigDocs.Details._
import better.files.File
import com.typesafe.config.ConfigFactory
import zio.config.typesafe.{TypeSafeConfigSource, TypesafeConfig}
import cats.implicits._

case class Format(name: String, format: String)
object Format {
Expand Down Expand Up @@ -50,9 +53,9 @@ object BranchConfig {
nameDesc.default(".*") |@|
prefixDesc.default("v") |@|
tagDesc.default(false) |@|
tagFormatDesc.default("version") |@|
tagFormatDesc.default("VersionBuildMetaData") |@|
tagMessageFormatDesc.default("release {Version}") |@|
preReleaseNameDesc.default("rc.") |@|
preReleaseNameDesc.default("rc") |@|
formatsDesc.default(Nil) |@|
patchesDesc.default(Nil)
)(BranchConfig.apply, BranchConfig.unapply)
Expand Down Expand Up @@ -89,24 +92,25 @@ object AppConfig {
nested("patches")(list(PatchConfig.patchConfigDesc))
)(AppConfig.apply, AppConfig.unapply)

def getBranchConfig(configFile: Option[String], currentBranch: String): BranchConfig = {
val appConfig = getAppConfig(configFile)
val defaults = appConfig.defaults

val branchConfig = appConfig.branches.find { bc => currentBranch.matches(bc.name) }

branchConfig.map { bc =>
BranchConfig(
name = bc.name,
prefix = bc.prefix.getOrElse(defaults.prefix),
tag = bc.tag.getOrElse(defaults.tag),
tagFormat = bc.tagFormat.getOrElse(defaults.tagFormat),
tagMessageFormat = bc.tagMessageFormat.getOrElse(defaults.tagMessageFormat),
preReleaseName = bc.preReleaseName.getOrElse(defaults.preReleaseName),
formats = mergeFormats(bc.formats.getOrElse(Nil), defaults.formats),
patches = bc.patches.getOrElse(defaults.patches)
)
}.getOrElse(defaults)
def getBranchConfig(configFile: Option[String], currentBranch: String): Either[MkVerError, BranchConfig] = {
getAppConfig(configFile).map { appConfig =>
val defaults = appConfig.defaults

val branchConfig = appConfig.branches.find { bc => currentBranch.matches(bc.name) }

branchConfig.map { bc =>
BranchConfig(
name = bc.name,
prefix = bc.prefix.getOrElse(defaults.prefix),
tag = bc.tag.getOrElse(defaults.tag),
tagFormat = bc.tagFormat.getOrElse(defaults.tagFormat),
tagMessageFormat = bc.tagMessageFormat.getOrElse(defaults.tagMessageFormat),
preReleaseName = bc.preReleaseName.getOrElse(defaults.preReleaseName),
formats = mergeFormats(bc.formats.getOrElse(Nil), defaults.formats),
patches = bc.patches.getOrElse(defaults.patches)
)
}.getOrElse(defaults)
}
}

def mergeFormats(branch: List[Format], defaults: List[Format]): List[Format] = {
Expand All @@ -115,45 +119,60 @@ object AppConfig {
val overridesMap = overrides.map( it => (it.name, it)).toMap
overridesMap.values.foldLeft(startMap)((a, n) => a.+((n.name, n))).values.toList.sortBy(_.name)
}
// Start with defaults
val v1 = update(defaults, branch)
val v2 = update(v1, Formatter.builtInFormats)
v2
update(defaults, branch)
}

def getPatchConfigs(configFile: Option[String], branchConfig: BranchConfig): List[PatchConfig] = {
val allPatchConfigs = getAppConfig(configFile).patches.map(it => (it.name, it)).toMap
branchConfig.patches.map( c => allPatchConfigs.get(c).orElse(sys.error(s"Can't find patch config named $c")).get)
}

def getAppConfig(configFile: Option[String]): AppConfig = {
val file = if (configFile.exists(File(_).exists)) {
configFile
} else if (sys.env.get("GITMKVER_CONFIG").exists(File(_).exists)) {
sys.env.get("GITMKVER_CONFIG")
} else if (File("mkver.conf").exists) {
Some("mkver.conf")
} else {
None
def getPatchConfigs(configFile: Option[String], branchConfig: BranchConfig): Either[MkVerError, List[PatchConfig]] = {
getAppConfig(configFile).flatMap { appConfig =>
val allPatchConfigs = appConfig.patches.map(it => (it.name, it)).toMap
val x: List[Either[MkVerError, PatchConfig]] = branchConfig.patches.map { c =>
allPatchConfigs.get(c) match {
case Some(p) => Right[MkVerError, PatchConfig](p)
case None => Left[MkVerError, PatchConfig](MkVerError(s"Can't find patch config named $c"))
}
}
val y = x.sequence
y
}
}

val hocon = file.map { f =>
TypeSafeConfigSource.fromTypesafeConfig(ConfigFactory.parseFile(new java.io.File(f)))
// TODO Use this when in ZIO land
// TypeSafeConfigSource.fromHoconFile(new java.io.File("mkver.conf"))
def getAppConfig(configFile: Option[String]): Either[MkVerError, AppConfig] = {
val file = configFile.map { cf =>
if (File(cf).exists) {
Right(Some(cf)
)
} else {
Left(MkVerError(s"--config $cf does not exist"))
}
}.orElse {
sys.env.get("GITMKVER_CONFIG").map { cf =>
if (File(cf).exists) {
Right(Some(cf))
} else {
Left(MkVerError(s"GITMKVER_CONFIG $cf does not exist"))
}
}
}.getOrElse {
TypeSafeConfigSource.fromTypesafeConfig(ConfigFactory.load("reference.conf"))
if (File("mkver.conf").exists) {
Right(Some("mkver.conf"))
} else {
Right(None)
}
}

val config =
hocon match {
case Left(value) => sys.error("Unable to load config: " + value)
case Right(source) => read(AppConfig.appConfigDesc from source)
file.flatMap { of =>
of.map { f =>
TypeSafeConfigSource.fromTypesafeConfig(ConfigFactory.parseFile(new java.io.File(f)))
// TODO Use this when in ZIO land
// TypeSafeConfigSource.fromHoconFile(new java.io.File("mkver.conf"))
}.getOrElse {
TypeSafeConfigSource.fromTypesafeConfig(ConfigFactory.load("reference.conf"))
}.fold(l => Left(MkVerError(l)), r => Right(r))
}.flatMap { source: ConfigSource[String, String] =>
read(AppConfig.appConfigDesc from source) match {
case Left(value) => Left(MkVerError("Unable to parse config: " + value))
case Right(result) => Right(result)
}

config match {
case Left(value) => sys.error("Unable to parse config: " + value)
case Right(result) => result
}
}
}
14 changes: 11 additions & 3 deletions src/main/scala/net/cardnell/mkver/CommandLineArgs.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,19 @@ import com.monovore.decline.{Command, Opts}
import cats.implicits._

object CommandLineArgs {
case class NextOpts(format: Option[String])
case class NextOpts(format: Option[String], prefix: Boolean)
case class TagOpts(format: Option[String])
case class PatchOpts(format: Option[String])
case class InfoOpts(includeBranchConfig: Boolean)

val configFile: Opts[Option[String]] = Opts.option[String]("config", short = "c", metavar = "file", help = "Config file to load").orNone
val format: Opts[Option[String]] = Opts.option[String]("format", short = "f", metavar = "string", help = "Format string for the version number").orNone
val nextOptions: Opts[NextOpts] = format.map(NextOpts.apply)
val prefix: Opts[Boolean] = Opts.flag("prefix", short = "p", help = "Include the tag prefix in the output").orFalse
val includeBranchConfig: Opts[Boolean] = Opts.flag("include-branch-config", short = "i", help = "Format string for the version number").orTrue
val nextOptions: Opts[NextOpts] = (format, prefix).mapN(NextOpts.apply)
val tagOptions: Opts[TagOpts] = format.map(TagOpts.apply)
val patchOptions: Opts[PatchOpts] = format.map(PatchOpts.apply)
val infoOptions: Opts[InfoOpts] = includeBranchConfig.map(InfoOpts.apply)

val nextCommand: Command[NextOpts] = Command("next", header = "Print the next version tag that would be used") {
nextOptions
Expand All @@ -26,9 +30,13 @@ object CommandLineArgs {
patchOptions
}

val infoCommand: Command[InfoOpts] = Command("info", header = "output all formats and branch configuration") {
infoOptions
}

case class CommandLineOpts(configFile: Option[String], p: Product)

val commands: Opts[Product] = Opts.subcommands(nextCommand, tagCommand, patchCommand)
val commands: Opts[Product] = Opts.subcommands(nextCommand, tagCommand, patchCommand, infoCommand)

val commandLineOpts: Opts[CommandLineOpts] = (configFile, commands).mapN(CommandLineOpts.apply)

Expand Down
16 changes: 9 additions & 7 deletions src/main/scala/net/cardnell/mkver/Formatter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,7 @@ object Formatter {
Format("VersionPreRelease", "{Version}-{PreRelease}"),
Format("VersionBuildMetaData", "{Version}+{BuildMetaData}"),
Format("VersionPreReleaseBuildMetaData", "{Version}-{PreRelease}+{BuildMetaData}"),
)

val defaultFormats = List(
Format("PreRelease", "rc-{PreReleaseName}"),
Format("BuildMetaData", "{br}.{sh}"),
Format("PreRelease", "{PreReleaseName}{PreReleaseNumber}"),
)

case class Formatter(formats: List[Format]) {
Expand All @@ -35,6 +31,11 @@ object Formatter {

def apply(version: VersionData, branchConfig: BranchConfig): Formatter = {
Formatter(List(
Format("Next", "{" + branchConfig.tagFormat + "}"),
Format("Tag", "{pr}{" + branchConfig.tagFormat + "}"),
Format("TagMessage", "{" + branchConfig.tagMessageFormat + "}"),
Format("PreReleaseName", branchConfig.preReleaseName),
Format("PreReleaseNumber", ""),
Format("x", version.major.toString),
Format("y", version.minor.toString),
Format("z", version.patch.toString),
Expand All @@ -47,7 +48,7 @@ object Formatter {
Format("bn", version.buildNo),
Format("tag?", branchConfig.tag.toString),
Format("pr", branchConfig.prefix.toString)
) ++ branchConfig.formats
) ++ AppConfig.mergeFormats(branchConfig.formats, builtInFormats)
++ envVariables()
++ azureDevOpsVariables())
}
Expand All @@ -72,6 +73,7 @@ object Formatter {
branchName
.replace("refs/heads/", "")
.replace("refs/", "")
.replace("/", "_")
.replace("/", "-")
.replace("_", "-")
}
}
42 changes: 31 additions & 11 deletions src/main/scala/net/cardnell/mkver/Main.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package net.cardnell.mkver

import better.files._
import net.cardnell.mkver.MkVer._
import net.cardnell.mkver.CommandLineArgs.{CommandLineOpts, NextOpts, PatchOpts, TagOpts}
import net.cardnell.mkver.CommandLineArgs.{CommandLineOpts, InfoOpts, NextOpts, PatchOpts, TagOpts}

case class ProcessResult(stdout: String, stderr: String, exitCode: Int)

Expand All @@ -29,15 +29,19 @@ class Main(git: Git.Service = Git.Live.git()) {
def run(opts: CommandLineOpts): Either[MkVerError, String] = {
git.checkGitRepo().flatMap { _ =>
val currentBranch = git.currentBranch()
val config = AppConfig.getBranchConfig(opts.configFile, currentBranch)
opts.p match {
case nextOps@NextOpts(_) =>
runNext(nextOps, config, currentBranch)
case TagOpts(_) =>
runTag(config, currentBranch).map(_ => "")
case PatchOpts(_) =>
val patchConfigs = AppConfig.getPatchConfigs(opts.configFile, config)
runPatch(config, currentBranch, patchConfigs).map(_ => "")
AppConfig.getBranchConfig(opts.configFile, currentBranch).flatMap { config =>
opts.p match {
case nextOps@NextOpts(_, _) =>
runNext(nextOps, config, currentBranch)
case TagOpts(_) =>
runTag(config, currentBranch).map(_ => "")
case PatchOpts(_) =>
AppConfig.getPatchConfigs(opts.configFile, config).flatMap { patchConfigs =>
runPatch(config, currentBranch, patchConfigs).map(_ => "")
}
case InfoOpts(includeBranchConfig) =>
runInfo(config, currentBranch, includeBranchConfig)
}
}
}
}
Expand All @@ -47,7 +51,7 @@ class Main(git: Git.Service = Git.Live.git()) {
nextOpts.format.map { format =>
Right(Formatter(nextVersion, config).format(format))
}.getOrElse {
formatTag(config, nextVersion)
formatTag(config, nextVersion, nextOpts.prefix)
}
}
}
Expand Down Expand Up @@ -78,4 +82,20 @@ class Main(git: Git.Service = Git.Live.git()) {
}
}
}

def runInfo(config: BranchConfig, currentBranch: String, includeBranchConfig: Boolean): Either[MkVerError, String] = {
getNextVersion(git, config, currentBranch).map { nextVersion =>
val formatter = Formatter(nextVersion, config)
val formats = formatter.formats.map { format =>
val result = formatter.format(format.format)
s"${format.name}=$result"
}.mkString(System.lineSeparator())

if (includeBranchConfig) {
config.toString + System.lineSeparator() + System.lineSeparator() + formats
} else {
formats
}
}
}
}
Loading

0 comments on commit 298326d

Please sign in to comment.