Skip to content

Defining Rules

Till Krullmann edited this page Mar 3, 2018 · 1 revision

Defining Rules

Rules are defined using the rules DSL inside the gitVersion extension. It can be used either as a property, or in "block" mode using a closure:

gitVersion.rules.onBranch( ... ) { ... }

gitVersion {
  rules {
    onBranch (...) { ... }
  }
}

Types of Rules

Different types of rules match under certain conditions. When determining the version, all rules are taken into consideration in the order of their appearance (with the exception of before and after rules, see below). For each rule whose condition matches, the body will be evaluated.

The onBranch Rule

This is the most important type of rule, because you will usually want to follow different scenarios based on the branch that is currently checked out. For example, when building from the master or a feature branch, you may want a suffix (called a "prerelease tag" in the SemVer spec) appended to the version, whereas for a release

The first argument to onBranch can either be a string or a regular expression. If the argument is a string, the rule body will be evaluated if the current branch name exactly matches the argument. The string is not interpreted as a regular expression.

gitVersion.rules {

  onBranch ('master') {
    println 'on master branch!'
  }

}

If the argument is a Pattern, then the rule body will be evaluated if the current branch name matches the pattern. Inside the rule body, you may use the matcher variable to access any captured groups.

Remember that Groovy allows you to construct a Pattern conveniently using the pattern operator ~ and "slashy" string literals.

gitVersion.rules {

  onBranch (~'feature/(.*)') {
    println 'on feature branch!'
  }

  onBranch (~/release\/(\d+)\.(\d+)/) {
    println 'on release branch!'
  }

}

The always, before, and after Rules

As their name implies, these rules always match, regardless of the current branch name or any other repository state.

gitVersion.rules {

  before { ... }

  always { ... }

  after { ... }

}

The before and after rules are always evaluated before (or after) any other rules, regardless of where they are defined. Multiple before rules (or multiple after rules) are evaluated in the order of their appearance.

The onDetachedHead Rule

As a special case, the repository might be in a state that is called a "detached head" in Git: in this state, there is no branch currently checked out; the HEAD points to a specific commit instead of a branch. You can cover this case using the onDetachedHead rule:

gitVersion.rules {

  onDetachedHead { ... }

}

Writing the Body of a Rule

Inside a rule body, you presumably want to define your version, rather than printing messages to the console. For this goal, there are a number of properties and methods available inside the rule body.

In addition to manipulating the version directly, there are some powerful high-level Git operations available which are useful in versioning tasks (see "Git Operations" below).

version

The version property allows you to manipulate the version, which is ultimately the result of the versioning process. The version may be manipulated in a number of different ways:

// Parsing the version directly from a SemVer-compliant string
version = '1.2.3-beta'

// Setting multiple parts of the version at once
version.set(1, 2, 3, 'beta')

// Setting parts of the version individually
version.major = 1
version.minor = 2
version.patch = 3
version.prereleaseTag = 'beta'

Suppressing the Evaluation of Other Rules

By default, all matching rules are evaluated in the order of definition (with special treatment of before and after rules). This may not always be desired. Within a rule, you can skip the evaluation of all following rules by setting skipOtherRules to true:

gitVersion.rules {

    onBranch('master') {
        // ...
        skipOtherRules = true
    }

    // This rule will *not* be evaluated for the master branch
    always {
        // ...
    }
}

Git Operations

Inside a rule body, the following properties are available to access the state of the Git repository:

Property Type Description

head

GitCommit

The current HEAD of the repository. May be used to access the commit ID, commit message, attached tags or even walk through the history.

currentBranch

GitBranch

Representation of the current Git branch. Will be null if in detached-head mode. May be used to access the short name (e.g. "master") or the full name (e.g. "refs/heads/master") of the branch.

branchName

String

The name of the current branch.

repository

GitRepository

Allows access to the Git repository as a whole, e.g. to list tags and branches.

High-Level Operations

The following methods are available inside a rule body, and allow for high-level operations on a Git repository which are useful for versioning tasks:

findLatestTag

Finds the latest Git tag with the given name or matching the given regular expression. This is useful if tags are your primary versioning mechanism.

For example, if you tag commits on the master branch with versions like "v1.2", then the following rule will find the latest such tag:

gitVersion.rules {

    onBranch('master') {
        def tag = findLatestTag ~/v?(\d+)\.(\d+)/
        if (tag) {
            version.major = tag.matches[1].toInteger()
            version.minor = tag.matches[2].toInteger()
        }
    }
}

By default, this does not consider tagged commits from branches that were merged in. Put differently, the method findLatestTag walks backward from the HEAD until it finds a matching tag. On a merge commit (i.e. a commit with more than one parent), only the first parent is followed.

You can change this behavior using the second parameter includeMerges to the method, which is false by default:

def tag = findLatestTag(~/v?(\d+)\.(\d+)/, true)

In this case all merged branches are followed. The sorting is topographical first, then by commit date. Topographical sorting means that when walking backwards in the history, all commits are looked at before all of their ancestors.

countCommitsSince

This method counts the number of commits that were made after the given commit or tag. It can be used with any object that has a Git ID (commit hash). If used with the current HEAD, it will return 0.

For example, to use it on the result of the findLatestTag method from above:

gitVersion.rules {

    onBranch('master') {
        def tag = findLatestTag ~/v?(\d+)\.(\d+)/
        if (tag) {
            version.major = tag.matches[1].toInteger()
            version.minor = tag.matches[2].toInteger()
            version.patch = countCommitsSince tag
        }
    }
}

In this example, the tags will only contain the major/minor version numbers, and we use the number of commits since the tag as the patch version part. If we build directly on a commit tagged "v1.2", the resulting version will be "1.2.0". If 3 more commits have been made since the tag, the resulting version will be "1.2.3".

branchPoint

The method branchPoint finds the commit where the current branch was created off another branch. This can be very useful in combination with countCommitsSince, if you follow a GitFlow-like approach with release branches:

gitVersion.rules {

    // Release branch names contain the major/minor version, e.g. release/1.0
    onBranch(~/release\/(\d+\.\d+)/) {

        // Take the major and minor version from the branch name
        version.setFrom matches[1]

        // The patch version should be the number of commits since
        // this release branch was branched off
        version.patch = countCommitsSince branchPoint('origin/develop')

    }
}