GitFlowSemVerPlugin
An opinionated Gradle Plugin for Implementing SemVer 2.0 on GitFlow based projects
Install / Use
/learn @OpenLimark/GitFlowSemVerPluginREADME
GitFlow SemVer Gradle Plugin
This is an opinionated Gradle plugin that enables Semantic Versioning (2.0) for projects that follow GitFlow branching model. With this plugin, you can do semantic versioning for your Gradle projects with minimum configuration, as it uses an opinionated versioning model. This plugin uses Git Tags to track the version numbers, so it does not require maintaining separate files.
Versioning Model
Based on the GitFlow model, versioning is done as follows.
Master Branch
masterbranch contains the production releases. Each production release should be tagged (annotated tags) in Git using the patternmaj.min.patch(ex. 1.0.0).masterbranch should not have any direct commits other than tagged releases.- The version of
masterbranch will be derived from the most recent tag. For example, if the last tag is1.0.1, when a Gradle build gets executed on the master branch,project.versionproperty will be set to1.0.1.
Release Branches
- Version of the release branches will be determined based on the branch name. For example, if the name of the release branch is
release/1.1.0, then the base version for that release branch will be1.1.0. This will be suffixed with a pre-release identifier namedrcwhich stands for 'release candidate'. - The pre-release suffix will include the text
rcfollowed by a number, which indicates the number of commits that happened in that release branch since its creation (base lined out of develop branch) + 1.- That is, when the release branch is created, it will have the pre-release suffix as
rc.1. The number is 1 because number of commits since branching is zero (0 + 1). - When the first commit is done in the release branch, the suffix will be
rc.2(number of commits = 1, so its 1 + 1 = 2).
- That is, when the release branch is created, it will have the pre-release suffix as
- So the effective version after the above examples would be
1.1.0-rc.2.
Hotfix Branches
- Hotfix branches follow the same model as the release branches. The suffix will be
rcsimilar to release branches, since any build made out of the hotfix branch will also be a release candidate. - As a convention, the branch names in hotfix branch should increment the
patchsegment of the version number. - Hotfix branches will be based lined out of the
masterbranch to calculate thercnumber. - As an example, if the name of the hotfix branch is
hotfix/1.1.1and we have made 2 commits after branching out, the version number would be1.1.1-rc.3.
Develop Branch
- Integration builds will be taken out of the develop branch. So the builds of this branch will have the pre-release suffix of
alpha. - The base version for this branch will be determined as follows.
- Last released version will be identified by scanning for the most recent git tag. For example, if our last release was
1.2.5, then there will be a Git tag in our repo with that number. - The
minorsegment of the last released version will be incremented by 1. Thepatchsegment of the last released version will be reset to 0. - This gives us the base version of the development branch as
1.3.0.
- Last released version will be identified by scanning for the most recent git tag. For example, if our last release was
- The base version will then be suffixed with the
alphapre-release tag, along with a number that is the number of commits on development branch since the last release tag was made + 1.- If there are no commits since the last tag, then the suffix would be
alpha.1. - If there are two commits since the last tag, it would be
alpha.3.
- If there are no commits since the last tag, then the suffix would be
- Following the above example of last release version
1.2.5, if we have 4 commits made on the development branch since then, our effective version would be1.3.0-alpha.5.
Feature / Bug Fix / Etc
In most cases, these branches will not be deployed as is except for local testing. However calculating the unique version of such branches is not straight forward. As a work-around to this, this plugin relies on a calculated unique branch identifier that is SemVer safe. The versioning strategy in this case would be,
- The base version will be calculated as same as the
developbranch. - The pre-release version tag will be
feat,bugfixorunknownin all other cases. - This will be suffixed with a unique branch identifier that will be calculated based on the branch name (ex.
l3jpwte7ehbenbh8lteyrn8). - After the branch id, an integer will indicate the number of commits made to the branch since develop branch.
- Ex.
0.2.0-feat-l3jpwte7ehbenbh8lteyrn8.1
How To Use This Plugin
Pre-Requisites: You need to have at least one annotated tag indicating the last released version / base line version of the project. This will be used to calculate the potential next version number. In case of a fresh project, just create a tag with the value
0.0.0. For example:git tag -a 0.0.0 -m "Base Version"
Quick Start
- Refer to the plugin in the
pluginssection of the Gradle build script as follows.
plugins {
id 'com.limark.gitflowsemver' version '0.1.0'
}
- That's it! When you run the next build, it will determine the next version automatically based on the versioning model outlined above and the plugin will update the
project.versionproperty in the Gradle script.- You can refer to this property from anywhere within your Gradle script to take any action.
- If you would like to print the version number, run the
printVersiontask as follows.- Ex.
./gradlew printVersion
- Ex.
- The plugin also adds a task for writing a property file in the
buildDirof the project with the version number underversionproperty name. This is useful in cases where you want to read the version as part of the CI pipeline. For example, we use this in Jenkins pipelines.- Ex.
./gradlew writeVersion
- Ex.
Customizing The Plugin
The behavior of the plugin can be customized by adding a configuration block to your Gradle build script as follows.
versionConfig {
alphaLabel = "pre"
rcLabel = "beta"
gitDescribeMatchRule = "*[0-9].[0-9]*.[0-9]*"
propertiesFile = "${project.buildDir}/META-INF/version.properties"
}
Above configuration does the following changes,
- Instead of using the
alphapre-release identifier, plugin will usepre. For example,1.1.0-pre.1. - Instead of the
rcidentifier for release versions / hotfix versions, plugin will usebeta. - The
gitDescribeMatchRuleallows you to customize how a release tag will be identified. - Using the
propertiesFile, you can change the location where theversion.propertiesfile will be created. By default, it gets written to${project.buildDir}/version.properties. In the above example, it is changed to be inside theMETA-INFdirectory.
Integrations / Compatibility
GitFlow AVH
This plugin is fully compatible and tested with GitFlow AVH Plugin for the standard GitFlow branches.
Currently, this plugin does not recognise support branches supported by the AVH Plugin. Support branches are not a branch type from the original GitFlow spec. This plugin may be extended to recognize support branches in a future release, if there's a demand for that.
Jenkins
Please note that since v3.4.0 of Jenkins Git Plugin, the default SCM checkout of Jenkins will not fetch the tags (see JENKINS-45164 for more details). However, this plugin relies on tags being present so that we can track the last released version. To address this issue, you need to configure your Jenkins jobs to fetch the tags when SCM checkout happens. Below steps are for configuring a Jenkins multi-branch declarative pipelines project. Similar steps can be taken for other project types as well.
- Go to your project configuration page in Jenkins.
- Under 'Branch Sources', you should see a section called 'Behaviors'. Under this section, click 'Add' button and select 'Advance Clone Behaviors'.
- If 'Fetch Tags' is not selected, select it. As of current version, when you add 'Advance Clone Behaviors', this automatically gets checked.
Once above configuration change is done, Jenkins will fetch the tags during SCM checkout.
