SBT Semantic Versioning Release

This project is inspired by the Semantic build-versioning for Gradle plugin.

1. Introduction

This SBT plugin is an extension to sbt-release plugin.

1.1. Differences with sbt-release

Git

This plugin depends on Git.

version.sbt

This plugin doesn’t require the version.sbt, since it relies on Git tags to find the latest version. Setting version in your build.sbt would be enough. Following release steps are not required in this plugin:

  • commitReleaseVersion

  • setNextVersion

  • commitNextVersion

Therefore, the default release process is:

releaseProcess := Seq[ReleaseStep](
      checkSnapshotDependencies,
      inquireVersions,
      runClean,
      runTest,
      setReleaseVersion,
      tagRelease,
      publishArtifacts,
      pushChanges
    )

2. Usage

resolvers += "Sonatype OSS" at "https://s01.oss.sonatype.org/content/groups/public/"

addSbtPlugin("io.github.sfali23" % "sbt-semver-release"  % "0.5.0")

3. Plugin Settings

Following are plugin settings:

3.1. startingVersion

This option defines the starting version of the build in case there is no tag available to determine next version. Default value is 0.1.0-SNAPSHOT. If not defined it will be deduced from project version.

3.2. tagPrefix

This option defines prefix to use when tagging a release. Default value is v.

Note

If you are planning to override this value then do not forget to override releaseTagName property as follows, release plugin uses releaseTagName property to set tag name.

releaseTagName := s"${tagPrefix.value}${runtimeVersion.value}"

3.3. componentToBump

This property bumps the version. It can be set to the values MAJOR, MINOR, or PATCH. This property is required if you are using force bump. This property has no effect when using auto-bump. This property can be set via system property sbt.release.componentToBump.

Assuming that the base version is x.y.z, and the value of componentToBump is:

MAJOR

The new version will be (x + 1).0.0; if the base version is a pre-release version, the pre-release version-component is discarded, and the new version will still be (x + 1).0.0.

MINOR

The new version will be x.(y + 1).0; if the base version is a pre-release version, the pre-release version-component is discarded, and the new version will still be x.(y + 1).0.

PATCH

The new version will be x.y.(z + 1); if the base version is a pre-release version, the pre-release version-component is discarded, and the new version will still be x.y.(z + 1).

3.4. defaultBumpLevel

This property defines default bump level for auto-bump strategy. Default value is PATCH.

3.5. forceBump (Manual bump)

This property defines the flag to enable forceBump. Default value is false. This option can be set via system property sbt.release.forceBump. In order to force bump to work, you have to set componentToBump property.

If you use auto-bumping (see Automatic bumping based on commit messages) and manual bumping together, then force bump will take precedence.

3.6. newPreRelease

This property defines the flag to enable new-pre-release. Default value is false. This option can be set either of following ways:

Via system property

New pre-release can be created via system property by passing sbt.release.newPreRelease property:

  sbt -Dsbt.release.newPreRelease=true "release with-defaults"
Via SBT settings

Set newPreRelease property in SBT settings:

  newPreRelease := true
Via commit message

New pre-release can be created by adding specific pattern in your commit message, default value is [new-pre-release]. See here on how to customize this value.

This property creates a new pre-release version by bumping the requested version-component and then adding the starting pre-release version from the pre-release configuration (see pre-release). It has the following behavior:

  • When used by itself it will bump the patch version and then append the starting pre-release version as specified in the pre-release configuration. Assuming that the base version is x.y.z, the new version will be x.y.(z + 1)-<startingVersion> (see startingVersion), for example, 1.2.2 will become 1.2.3-RC.1.

  • When used with componentToBump=patch or [patch] commit message, the behavior is the same as using newPreRelease by itself.

  • When used with componentToBump=minor or [minor] commit message, it will bump the minor version and then append the starting pre-release version as specified in the pre-release configuration. Assuming that the base version is x.y.z, the new version will be x.(y + 1).0-<startingVersion> (see startingVersion), for example, 1.2.2 will become 1.3.0-RC.1.

  • When used with componentToBump=major or [major] commit message, it will bump the major version and then append the starting pre-release version as specified in the pre-release configuration. Assuming that the base version is x.y.z, the new version will be (x + 1).0.0-<startingVersion> (see startingVersion), for example, 1.2.2 will become 2.0.0-RC.1.

3.6.1. Bump pre-release version

Once new pre-release version is created any subsequent bump will only bump pre-release version, any attempt to bump wither of major, minor, or patch version will be ignored, either by forceBump or autoBump, for example, if the pre-release version is 1.2.3-RC.1, then next version will be 1.2.3-RC.2.

3.7. promoteToRelease

This property defines the flag to enable promote-to-release. Default value is false. This option can be set via system property sbt.release.promoteToRelease as well as via [promote] in the commit message.

This property promotes a pre-release version to a release version. This is done by discarding the pre-release version-component. For example, assuming that the base version is x.y.z-some.identifiers.here, the new version will be x.y.z. This property can only be used if the base version is a pre-release version.

3.8. Pre-releases

This is how you can define your pre-release versioning-strategy. This is a special case because other than defining a basic syntax and ordering rules, the semantic-versioning specification has no other rules about pre-release identifiers. This means that some extra configuration is required if you want to generate pre-release versions.

import sbtsemverrelease.PreReleaseConfig

preRelease := PreReleaseConfig(prefix = "RC", separator = ".", startingVersion = 1)

3.8.1. prefix and separator

These options define prefix and separator of pre-release version.

3.8.2. startingVersion

