diff --git a/.gitignore b/.gitignore
index 96ef862..136d405 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
target/
.idea/
+_site/
diff --git a/README.md b/README.md
index bb4ee28..a2781bb 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,32 @@
# git-mkver
-Git-mkver uses git tags, branch names and commit messages to determine
-the next version of the software to release.
+Helps version your software and patch version numbers into the build.
+For more information head to the [project site][https://git-mkver.github.com].
+
+## Features
+
+- Determine next version based on:
+ - Last tagged commit
+ - [Conventional Commits][https://www.conventionalcommits.org/]
+ - Branch names
+ - Manual tagging
+- Next version conforms to [Semantic Versioning][https://semver.org/] scheme
+- Patch the next version into the build:
+ - Java
+ - C#
+ - Many others, fully configurable
+- Tag the current commit with the next version
+
+All of this can be configured based on the branch name so release/master branches get different
+version numbers to develop or feature branches.
## Installation
Download the binary for your os from the releases page and copy to
somewhere on your path.
+
## Usage
Basic usage is to just call `git mkver next` and it will tell you the next
diff --git a/build.sbt b/build.sbt
index 9622714..8c18532 100644
--- a/build.sbt
+++ b/build.sbt
@@ -6,10 +6,16 @@ ThisBuild / organization := "net.cardnell"
lazy val root = (project in file("."))
.settings(
- name := "git-mkver",
- //libraryDependencies += "com.github.xuwei-k" %% "optparse-applicative" % "0.8.2",
- libraryDependencies += "org.typelevel" %% "cats-core" % "2.0.0",
- libraryDependencies += "com.monovore" %% "decline" % "1.0.0",
- libraryDependencies += "com.github.pathikrit" %% "better-files" % "3.8.0",
- libraryDependencies += scalaTest % Test
+ name := "git-mkver",
+ libraryDependencies += "org.typelevel" %% "cats-core" % "2.0.0",
+ libraryDependencies += "com.monovore" %% "decline" % "1.0.0",
+ libraryDependencies += "com.github.pathikrit" %% "better-files" % "3.8.0",
+ libraryDependencies += "dev.zio" %% "zio-config" % "1.0.0-RC14",
+ libraryDependencies += "dev.zio" %% "zio-config-typesafe" % "1.0.0-RC14",
+ libraryDependencies += scalaTest % Test,
+
+ assemblyMergeStrategy in assembly := {
+ case PathList("META-INF", xs @ _*) => MergeStrategy.discard
+ case x => MergeStrategy.first
+ }
)
diff --git a/docs/Gemfile b/docs/Gemfile
new file mode 100644
index 0000000..37f5eaa
--- /dev/null
+++ b/docs/Gemfile
@@ -0,0 +1,2 @@
+source 'https://rubygems.org'
+gem 'github-pages', group: :jekyll_plugins
diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock
new file mode 100644
index 0000000..81918a8
--- /dev/null
+++ b/docs/Gemfile.lock
@@ -0,0 +1,248 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ activesupport (6.0.2.2)
+ concurrent-ruby (~> 1.0, >= 1.0.2)
+ i18n (>= 0.7, < 2)
+ minitest (~> 5.1)
+ tzinfo (~> 1.1)
+ zeitwerk (~> 2.2)
+ addressable (2.7.0)
+ public_suffix (>= 2.0.2, < 5.0)
+ coffee-script (2.4.1)
+ coffee-script-source
+ execjs
+ coffee-script-source (1.11.1)
+ colorator (1.1.0)
+ commonmarker (0.17.13)
+ ruby-enum (~> 0.5)
+ concurrent-ruby (1.1.6)
+ dnsruby (1.61.3)
+ addressable (~> 2.5)
+ em-websocket (0.5.1)
+ eventmachine (>= 0.12.9)
+ http_parser.rb (~> 0.6.0)
+ ethon (0.12.0)
+ ffi (>= 1.3.0)
+ eventmachine (1.2.7)
+ execjs (2.7.0)
+ faraday (1.0.1)
+ multipart-post (>= 1.2, < 3)
+ ffi (1.12.2)
+ forwardable-extended (2.6.0)
+ gemoji (3.0.1)
+ github-pages (204)
+ github-pages-health-check (= 1.16.1)
+ jekyll (= 3.8.5)
+ jekyll-avatar (= 0.7.0)
+ jekyll-coffeescript (= 1.1.1)
+ jekyll-commonmark-ghpages (= 0.1.6)
+ jekyll-default-layout (= 0.1.4)
+ jekyll-feed (= 0.13.0)
+ jekyll-gist (= 1.5.0)
+ jekyll-github-metadata (= 2.13.0)
+ jekyll-mentions (= 1.5.1)
+ jekyll-optional-front-matter (= 0.3.2)
+ jekyll-paginate (= 1.1.0)
+ jekyll-readme-index (= 0.3.0)
+ jekyll-redirect-from (= 0.15.0)
+ jekyll-relative-links (= 0.6.1)
+ jekyll-remote-theme (= 0.4.1)
+ jekyll-sass-converter (= 1.5.2)
+ jekyll-seo-tag (= 2.6.1)
+ jekyll-sitemap (= 1.4.0)
+ jekyll-swiss (= 1.0.0)
+ jekyll-theme-architect (= 0.1.1)
+ jekyll-theme-cayman (= 0.1.1)
+ jekyll-theme-dinky (= 0.1.1)
+ jekyll-theme-hacker (= 0.1.1)
+ jekyll-theme-leap-day (= 0.1.1)
+ jekyll-theme-merlot (= 0.1.1)
+ jekyll-theme-midnight (= 0.1.1)
+ jekyll-theme-minimal (= 0.1.1)
+ jekyll-theme-modernist (= 0.1.1)
+ jekyll-theme-primer (= 0.5.4)
+ jekyll-theme-slate (= 0.1.1)
+ jekyll-theme-tactile (= 0.1.1)
+ jekyll-theme-time-machine (= 0.1.1)
+ jekyll-titles-from-headings (= 0.5.3)
+ jemoji (= 0.11.1)
+ kramdown (= 1.17.0)
+ liquid (= 4.0.3)
+ mercenary (~> 0.3)
+ minima (= 2.5.1)
+ nokogiri (>= 1.10.4, < 2.0)
+ rouge (= 3.13.0)
+ terminal-table (~> 1.4)
+ github-pages-health-check (1.16.1)
+ addressable (~> 2.3)
+ dnsruby (~> 1.60)
+ octokit (~> 4.0)
+ public_suffix (~> 3.0)
+ typhoeus (~> 1.3)
+ html-pipeline (2.12.3)
+ activesupport (>= 2)
+ nokogiri (>= 1.4)
+ http_parser.rb (0.6.0)
+ i18n (0.9.5)
+ concurrent-ruby (~> 1.0)
+ jekyll (3.8.5)
+ addressable (~> 2.4)
+ colorator (~> 1.0)
+ em-websocket (~> 0.5)
+ i18n (~> 0.7)
+ jekyll-sass-converter (~> 1.0)
+ jekyll-watch (~> 2.0)
+ kramdown (~> 1.14)
+ liquid (~> 4.0)
+ mercenary (~> 0.3.3)
+ pathutil (~> 0.9)
+ rouge (>= 1.7, < 4)
+ safe_yaml (~> 1.0)
+ jekyll-avatar (0.7.0)
+ jekyll (>= 3.0, < 5.0)
+ jekyll-coffeescript (1.1.1)
+ coffee-script (~> 2.2)
+ coffee-script-source (~> 1.11.1)
+ jekyll-commonmark (1.3.1)
+ commonmarker (~> 0.14)
+ jekyll (>= 3.7, < 5.0)
+ jekyll-commonmark-ghpages (0.1.6)
+ commonmarker (~> 0.17.6)
+ jekyll-commonmark (~> 1.2)
+ rouge (>= 2.0, < 4.0)
+ jekyll-default-layout (0.1.4)
+ jekyll (~> 3.0)
+ jekyll-feed (0.13.0)
+ jekyll (>= 3.7, < 5.0)
+ jekyll-gist (1.5.0)
+ octokit (~> 4.2)
+ jekyll-github-metadata (2.13.0)
+ jekyll (>= 3.4, < 5.0)
+ octokit (~> 4.0, != 4.4.0)
+ jekyll-mentions (1.5.1)
+ html-pipeline (~> 2.3)
+ jekyll (>= 3.7, < 5.0)
+ jekyll-optional-front-matter (0.3.2)
+ jekyll (>= 3.0, < 5.0)
+ jekyll-paginate (1.1.0)
+ jekyll-readme-index (0.3.0)
+ jekyll (>= 3.0, < 5.0)
+ jekyll-redirect-from (0.15.0)
+ jekyll (>= 3.3, < 5.0)
+ jekyll-relative-links (0.6.1)
+ jekyll (>= 3.3, < 5.0)
+ jekyll-remote-theme (0.4.1)
+ addressable (~> 2.0)
+ jekyll (>= 3.5, < 5.0)
+ rubyzip (>= 1.3.0)
+ jekyll-sass-converter (1.5.2)
+ sass (~> 3.4)
+ jekyll-seo-tag (2.6.1)
+ jekyll (>= 3.3, < 5.0)
+ jekyll-sitemap (1.4.0)
+ jekyll (>= 3.7, < 5.0)
+ jekyll-swiss (1.0.0)
+ jekyll-theme-architect (0.1.1)
+ jekyll (~> 3.5)
+ jekyll-seo-tag (~> 2.0)
+ jekyll-theme-cayman (0.1.1)
+ jekyll (~> 3.5)
+ jekyll-seo-tag (~> 2.0)
+ jekyll-theme-dinky (0.1.1)
+ jekyll (~> 3.5)
+ jekyll-seo-tag (~> 2.0)
+ jekyll-theme-hacker (0.1.1)
+ jekyll (~> 3.5)
+ jekyll-seo-tag (~> 2.0)
+ jekyll-theme-leap-day (0.1.1)
+ jekyll (~> 3.5)
+ jekyll-seo-tag (~> 2.0)
+ jekyll-theme-merlot (0.1.1)
+ jekyll (~> 3.5)
+ jekyll-seo-tag (~> 2.0)
+ jekyll-theme-midnight (0.1.1)
+ jekyll (~> 3.5)
+ jekyll-seo-tag (~> 2.0)
+ jekyll-theme-minimal (0.1.1)
+ jekyll (~> 3.5)
+ jekyll-seo-tag (~> 2.0)
+ jekyll-theme-modernist (0.1.1)
+ jekyll (~> 3.5)
+ jekyll-seo-tag (~> 2.0)
+ jekyll-theme-primer (0.5.4)
+ jekyll (> 3.5, < 5.0)
+ jekyll-github-metadata (~> 2.9)
+ jekyll-seo-tag (~> 2.0)
+ jekyll-theme-slate (0.1.1)
+ jekyll (~> 3.5)
+ jekyll-seo-tag (~> 2.0)
+ jekyll-theme-tactile (0.1.1)
+ jekyll (~> 3.5)
+ jekyll-seo-tag (~> 2.0)
+ jekyll-theme-time-machine (0.1.1)
+ jekyll (~> 3.5)
+ jekyll-seo-tag (~> 2.0)
+ jekyll-titles-from-headings (0.5.3)
+ jekyll (>= 3.3, < 5.0)
+ jekyll-watch (2.2.1)
+ listen (~> 3.0)
+ jemoji (0.11.1)
+ gemoji (~> 3.0)
+ html-pipeline (~> 2.2)
+ jekyll (>= 3.0, < 5.0)
+ kramdown (1.17.0)
+ liquid (4.0.3)
+ listen (3.2.1)
+ rb-fsevent (~> 0.10, >= 0.10.3)
+ rb-inotify (~> 0.9, >= 0.9.10)
+ mercenary (0.3.6)
+ mini_portile2 (2.4.0)
+ minima (2.5.1)
+ jekyll (>= 3.5, < 5.0)
+ jekyll-feed (~> 0.9)
+ jekyll-seo-tag (~> 2.1)
+ minitest (5.14.0)
+ multipart-post (2.1.1)
+ nokogiri (1.10.9)
+ mini_portile2 (~> 2.4.0)
+ octokit (4.18.0)
+ faraday (>= 0.9)
+ sawyer (~> 0.8.0, >= 0.5.3)
+ pathutil (0.16.2)
+ forwardable-extended (~> 2.6)
+ public_suffix (3.1.1)
+ rb-fsevent (0.10.3)
+ rb-inotify (0.10.1)
+ ffi (~> 1.0)
+ rouge (3.13.0)
+ ruby-enum (0.8.0)
+ i18n
+ rubyzip (2.3.0)
+ safe_yaml (1.0.5)
+ sass (3.7.4)
+ sass-listen (~> 4.0.0)
+ sass-listen (4.0.0)
+ rb-fsevent (~> 0.9, >= 0.9.4)
+ rb-inotify (~> 0.9, >= 0.9.7)
+ sawyer (0.8.2)
+ addressable (>= 2.3.5)
+ faraday (> 0.8, < 2.0)
+ terminal-table (1.8.0)
+ unicode-display_width (~> 1.1, >= 1.1.1)
+ thread_safe (0.3.6)
+ typhoeus (1.3.1)
+ ethon (>= 0.9.0)
+ tzinfo (1.2.6)
+ thread_safe (~> 0.1)
+ unicode-display_width (1.7.0)
+ zeitwerk (2.3.0)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ github-pages
+
+BUNDLED WITH
+ 2.1.4
diff --git a/docs/_config.yml b/docs/_config.yml
new file mode 100644
index 0000000..c741881
--- /dev/null
+++ b/docs/_config.yml
@@ -0,0 +1 @@
+theme: jekyll-theme-slate
\ No newline at end of file
diff --git a/docs/_includes/navbar.html b/docs/_includes/navbar.html
new file mode 100644
index 0000000..eb9b9b7
--- /dev/null
+++ b/docs/_includes/navbar.html
@@ -0,0 +1,9 @@
+
diff --git a/docs/_layouts/default.html b/docs/_layouts/default.html
new file mode 100644
index 0000000..d6752ac
--- /dev/null
+++ b/docs/_layouts/default.html
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+ {% seo %}
+
+
+
+
+
+
+
+
+{% include navbar.html %}
+
+
+
+
+
+
+
+
+
+{% if site.google_analytics %}
+
+{% endif %}
+
+
\ No newline at end of file
diff --git a/docs/config_reference.md b/docs/config_reference.md
new file mode 100644
index 0000000..b9ef52d
--- /dev/null
+++ b/docs/config_reference.md
@@ -0,0 +1,48 @@
+# Configuration
+
+git-mkver comes with a default configuration. It can be overriden by creating a `mkver.conf` file. git-mkver will search
+for this file in the current working directory.
+
+The application uses the HOCON format. More details on the specification can be found
+[here][https://github.com/lightbend/config/blob/master/HOCON.md].
+
+## mkver.conf
+
+```hocon
+# d
+defaults {
+ prefix: v
+ tagMessageFormat: "release %ver - buildno: %bn"
+ tagParts: VersionBuildMetadata
+ #minimumVersionIncrement: Major|Minor|Patch|PreRelease|None
+ patches: [
+ helm-chart
+ csproj
+ ]
+}
+branches: [
+ {
+ name: "master"
+ tag: true
+ tagParts: Version
+ }
+ {
+ name: ".*"
+ tag: false
+ }
+]
+patches: [
+ {
+ name: helm-chart
+ filePatterns: ["**/Chart.yaml"]
+ find: "version: .*"
+ replace: "version: \"%ver\""
+ }
+ {
+ name: csproj
+ filePatterns: ["**/*.csproj"]
+ find: ".*"
+ replace: "%ver"
+ }
+]
+```
\ No newline at end of file
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 0000000..75d3ab4
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,20 @@
+Helps version your software and patch version numbers into the build.
+
+## Features
+
+- Determine next version based on:
+ - Last tagged commit
+ - [Conventional Commits](https://www.conventionalcommits.org/)
+ - Branch names
+ - Manual tagging
+- Next version conforms to [Semantic Versioning](https://semver.org/) scheme
+- Patch the next version into the build:
+ - Java
+ - C#
+ - Many others, fully configurable
+- Tag the current commit with the next version
+
+All of this can be configured based on the branch name so release/master branches get different
+version numbers to develop or feature branches.
+
+
diff --git a/docs/usage.md b/docs/usage.md
new file mode 100644
index 0000000..391844c
--- /dev/null
+++ b/docs/usage.md
@@ -0,0 +1,49 @@
+## Usage
+
+Basic usage is to just call `git mkver next` and it will tell you the next
+version of the software if you publish now.
+
+```
+$ git mkver next
+0.4.0
+```
+
+### Tagging
+
+If you would like to publish a version mkver can tag the current commit.
+
+```
+$ git mkver tag
+```
+
+This will apply an annotated tag from the `next` command to the current commit.
+
+### Patching versions in files
+
+If you would like to patch version numbers in files prior to building and tagging then
+you can use the `patch` command. The files to be patched and the replacements are
+defined in the `mkver.yaml` config file. A large number of standard patches come
+pre-defined.
+
+```
+$ git mkver patch
+```
+
+### Usage Patterns
+
+
+Developers commit to master or work on feature branches:
+
+- Any commit containing `feat:` will bump the minor version
+- Any commit containing `fix:` will bump the patch version
+
+The build script run by the build server would look something like:
+
+```
+nextVer=$(git mkver next)
+git tag -a -m "New Version" "v$nextVer"
+# Publish artifacts
+```
+
+To control the frequency of releases, include these steps only on manually
+triggered builds.
diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf
new file mode 100644
index 0000000..9e52c2d
--- /dev/null
+++ b/src/main/resources/application.conf
@@ -0,0 +1,35 @@
+defaults {
+ prefix: v
+ tagMessageFormat: "release %ver - buildno: %bn"
+ tagParts: VersionBuildMetadata
+ #minimumVersionIncrement: Major|Minor|Patch|PreRelease|None
+ patches: [
+ helm-chart
+ csproj
+ ]
+}
+branches: [
+ {
+ name: "master"
+ tag: true
+ tagParts: Version
+ }
+ {
+ name: ".*"
+ tag: false
+ }
+]
+patches: [
+ {
+ name: helm-chart
+ filePatterns: ["**/Chart.yaml"]
+ find: "version: .*"
+ replace: "version: \"%ver\""
+ }
+ {
+ name: csproj
+ filePatterns: ["**/*.csproj"]
+ find: ".*"
+ replace: "%ver"
+ }
+]
diff --git a/src/main/resources/mkver.yaml b/src/main/resources/mkver.yaml
deleted file mode 100644
index aad2525..0000000
--- a/src/main/resources/mkver.yaml
+++ /dev/null
@@ -1,23 +0,0 @@
-defaults:
- prefix: v
- minimumVersionIncrement: Major|Minor|Patch|PreRelease
- patches:
- - helm-chart
- - csproj
-
-branches:
- - name: "master"
- tag: true
- tagParts: Version
- tagMessageFormat: "release %v - buildno: %bn"
- preReleaseFormat: ""
- buildMetadataFormat: ""
- - name: ".*"
- tag: false
- patches:
- - helm-chart
- - csproj
-
-patches:
- - name: helm-chart
- files: "**/Chart.yaml"
diff --git a/src/main/scala/net/cardnell/mkver/AppConfig.scala b/src/main/scala/net/cardnell/mkver/AppConfig.scala
new file mode 100644
index 0000000..3a4d53e
--- /dev/null
+++ b/src/main/scala/net/cardnell/mkver/AppConfig.scala
@@ -0,0 +1,150 @@
+package net.cardnell.mkver
+
+import zio.IO
+import zio.config._
+import ConfigDescriptor._
+import zio.config.PropertyTree._
+import zio.config.ConfigDocs._
+import ConfigDocs.Details._
+import better.files.File
+import com.typesafe.config.ConfigFactory
+import zio.config.typesafe.{TypeSafeConfigSource, TypesafeConfig}
+
+
+case class BranchConfig(name: String,
+ prefix: String,
+ tag: Boolean,
+ tagParts: TagParts,
+ tagMessageFormat: String,
+ preReleaseName: String,
+ buildMetadataFormat: String,
+ patches: List[String])
+
+case class BranchConfigOpt(name: String,
+ prefix: Option[String],
+ tag: Option[Boolean],
+ tagParts: Option[TagParts],
+ tagMessageFormat: Option[String],
+ preReleaseName: Option[String],
+ buildMetadataFormat: Option[String],
+ patches: Option[List[String]])
+
+object BranchConfig {
+ val nameDesc = string("name").describe("regex to match branch name on")
+ val prefixDesc = string("prefix").describe("prefix for git tags")
+ val tagDesc = boolean("tag").describe("whether to actually tag this branch when `mkver tag` is called")
+ val tagPartsDesc = string("tagParts")(TagParts.apply, TagParts.unapply).describe("")
+ val tagMessageFormatDesc = string("tagMessageFormat").describe("")
+ val preReleaseNameDesc = string("preReleaseName").describe("")
+ val buildMetadataFormatDesc = string("buildMetadataFormat").describe("format string to produce build metadata part of a semantic version")
+ val patchesDesc = list(string("patches")).describe("Patch configs to be applied")
+
+ val branchConfigDesc = (
+ nameDesc.default(".*") |@|
+ prefixDesc.default("v") |@|
+ tagDesc.default(false) |@|
+ tagPartsDesc.default(TagParts.VersionBuildMetadata) |@|
+ tagMessageFormatDesc.default("release %ver") |@|
+ preReleaseNameDesc.default("rc.") |@|
+ buildMetadataFormatDesc.default("%br.%sh") |@|
+ patchesDesc.default(Nil)
+ )(BranchConfig.apply, BranchConfig.unapply)
+
+ val branchConfigOptDesc = (
+ nameDesc |@|
+ prefixDesc.optional |@|
+ tagDesc.optional |@|
+ tagPartsDesc.optional |@|
+ tagMessageFormatDesc.optional |@|
+ preReleaseNameDesc.optional |@|
+ buildMetadataFormatDesc.optional |@|
+ patchesDesc.optional
+ )(BranchConfigOpt.apply, BranchConfigOpt.unapply)
+}
+
+sealed trait TagParts
+object TagParts {
+ case object Version extends TagParts
+ //case object VersionPreRelease extends TagParts
+ case object VersionBuildMetadata extends TagParts
+ //case object VersionPreReleaseBuildMetadata extends TagParts
+
+ def apply(tagParts: String): TagParts = {
+ tagParts match {
+ case "Version" => Version
+ //case "VersionPreRelease" => VersionPreRelease
+ case "VersionBuildMetadata" => VersionBuildMetadata
+ //case "VersionPreReleaseBuildMetadata" => VersionPreReleaseBuildMetadata
+ }
+ }
+
+ def unapply(arg: TagParts): Option[String] = Some(arg.toString)
+}
+
+case class PatchConfig(name: String, filePatterns: List[String], find: String, replace: String)
+
+object PatchConfig {
+ val patchConfigDesc = (
+ string("name").describe("Name of patch, referenced from branch configs") |@|
+ list(string("filePatterns").describe("Files to apply find and replace in. Supports ** and * glob patterns.")) |@|
+ string("find").describe("Regex to find in file") |@|
+ string("replace").describe("Replacement string. Can include version format strings (see help)")
+ )(PatchConfig.apply, PatchConfig.unapply)
+}
+
+case class AppConfig(defaults: BranchConfig, branches: List[BranchConfigOpt], patches: List[PatchConfig], formats: List[String])
+
+object AppConfig {
+ val appConfigDesc = (
+ nested("defaults")(BranchConfig.branchConfigDesc) |@|
+ nested("branches")(list(BranchConfig.branchConfigOptDesc)) |@|
+ nested("patches")(list(PatchConfig.patchConfigDesc)) |@|
+ list(string("formats")).default(Nil)
+ )(AppConfig.apply, AppConfig.unapply)
+
+ def getBranchConfig(currentBranch: String): BranchConfig = {
+ val appConfig = getAppConfig()
+ 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),
+ tagParts = bc.tagParts.getOrElse(defaults.tagParts),
+ tagMessageFormat = bc.tagMessageFormat.getOrElse(defaults.tagMessageFormat),
+ preReleaseName = bc.preReleaseName.getOrElse(defaults.preReleaseName),
+ buildMetadataFormat = bc.buildMetadataFormat.getOrElse(defaults.buildMetadataFormat),
+ patches = bc.patches.getOrElse(defaults.patches)
+ )
+ }.getOrElse(defaults)
+ }
+
+ def getPatchConfigs(branchConfig: BranchConfig): List[PatchConfig] = {
+ val allPatchConfigs = getAppConfig().patches.map(it => (it.name, it)).toMap
+ branchConfig.patches.map(allPatchConfigs.get(_).orElse(sys.error("Can't find patch config")).get)
+ }
+
+ def getAppConfig(): AppConfig = {
+ val hocon = if (File("mkver.conf").exists) {
+ TypeSafeConfigSource.fromTypesafeConfig(ConfigFactory.parseFile(new java.io.File("mkver.conf")))
+ // TODO Use this when in ZIO land
+ // TypeSafeConfigSource.fromHoconFile(new java.io.File("mkver.conf"))
+ } else {
+ TypeSafeConfigSource.fromTypesafeConfig(ConfigFactory.load("application.conf"))
+ }
+
+ val config =
+ hocon match {
+ case Left(value) => sys.error("Unable to load config: " + value)
+ case Right(source) => read(AppConfig.appConfigDesc from source)
+ }
+
+ config match {
+ case Left(value) => sys.error("Unable to parse config: " + value)
+ case Right(result) => result
+ }
+ }
+}
diff --git a/src/main/scala/net/cardnell/mkver/Config.scala b/src/main/scala/net/cardnell/mkver/Config.scala
deleted file mode 100644
index 299de3e..0000000
--- a/src/main/scala/net/cardnell/mkver/Config.scala
+++ /dev/null
@@ -1,26 +0,0 @@
-package net.cardnell.mkver
-
-import scala.util.matching.Regex
-
-case class BranchConfig(name: Regex,
- prefix: String,
- tag: Boolean,
- tagParts: TagParts,
- tagMessageFormat: String,
- preReleaseName: String,
- buildMetadataFormat: String,
- patches: List[PatchConfig])
-
-sealed trait TagParts
-object TagParts {
- case object Version extends TagParts
- case object VersionPreRelease extends TagParts
- case object VersionBuildMetadata extends TagParts
- case object VersionPreReleaseBuildMetadata extends TagParts
-}
-
-case class PatchConfig(val name: String, val filePatterns: List[String], val findRegex: String, val replace: String)
-
-object Config {
-
-}
diff --git a/src/main/scala/net/cardnell/mkver/Main.scala b/src/main/scala/net/cardnell/mkver/Main.scala
index 0e1b13e..f64851d 100644
--- a/src/main/scala/net/cardnell/mkver/Main.scala
+++ b/src/main/scala/net/cardnell/mkver/Main.scala
@@ -10,18 +10,13 @@ case class ProcessResult(stdout: String, stderr: String, exitCode: Int)
object Main {
- val patchConfigs = List(
- PatchConfig("helm-chart", List("**/Chart.yaml"), "version: .*", "version: \"%ver\""),
- PatchConfig("csproj", List("**/*.csproj"), ".*", "%ver")
- )
-
def main(args: Array[String]): Unit = {
CommandLineArgs.mkverCommand.parse(args, sys.env) match {
case Left(help) =>
System.err.println(help)
sys.exit(1)
- case Right(NextOpts(_)) =>
- runNext()
+ case Right(nextOps@NextOpts(_)) =>
+ runNext(nextOps)
case Right(TagOpts(_)) =>
runTag()
case Right(PatchOpts(_)) =>
@@ -34,18 +29,21 @@ object Main {
sys.exit(0)
}
- def runNext(): Unit = {
+ def runNext(nextOpts: NextOpts): Unit = {
checkGitRepo()
- val currentBranch = exec("git rev-parse --abbrev-ref HEAD").stdout
- val config = getConfig(currentBranch)
+ val currentBranch = getCurrentBranch()
+ val config = AppConfig.getBranchConfig(currentBranch)
val nextVersionData = getNextVersion(config, currentBranch)
- println(formatTag(config, nextVersionData))
+ val output = nextOpts.format.map { format =>
+ VariableReplacer(nextVersionData).replace(format)
+ }.getOrElse(formatTag(config, nextVersionData))
+ println(output)
}
def runTag(): Unit = {
checkGitRepo()
- val currentBranch = exec("git rev-parse --abbrev-ref HEAD").stdout
- val config = getConfig(currentBranch)
+ val currentBranch = getCurrentBranch()
+ val config = AppConfig.getBranchConfig(currentBranch)
val nextVersion = getNextVersion(config, currentBranch)
val tag = formatTag(config, nextVersion)
val tagMessage = VariableReplacer(nextVersion).replace(config.tagMessageFormat)
@@ -54,21 +52,18 @@ object Main {
}
}
- def runPatch() = {
- val currentBranch = exec("git rev-parse --abbrev-ref HEAD").stdout
- val config = getConfig(currentBranch)
+ def runPatch(): Unit = {
+ val currentBranch = getCurrentBranch()
+ val config = AppConfig.getBranchConfig(currentBranch)
val nextVersion = getNextVersion(config, currentBranch)
- patchConfigs.foreach { patch =>
- val regex = patch.findRegex.r
+ AppConfig.getPatchConfigs(config).foreach { patch =>
+ val regex = patch.find.r
val replacement = VariableReplacer(nextVersion).replace(patch.replace)
- println(replacement)
patch.filePatterns.foreach { filePattern =>
File.currentWorkingDirectory.glob(filePattern, includePath = false).foreach { file =>
- println(s"checking $file")
- val newLines = file.lines().map { line =>
- regex.replaceAllIn(line, replacement)
- }
- file.overwrite(newLines.mkString(System.lineSeparator()))
+ println(s"patching: $file, replacement: $replacement")
+ val newContent = regex.replaceAllIn(file.contentAsString, replacement)
+ file.overwrite(newContent)
}
}
}
@@ -81,12 +76,4 @@ object Main {
System.exit(output.exitCode)
}
}
-
- def getConfig(currentBranch: String): BranchConfig = {
- if (currentBranch == "master") {
- BranchConfig("master".r, "v", true, TagParts.Version, "release %v", "rc", "%sh", patchConfigs)
- } else {
- BranchConfig(".*".r, "v", false, TagParts.VersionBuildMetadata, "release %v", "rc", "%br.%sh", patchConfigs)
- }
- }
}
diff --git a/src/main/scala/net/cardnell/mkver/MkVer.scala b/src/main/scala/net/cardnell/mkver/MkVer.scala
index 264cf81..5d9ddfe 100644
--- a/src/main/scala/net/cardnell/mkver/MkVer.scala
+++ b/src/main/scala/net/cardnell/mkver/MkVer.scala
@@ -63,6 +63,10 @@ object MkVer {
lastVersionTag match {
case version(major, minor, patch, prerelease, buildmetadata) => Version(major.toInt, minor.toInt, patch.toInt, Option(prerelease), Option(buildmetadata))
+ case _ =>
+ System.err.println(s"warning: unable to parse last tag. ($lastVersionTag) doesn't match a SemVer pattern")
+ System.exit(1)
+ Version(0, 0, 0, None, None)
}
}
@@ -72,9 +76,9 @@ object MkVer {
val buildMetaData = VariableReplacer(versionData).replace(config.buildMetadataFormat)
config.tagParts match {
case TagParts.Version => version
- case TagParts.VersionPreRelease => s"$version-$preRelease"
+ //case TagParts.VersionPreRelease => s"$version-$preRelease"
case TagParts.VersionBuildMetadata =>s"$version+$buildMetaData"
- case TagParts.VersionPreReleaseBuildMetadata =>s"$version-$preRelease+$buildMetaData"
+ //case TagParts.VersionPreReleaseBuildMetadata =>s"$version-$preRelease+$buildMetaData"
}
}
@@ -135,6 +139,16 @@ object MkVer {
}
}
+ def getCurrentBranch(): String = {
+ if (sys.env.contains("BUILD_SOURCEBRANCH")) {
+ // Azure Devops Pipeline
+ sys.env("BUILD_SOURCEBRANCH").replace("refs/heads/", "")
+ } else {
+ // TODO better fallback if we in detached head mode like build systems do
+ exec("git rev-parse --abbrev-ref HEAD").stdout
+ }
+ }
+
def exec(command: String): ProcessResult = {
exec(command.split(" "))
}
diff --git a/src/test/scala/net/cardnell/mkver/ConfigSpec.scala b/src/test/scala/net/cardnell/mkver/ConfigSpec.scala
new file mode 100644
index 0000000..d3b444c
--- /dev/null
+++ b/src/test/scala/net/cardnell/mkver/ConfigSpec.scala
@@ -0,0 +1,28 @@
+package net.cardnell.mkver
+
+import com.typesafe.config.ConfigFactory
+import org.scalatest.flatspec.AnyFlatSpec
+import org.scalatest.matchers.should.Matchers
+import zio.config.read
+import zio.config.typesafe.TypeSafeConfigSource
+import zio.config.typesafe.TypeSafeConfigSource.fromHoconString
+import zio.{App, Has, ZEnv, ZIO, ZLayer, console}
+
+class ConfigSpec extends AnyFlatSpec with Matchers {
+
+ "config" should "load" in {
+ //val c = TypeSafeConfigSource.fromDefaultLoader
+ val c = TypeSafeConfigSource.fromTypesafeConfig(ConfigFactory.load("application.conf"))
+ println(c)
+ val config =
+ c match {
+ case Left(value) => Left(value)
+ case Right(source) => read(AppConfig.appConfigDesc from source)
+ }
+
+ println(config)
+
+ assert(config != null)
+ }
+
+}