This option is required and describes the starting pre-release version of a new pre-release. This value will be used if newPreRelease is invoked (either explicitly or via Automatic bumping based on commit messages). The default value is 1, this value has to greater or equal to 1.

3.9. Automatic bumping based on commit messages

Sometimes you might want to automatically bump your version as part of your continuous-integration process. Without this option, you would have to explicitly configure your CI process to use the corresponding componentToBump property value, depending on the version component you want to bump. This is because the default behavior of the plugin is to bump the component with the least precedence. Instead, you can configure the plugin to automatically bump the desired version-component based on the contents of all your commit messages since the nearest ancestor-tags; this essentially means messages from all unreleased ancestor-commits. If multiple commit-messages apply, then the component with the highest precedence wins. This way you can note in each commit message whether the change is major or minor directly, and this plugin uses that information to calculate the next version-number to be used.

3.9.1. autoBump

This option allows you to specify how the build version should be automatically bumped based on the contents of commit messages. The full message of each applicable commit-message is checked to see if a match for any of specified pattern can be found. Note that in the case of multiple matches, the component with the highest precedence wins. This option has the following sub-options:

majorPattern

If any relevant commit message contains a match for majorPattern, the major version will be bumped. This has to be a regular expression, and its default value is \[major\], which means [major] anywhere in the commit message.

minorPattern

If any relevant commit message contains a match for minorPattern, the minor version will be bumped. This has to be a regular expression, and its default value is \[minor\], which means [minor] anywhere in the commit message.

patchPattern

If any relevant commit message contains a match for patchPattern, the patch version will be bumped. This has to be a regular expression, and its default value is \[patch\], which means [patch] anywhere in the commit message.

newPreReleasePattern

If any relevant commit message contains a match for newPreReleasePattern, then a new pre-release version will be created. If no major or minor-version bumping is specified via autobumping or manually, the new pre-release version will be created after bumping the patch version. Otherwise, the new pre-release version is created after bumping the appropriate component. The same restrictions and rules that apply to the newPreRelease property apply here as well. This has to be a regular expression, and its default value is \[new-pre-release\], which means [new-pre-release] anywhere in the message.

promoteToReleasePattern

If any relevant commit message contains a match for promoteToReleasePattern, the version will be promoted to a release version. The same rules that apply to the promoteToRelease property apply here as well. This has to be a regular expression, and its default value is \[promote\], which means [promote] anywhere in any line.

Example 1. Defining custom patterns to be used by autoBump
import sbtsemverrelease.AutoBump

autoBump := AutoBump(
  // match "[bump-major]" on its own line without leading or trailing characters
  majorPattern = Some("(?m)^\\[bump-major\\]$".r),

   // match "[bump-minor]" on its own line without leading or trailing characters
  minorPattern = Some("(?m)^\\[bump-minor\\]$".r),

  // match "[bump-patch]" on its own line without leading or trailing characters
  patchPattern = Some("?m)^\\[bump-patch\\]$".r),

  // match "[make-new-pre-release]" on its own line without leading or trailing characters
  newPreReleasePattern = Some("(?m)^\\[make-new-pre-release\\]$".r),

  // match "[promote-to-release]" on its own line without leading or trailing characters
  promoteToReleasePattern = Some("(?m)^\\[promote-to-release\\]$".r)
)
Note

If none of the commit messages match the patterns in autoBump, the plugin assumes its default behavior and will use defaultBumpLevel property.

3.10. extraReleaseBranches

By default, this plugin will only allow to release from either from main or master branches. This option provides name of the branches you wish to release from, for example, development. If the branch is not main, master, or one of the branch in `extraReleaseBranches`then snapshot version will be created.

3.11. snapshot config

This option defines how snapshot versions will be tagged, format of the snapshot version will, x.y.z-SNAPSHOT+<commit_has>. There three parts in this property:

3.11.1. Snapshot prefix

Default value is SNAPSHOT.

3.11.2. appendCommitHash

This option specifies whether to include commit hash as part of snapshot version. Default value is true. If false no commit has will be appended and snapshot version will be x.y.z-SNAPSHOT.

3.11.3. useShortHash

This option specifies whether to use short commit hash. Default value is true. If this value is set to false then full commit hash will be used.

Note

Release plugin has strict regular expression for version, when using commit hash in the snapshot then configure releaseNextVersion property as follows:

releaseNextVersion := { _ => "" }

3.12. snapshot

This option defines the flag to make current release a snapshot release. This option is calculated as follows:

  1. The option is explicitly set in build.sbt using snapshot property.

  2. The option is set by sbt.release.snapshot via system property.

  3. The option is set via hasUncommittedChanges function of Git. If the function returns true then snapshot flag will be set to true, false otherwise.

  4. If the current branch is one of main, `master, or specified in extraReleaseBranches.

3.13. hotfixBranchPattern — Checking out a tag

It is useful to check out a tag when you want to create a build of an older version. Once you check out specific tag create a branch by using following pattern: <tag-prefix>major.minor.patch+, for example: if the tag was v1.2.3 then branch name should be v1.2.3+. Any subsequent build will only bump hot fix version, so next version will be, v1.2.3.1 and so on. It is not possible to bump any other part of version once you have a tag checked out.

3.14. unReleasedCommits

Collects un-released commits to be added into releaseTagComment if addUnReleasedCommitsToTagComment settings is set to true.

3.15. addUnReleasedCommitsToTagComment

Default comment of the release tag is Release <version_to_be_released>, this setting allows to add un-released commits to tag summary. With setting on tag comment will be:

Release <version_to_be_released>


Commit(<short_hash>, <commit_message>)