In this page
How will I use Better Commit Policy?
How to implement GitOps, SubversionOps or MercurialOps secured by commit policies?
Managing commit policies
Creating commit policies
Cloning commit policies
Updating commit policies
Disabling commit policies
Deleting commit policies
Permissions
Linking commit policies to Jira projects
Permissions for managing and viewing commit policies
Permissions for installing hook scripts
How linked projects affect the rules and conditions
Fixing links that point to deleted Jira projects
Anatomy of commit policies
Commit policy configuration options
(Re-)verifying already existing commits
Verifying merge commits
Rejection messages
Did you mean?
Rules
Rule types
Tag rules
Branch rules
Commit rules
Scope limiting
Limiting to branches
Limiting to paths
Conditions
Tag conditions
"Tag name must match a pattern" condition
"Tag name must contain issue keys from a JQL query" condition
Branch conditions
"Branch name must match a pattern" condition
"Branch name must contain issue keys from a JQL query" condition
Commit conditions
"Commit message must match a pattern" condition
"Commit message must contain issue keys from a JQL query" condition
"Changed paths (files) must match a pattern" condition
"Changed paths (files) must contain issue keys from a JQL query" condition
"Committer must have a valid Jira account" condition
"Committer attribute must match a pattern" condition
Custom conditions (written in Groovy)
Hook scripts
How hook scripts work
Hook script security
Hook script requirements
Generating hook scripts
Anatomy of a hook script
Configuring hook scripts
Authentication modes
Token-based authentication
Password-based authentication
Diagnostic mode
Hook script versions
Version checks
Upgrading hook scripts (and other type of clients)
Upgrading hook scripts
Upgrading Better Commit Policy for Bitbucket
Hook script compatibility matrix
Global settings
Parser patterns
Issue key pattern for commit messages
Using lowercase or mixed case issue keys
Using numbers in project keys
Issue key pattern for branches, tags and paths
Subversion branch pattern
Subversion tag pattern
Bypass patterns
How bypass patterns work
How to use bypass patterns?
Security
Token secret
Token expiration period
User interface
"Commits" menu visibility
Pattern syntax
Regular expressions
Glob
Differences between Centralized- and Distributed Version Control Systems
Verifying policies
Centralized
Distributed
Fixing commits
Centralized
Distributed
Next step
What is the Better Commit Policy for Jira app?
Better Commit Policy verifies the changes committed to your Version Control System against a set of configurable rules. If the changes satisfy the policy expressed by the rules, the commit is accepted, otherwise it is rejected and the user is asked to fix the change and re-commit that.
It guarantees traceability from source code to user stories, tasks or other issue types, and results in high quality code, change history and controlled processes.
For a high level overview of the app value proposition and core functionality, please see the app home page. This page is the starting page of the user documentation.
How will I use Better Commit Policy?
The app supports every kind of commit verification strategies for the widely used Version Control System software.
For example:
- Enforce useful commit messages: Each commit message must be at least 20 characters long and must follow the best practice known as the 50/72 rule.
- Link commits to Jira issues: Each commit message must start with the key of an issue that is assigned to the committer and is "In progress".
- Use fixed branch types: Each branch name must start with "feature/", "bugfix/", "hotfix/" or "release/".
- Link feature branches to Jira issues: Each branch name which starts with "feature/" must include the key of an issue that has the "Story" type, is unresolved and is in the current Scrum sprint.
- Use semantic version tags: Each tag must formatted like "1.2.3" or "0.7.11-beta".
- Enforce source code file structures: JPEG files and only those must be check in to the "/images" directory.
- Lock files: Reject changes on the "/doc/legal" directory.
These are just a couple of frequent examples, the app allows implementing your own policy by configuring rules and conditions.
How to implement GitOps, SubversionOps or MercurialOps secured by commit policies?
The app is also an important component to implement automatic development workflows or a complete GitOps practice.
GitOps (and SubversionOps, MercurialOps inspired by this) is a set of practices that make the Version Control System the single source of truth and the central control mechanism in a software project. One of the key GitOps practices is to use automation wherever possible.
If you are using the Better Commit Policy app, you can easily implement VCS-driven automation with its companion app called Better DevOps Automation.
With this combination:
- All code, infrastructure and configuration changes will be first verified by the Better Commit Policy app.
- Only if the changes were correct, the Better DevOps Automation app can trigger a CI/CD build, send a Slack or email notification, or execute a script that automatically deploys the changes from the repository to production.
Managing commit policies
A commit policy is essentially a named set of rules that will be verified against every commit you are trying to send into the central Version Control System repository.
To add, modify or delete policies, login to Jira as administrator → open the Commits menu in Jira's header → click Commit Policies.
In pre-2.0.0 app versions the navigation path is a little different: login to Jira as administrator → go to Add-ons → Commit Policies (in the left-bar).
Creating commit policies
Click the Add policy button under the list to create a new policy.
Each policy has a name, an optional description where you can typically explain the purpose of the policy, optional linked projects, some options and a list of rules and conditions to verify.
Cloning commit policies
(since app version 4.3.0)You can also create a new commit policy based on an existing one, instead of defining that from scratch. It is super-useful when you have multiple projects that want to enforce a similar set of commit rules and conditions.
Most typically, to create a policy for project "BAR" by reusing an existing policy created for "FOO", just clone it by clicking the corresponding icon in the Actions column, replace the project key "FOO" with "BAR" in the clone, and save it.
Note that the newly created clone and the original policy are not related in any way. They are two separate entities.
Updating commit policies
Commit policies are identified by unique IDs (integers starting from 1). Therefore you can rename or update a policy any time, without breaking the links between the policy and the hook scripts using it. You can also freely modify the conditions in the policy any time.
All these changes will be immediately visible for the hook scripts. There is no need to re-generate and re-install the hook scripts.
Disabling commit policies
Commit policies can be disabled and then re-enabled with a single click on the corresponding icons in the Actions column. When a policy is disabled, the hook scripts that verify it will accept all commits without checking any conditions.
Policies should be disabled primarily when you want to (temporarily) remove the constraints expressed by the policy, but you plan to re-activate those later. You should also consider temporarily disabling policies when facing technical problems in the verification process. For example, if a defect in Jira or in the app would totally stop your team's work, you can just disable the policy, let your team continue their work, fix the problem and re-enable the policy.
Deleting commit policies
You must be careful with deleting policies though. We suggest that you first check if there's any hook script that relies on a policy, and only delete if there isn't any.
If you accidentally delete a policy that is still in use by some hook script, then those hook scripts will reject all changes until you fix problem. Fixing simply means that you need to change the policy_id variable in the hook script's configuration to make it use an existing policy.
Permissions
In pre-4.2.0 app versions, only Jira administrators are allowed to manage (add, update or delete) commit policies.
Version 4.2.0 introduced an extended security model which supports optionally linking commit policies to Jira projects. Those commit policies that are linked to Jira projects can also be managed by the administrators of the corresponding projects.
The following sections explain the permissions in more details.
Linking commit policies to Jira projects
(since app version 4.2.0)Commit policies can be linked to zero, one or multiple Jira projects:
- If zero: the policy is a "global commit policy", not inheriting permissions from any project. (In pre-4.2.0 app versions, every commit policy is global.)
- If one: the policy inherits the permissions from the linked project.
- If multiple: the policy inherits the permissions combining those from the linked projects.
What are the rules of linking projects?
-
When creating commit policies:
- Jira administrators (and only they) can create global commit policies.
- Jira administrators can create commit policies linked to any project.
- Project administrators can create commit policies linked only to the projects that they administer.
-
When updating existing commit policies:
- Jira administrators can update the linked projects of any commit policy without restrictions.
- Jira administrators can convert a global commit policy to a project-linked one (by adding linked projects to it) and a project-linked policy to a global one (by removing all linked projects from it).
- Project administrators can update the linked projects of a commit policy only if they can administer all its currently linked projects. Plus, they can link the policy only to the projects(s) that they administer.
- Project administrators cannot convert commit policies from global to project-linked and vice versa.
Permissions for managing and viewing commit policies
The table below summarizes who can do what depending on the linked projects. Note that although Jira administrators can do anything with any policy, it is not written to each cell, in order to keep the table simple.
Commit policy type | Who can manage it? (1) | Who can view it? (2) | Use it if... |
---|---|---|---|
Global | Jira administrators | Anyone (3) |
|
Linked to one Jira project |
Project administrators (in the linked project) (4) |
Project members (in the linked project) (5) |
|
Linked to multiple Jira projects |
Project administrators (in all of the linked projects) |
Project members (in any of the linked projects) |
|
(1) "Managing" policies includes creating, updating and deleting those.
(2) "Viewing" policies also includes applying those to repositories.
(3) Precisely: those with access to the "Commits" menu.
(4) Precisely: those with "Administer Projects" permission in the project.
(5) Precisely: those with "Browse Projects" permission in the project.
Permissions for installing hook scripts
After creating a policy, there is an additional step of installing the hook script which implements the policy to the VCS repository. It requires a different type of permission, not the above-discussed Jira permissions.
You typically install the hook scripts:
- To the VCS repository's directory in the file system (e.g. when using Git): you will need write type file-system permission on the directory.
- By activating a hook in the VCS management application (e.g. when using Bitbucket): you will need administrator permission on the repository.
As you noticed, none of these permissions are Jira permissions. They are managed outside of Jira, therefore not discussed here.
How linked projects affect the rules and conditions
It is important to understand that linked projects affect only the manageability and visibility of a policy. Linked projects do not affect how rules and conditions are configured or evaluated.
For instance, if policy P is linked to project X, the rules and conditions in P are not restricted to have references to project X only. A condition in P, which is using a JQL query, can check against issues in project Y, for example.
Fixing links that point to deleted Jira projects
Although project deletion is a relatively infrequent action, it is possible for someone to delete a project to which commit policies are linked to.
What happens in that case?
The golden rule of thumb is that the broken links (that point to missing projects) are ignored. In other words, permissions will be checked by using only the actually existing projects.
Although deleting a linked project will change the permissions in an intuitive way, we strongly suggest fixing the broken links to avoid surprises. This is particularly important, because deleting a project can make a policy update-able and viewable by additional users, unwantedly. For example, if policy P is linked to project X, and you delete X, then P becomes a global policy. It also means that although P was originally viewable only by members of X, now being a global policy, it is visible for everyone.
The app makes it easy to detect broken links as they are displayed as MISSING:12345 in every relevant screens (12345 is the ID of the missing project). To fix them, just update the policy, explicitly delete the broken link and consider adding a new link to replace the broken one. If the policy itself is no longer needed, you can also delete that, of course.
Anatomy of commit policies
We designed commit policies to offer great flexibility without imposing too much complexity on administrators and committers. Commit policies offer some configuration options plus have two level structures, composed of rules and conditions.
Note that you may not need to use multi-rule policies in simple use cases, but it is important to be aware of the possibilities.
Commit policy configuration options
(Re-)verifying already existing commits
(since 3.0.0 for Git)
Git allows the same commit (i.e. a commit with the same hash) to appear on multiple branches. This leads to an interesting decision from the commit policy verification point of view.
Imagine the following:
- You are using a policy that requires an "In progress" issue mentioned in the commit message.
- You create a commit referencing a Jira issue FOO-123 (as the policy enforces) on a local feature branch B1.
- You push B1, the commits on this branch are verified and accepted.
- You move FOO-123 to the "Done" status. So far, so good.
- Next day, the integration engineer merges B1 to another branch B2 in the same repository.
- As B2 is receiving new commits, the policy is re-verified against the merged commits. Oops, FOO-123 is not in "In Progress" anymore, so the merge is rejected!
You can use one of the following two strategies to resolve this.
Most likely, you can just accept the commits already in the repository without being re-verified. If so, check the intuitively named option Accept the commits that already exist in the repository (on another branch) without verification.
If you, in fact, need re-verification then you need to rework your policy to work correctly in both when accepting new and already-existing commits. In this case, you should adjust the conditions accordingly or use different rules on different branches.
Verifying merge commits
(since 3.0.0 for Git and Mercurial)
Merge commits are the commits that have more than one parent. Merge commits are typically created when merging two branches.
In most cases, merge commits don't contain "actual" changes, or at least they are discouraged to do so. They are more like "administrative nodes" in the commit graph to combine two lines of development. (They may eventually contain minor changes if there were conflicts manually resolved during the merge.)
Not being "actual" changes, Better Commit Policy bypasses merge commits without verifying them. If this is not what you want, you can turn off the Accept the merge commits without verification option.
(Pre-3.0.0 app versions don't offer this option and always bypass merge commits.)
Rejection messages
Rejection messages can be defined on two levels: for the policy and for individual rules. Those are displayed for the committer when his commit was rejected due to violating the policy or the rule. Their purpose is to explain him why his work was not accepted and to guide him how to fix it.
For this, there are a couple of good practices:
-
Always explain JQL rejected commits.
Instead of relying on the auto-generated message, a well-written informative message will reduce frustration and increase efficiency.
For example, explain the generated message "Include exactly one issue that matches the JQL query <type = story AND sprint = currentSprint() AND status = Open>"" in plain English with "Include exactly one open user story key from the current sprint in the commit message, please.". -
Give an example for a correct commit message that would be accepted.
A good example saves a thousands words.
Add something like this to your rejection message: Use this for example: "Fix for the bug DB-123".
Did you mean?
Did you mean? is an important feature to help fixing rejected commits. See its own page for details.
Rules
Each commit policy contains one or more rules. To satisfy the policy, all of its rules must be satisfied, although you can limit the scope of rules to certain branches or paths only.
Rule types
Each rule has one of the types: tag-, branch- or commit rule. Any rule verifies the object that is suggested by its type name.
Tag rules
(since 3.0.0)
Tag rules verify and guarantee the consistency and integrity of VCS tags. They contain tag conditions.
Branch rules
(since 3.0.0)
Branch rules verify and guarantee the consistency and integrity of VCS branches (plus the commits on the branches). They contain branch conditions.
Commit rules
Commit rules are the most frequently used rules. They verify and guarantee the consistency and integrity of VCS commits. They contain commit conditions.
Scope limiting
The scope of rules can be limited to specific branches and specific paths (directories and files). If a commit is not in the scope of a rule, the rule will silently by-pass (accept) the commit.
Limiting to branches
Using patterns you can define the branches on which the rule is verified. For example, you can require a rule (and its conditions) only on the short-living feature branches, i.e. on the branches named feature/*.
Say, you have a rule limited to the features/* branches. Having one or more commits not matching, matching or partially matching the branch limiting pattern, the rule works like this:
Branches updated by the commits | Behavior |
---|---|
master (no commit matching the pattern) |
The rule is not verified. |
features/FOO-123-my-function (all commits matching the pattern) |
The rule is verified. |
features/FOO-123-my-function and master (some commits matching, some commits not matching the pattern) |
The rule is verified, but only for those commits that are on the branch features/FOO-123-my-function. It is not verified for commits on other branches. |
Limiting to paths
Using patterns you can define the repository paths on which the rule is verified. For example, you can require a rule (and its conditions) only against the files in the images directory, i.e. against the files whose path matches the pattern images/*.
Say, you have a rule limited to the images directory. Having a commit with files not matching, fully matching or partially matching the path limiting pattern, the rule works like this:
Files in the commit | Behavior |
---|---|
html/index.html (no files matching the pattern) |
The rule is not verified. |
images/cat.jpg (all files matching the pattern) |
The rule is verified. |
images/cat.jpg and html/index.html (some files matching, some files not matching the pattern) |
The rule is verified, but only for those files that match images/*. It is not verified for files outside that directory. |
Please note that the pattern does not depend on the operating system's path separator. It is forward slash ('/') on any OS:
path/to/some/dir/* ## correct path\\to\\some\\dir\\* ## will not work!
Conditions
Each rule contains a list of conditions. To satisfy a rule, either all its conditions (logical AND) or at least one of its conditions must be satisfied (logical OR).
Conditions are parametric. Based on their types, they support different sets of parameters. See the next section for more details.
Tag conditions
"Tag name must match a pattern" condition
Verifies the tag name. (since 3.0.0)
It matches a regular expression or a glob pattern to the tag name. The condition is satisfied if the pattern matches the tag name.
Examples:
- Require tag names in semantic version number format (ex: 4.5.11).
- Require tag names be one of the pre-defined "types" like release-1.2.3, snapshot-20180508, etc.
- Require tag names contain a CMDB item identifier (ex: cmdb-4567) that selects a "version" type asset in your CMDB.
- Require tag names be minimum X, maximum Y character long.
"Tag name must contain issue keys from a JQL query" condition
Enforces linking (associating) tag names to Jira issues that are specified by a JQL query. (since 3.0.0)
The Jira issue keys are parsed from the tag name. The parser is using a configurable pattern to ignore string parts that could formally be issue keys, but are not actually issue keys. Then the JQL is executed on the latest issue information in Jira. The condition is satisfied if exactly one or at least one issue key is in the JQL query's result.
Note that the condition works correctly with those issues that were moved between projects, thus changed their issue keys. For example, if an issue was moved from project FOO to project BAR, and the move changed the issue key from "FOO-5" to "BAR-7", the tag name can mention either of the old and the new issue key. The condition will find the issue in both ways.
The parameters of this condition are identical with those of the "Commit message must contain issue keys from a JQL query" condition. See that for more details.
Examples:
- A "Task" type issue whose summary starts with "Release ..." issue must be associated with each tag.
- A "Configuration" type issue that represents a particular state of the source code must be associated with each tag.
Branch conditions
"Branch name must match a pattern" condition
Verifies the branch name. (since 3.0.0)
It matches a regular expression or a glob pattern to the branch name. The condition is satisfied if the pattern matches the branch name.
Examples:
- Require branch names match one of the pre-defined "branch type" names (ex: feature/*, story/*, maintenance/*).
- Require branch names start with a Jira issue key, tracing the changes on that branch back to the issues that triggered them (ex: feature/CAL-123-shared-calendars).
- Require branch names contain a test execution ID, tracing bugfixes back to the failed test executions that triggered them (ex: bugfix/test-678).
"Branch name must contain issue keys from a JQL query" condition
Enforces linking (associating) branch names to Jira issues that are specified by a JQL query. (since 3.0.0)
The Jira issue keys are parsed from the branch name. The parser is using a configurable pattern to ignore string parts that could formally be issue keys, but are not actually issue keys. Then the JQL is executed on the latest issue information in Jira. The condition is satisfied if exactly one or at least one issue key is in the JQL query's result.
Note that the condition works correctly with those issues that were moved between projects, thus changed their issue keys. For example, if an issue was moved from project FOO to project BAR, and the move changed the issue key from "FOO-5" to "BAR-7", the branch name can mention either of the old and the new issue key. The condition will find the issue in both ways.
The parameters of this condition are identical with those of the "Commit message must contain issue keys from a JQL query" condition. See that for more details.
Examples:
- A "Story" type issue from the currently running Scrum sprint must be included in story branch names, tracing changes back to the story (or requirement) that triggered them.
- An unresolved "Bug" type issue must be included in bugfix branch names.
- A "Maintenance Task" type issue assigned to the current user must be included in maintenance branch names.
Commit conditions
"Commit message must match a pattern" condition
Verifies the commit message.
It matches a regular expression or a glob pattern to the commit message text. The condition is satisfied if the pattern matches the attribute.
Examples:
- Require at least 10 character long commit messages (excluding whitespace).
- Require commit messages starting with a Jira issue key, tracing changes back to the issues that triggered them.
- Require commit messages containing a test execution ID, tracing bugfixes back to the failed test executions that triggered them.
"Commit message must contain issue keys from a JQL query" condition
Enforces linking (associating) commits to Jira issues that are specified by a JQL query.
The Jira issue keys are parsed from the commit message. The parser is using a configurable pattern to ignore string parts that could formally be issue keys, but are not actually issue keys. Then the JQL is executed on the latest issue information in Jira. The condition is satisfied if exactly one or at least one issue key is in the JQL query's result.
Note that the condition works correctly with those issues that were moved between projects, thus changed their issue keys. For example, if an issue was moved from project FOO to project BAR, and the move changed the issue key from "FOO-5" to "BAR-7", the commit message can mention either of the old and the new issue key. The condition will find the issue in both ways.
You can use JQL variables in the JQL parameter to make that even smarter and more dynamic. Their names are substituted with their actual values just before Jira executes the JQL search.
Examples:
- At least one unresolved issue must be associated with every commit.
- Being close to the product release, only commits linked to an unresolved issue with critical or blocker priority are accepted.
- Exactly one unresolved issue in the current Scrum sprint must be associated with every commit.
- If the repository contains library code shared by multiple products, every commit must mention at least one unresolved task or bug from one of the products' Jira projects.
For inspiration and more example, see the Best practices page.
Options
The "Allow and ignore the issue keys that don't match the JQL" option is enabled only for the conditions which require exactly one issue key matching a specific (non-blank) JQL. Assuming that we have two Jira projects (FOO and BAR) and the JQL query is "project = FOO", this table explains how it works:
Commit message | "Allow..." is checked | "Allow..." is unchecked |
---|---|---|
"FOO-1 My bugfix" | Accepted (there is no additional issue key) |
Accepted (there is no additional issue key) |
"FOO-1 FOO-2 My bugfix" | Rejected (the additional issue key matches the JQL and exactly one is required) |
Rejected (the additional issue key matches the JQL and exactly one is required) |
"FOO-1 BAR-1 My bugfix" | Accepted (the additional issue key is not matching the JQL) |
Rejected (additional issue keys are not allowed) |
"FOO-1 BAZ-1 My bugfix" | Rejected (the additional issue key is not existing) |
Rejected (the additional issue key is not existing) |
As an alternative, you could use the global issue key pattern setting to exclude issue keys from the verification. That setting is useful to exclude "static and globally ignorable substrings" (like test identifiers from an external Test Management system), while this option is a more local and more dynamic approach.
"Changed paths (files) must match a pattern" condition
Verifies the committed files, including modified, added, removed or otherwise touched files.
It matches a regular expression or a glob pattern to the every file included in the commit. The condition is satisfied only if the pattern matches all file paths.
Examples:
- Only accept .html, .javascript, .css, etc. files in a web development project.
- Reject *.tmp, *.cfg, *.obj, *.class, .* and other temporary, hidden or local-only files from being checked in to the repository.
- Lock the "/doc/legal" directory and the "copyright.txt" file from modifications.
In many cases, it is useful to combine this condition with the scope limiting patterns. For example, you want to enforce using PNG images in the images directory. You can do this in two ways:
- Define a pattern that accepts *.png in the images directory, but any file outside images. This pattern is doable, but can easily become complicated.
- Define a rule with its scope limited to the images/* pattern, and add a condition to accept images/*.png files. This rule will not be applied outside the images directory, and this is what we want.
"Changed paths (files) must contain issue keys from a JQL query" condition
Enforces linking (associating) changed file paths to Jira issues, that are specified by a JQL query. (since 3.0.0)
The Jira issue keys are parsed from the paths. The parser is using a configurable pattern to ignore string parts that could formally be issue keys, but are not actually issue keys. Then the JQL is executed on the latest issue information in Jira. The condition is satisfied if exactly one or at least one issue key is in the JQL query's result.
Note that the condition works correctly with those issues that were moved between projects, thus changed their issue keys. For example, if an issue was moved from project FOO to project BAR, and the move changed the issue key from "FOO-5" to "BAR-7", the path can mention either of the old and the new issue key. The condition will find the issue in both ways.
The parameters of this condition are identical with those of the "Commit message must contain issue keys from a JQL query" condition. See that for more details.
Examples:
- A "Bug" type issue must be included in the name of each file in the patches directory.
- A "Test" type issue must be the name of each sub-directory in the testresults directory.
"Committer must have a valid Jira account" condition
Verifies if the committer has a valid user account in Jira, and if he is a member in specific groups.
The user account is looked up by the committer's username or email address, optionally. When using the username option, the VCS username must be identical with the Jira username. When using the email address option, we find the first user account registered with that email address in Jira. If there are multiple matches, a warning is written to the Jira log.
Please note that albeit all VCS support usernames, emails are handled differently. Git has a dedicated user.email setting for this, Mercurial encodes it into the username setting, but Subversion does not support this at all.
You can list multiple groups and require the member to be member in at least of those. If you specify no groups, then group membership is not verified.
Examples:
- Accept commits only from (valid and active) Jira users.
- Accept commits only from Jira users in the android-developers or iphone-developers groups.
"Committer attribute must match a pattern" condition
Verifies the identity of the committer.
It matches a regular expression or a glob pattern to the username or email address of the committer. The condition is satisfied if the pattern matches the attribute.
It is important to understand that the committer does not necessarily required to have a Jira account! When you are using this condition together with the "committer must have a valid Jira account" condition, you can verify if committer is using the company email address or you can allow committing only for specific users. When you are using this condition alone, you can still implement verifications based on the VCS(!) user accounts.
Examples:
- Accept commits only from users listed by their usernames.
- Temporarily or permanently freeze the repository by not accepting changes from anyone.
Custom conditions (written in Groovy)
If you have special requirements that cannot be solved with the built-in conditions, we allow writing custom conditions in the Groovy language.
This condition executes a Groovy script, with the "context" (Groovy binding) containing the list of commits, the policy details, and other contextual information. The script has full access to Jira internals and to data managed by other apps (like Jira Agile or Zephyr Squad). It can also call an external API and integrate with other resources that provide data for the policy verification.
This condition is not included in the app yet. Please vote for the feature request if you want us to made this production ready.
Hook scripts
Hook scripts are little programs that are automatically executed by the Version Control System when a developer commits a change. In the context of Better Commit Policy, they represent the connection between the Version Control System and Jira. They are written in Python, an easy to use programming language that is readily available in most server environments.
The concept of hook scripts exists in all major Version Control Systems with some differences:
- Hook terminology: Git calls them hooks, Subversion calls them hooks, Perforce calls them triggers, but they are essentially the same.
- Event terminology: Git calls it pre-receive, Subversion calls it pre-commit, Perforce calls it change-submit, but this is essentially the same event. It is fired before a change is to be sent to the central repository.
The hook scripts used by Better Commit Policy are designed to completely hide the VCS differences from the commit policy verifier engine. The purpose is to allow using any policy containing any condition with any Version Control System.
Technically speaking, there is a different script for each supported VCS, and it is the responsibility of the script to normalize the data to the common format expected by the verifier engine. The evaluation is done in the server-side and it is 100% VCS-agnostic.
How hook scripts work
Although it's unlikely that you will ever need to modify a script, it is useful to understand what happens under the hood:
-
When started, the script collects information about the commit to be made.
This primarily includes the committer user, the commit message and the list of the modified files. (How it is done highly depends on the VCS system used.) - Then the script sends an authenticated HTTP request to Jira by posting the previously collected information to a REST end-point.
-
The app evaluates the conditions on the passed the commit information.
As result, it will return an HTTP status code and an optional text message to the script. The status code is one of accepted, rejected or execution error. The message is to explain the result for the user, typically why his work was rejected. - Based on the status code, the hook script either lets the VCS system to process the commit (when accepted) or to display the result and terminate (when rejected). (This step also depends on the concrete VCS system.)
Hook script security
Communicating with Jira requires hook scripts to login to Jira. Scripts will use the user account encoded in the secure token in the hook script configuration. It defaults to the user who generates the hook script.
It has one very important consequence: all Jira operations made while evaluating a policy is done on behalf of the user defined in the hook script's configuration.
For example, when you specify a JQL query for the JQL condition, the query will be executed by that given user. If he has no access to a project, for example, then the query will not return issues from that project, and you cannot include issue keys from that project in the commit message.
Consequently, you should use a user account that has at least view access to the projects that may be referenced in commit messages. The user account should also have the permission to view other users if you want to use conditions that verify the committer.
Hook script requirements
Executing the hook scripts requires the following minimal set of software installed on the server where the hook scripts and the VCS repositories are hosted:
-
Python: both Python 2.7+ and 3.3+ lines are supported.
You can install Python from this page for all major operating systems (Linux, Windows, Mac OS X).
Note: most Linux distributions have Python pre-installed, you can verify them by executing the python and python3 commands. -
pip: (only needed in pre-2.0.0 app versions!) Python package manager, only used to install the Requests library (see next item).
The actual installation steps are simple, but depend on your operating system. The app will guide you after you selected your OS. -
Requests module: (only needed in pre-2.0.0 app versions!) the hook scripts use this HTTP library to help with the HTTP protocol.
The actual installation steps are simple, but depend on your operating system. The app will guide you after you selected your OS.
Generating hook scripts
As written previously, the hook script itself and its installation procedure heavily depends on the Version Control System used, and partly on the Operating System used. The app guides you through the hook script installation process with step-by-step instructions, to make it fast and easy.
You can start the Hook Script Wizard from the Commit Policies page by clicking the Apply to a repository link at a policy.
Select your environment settings in the first page of the wizard, then precisely follow the instructions.
Please note that any user, not only Jira administrators, can generate hook scripts for his local clone if he is using a distributed VCS and wants to take advantage of local verifications.
Anatomy of a hook script
A hook script consists of multiple files, that depend on your VCS system and your operating system. The Hook Script Wizard pre-configures the files you will need and creates the hook package (a ZIP file) for you from those.
The hook package includes a sub-set of these:
- jcp_config.py: configuration for the hook script. See this section for details.
- pre-receive, pre-commit: the main logic of the script with the filename prescribed by the VCS. It should never be modified, unless you know what you are doing.
- jcp_common.py: a library of common routines for hook scripts. It should not be modified normally.
- pre-commit.bat: Helper batch file for Windows. (Subversion hooks can start executable files only. Python is not an executable in Windows, therefore this file is used to launch the actual Python script.)
Configuring hook scripts
The configuration is captured in a single Python file called jcp_config.py, which is in the same directory as the hook script file itself. jcp_config.py is basically a list of variables pre-configured by the Hook Script Generator. All variables has a sensible default value, but you can edit the file any time later to change those. (Those changes will take effect immediately after saving the file.)
Configuration variables:
Configuration variable | Description | Default value |
---|---|---|
commit_policy_id | Identifier of the commit policy to verify against in this hook script. | ID of the commit policy selected in the Hook Script Generator. |
jira_base_url | Base URL of the Jira instance that will verify the policies. | URL of the Jira that was used to generate the hook script. It is unlikely you ever need to modify this. |
jcp_token | Security token to login to Jira using token-based authentication. |
This value is generated automatically.
Do not ever change this, unless you know what you are doing! Leave this empty and set jira_login and jira_password instead for password-based authentication. |
jira_login | User name to login to Jira using password-based authentication. |
User name of the user who generated the script.
As he is always a Jira system administrator, the hook script will have access to all Jira data for verification. This is ignored when jcp_token is set, i.e. when using token-based authentication. |
jira_password | Password to login to Jira using password-based authentication. |
Password entered when the script was generated. It must, obviously, match the previous variable. This is ignored when jcp_token is set, i.e. when using token-based authentication. |
python_path (only for Subversion) | Path of the python executable. | You may change this value if your system cannot access the Python interpreter as it is the case with e.g. some VisualSVN environments. Providing the absolute path to Python is a good practice if you encounter any errors. |
svnlook_path (only for Subversion) | Path of the svnlook application. | It is simply "svnlook", assuming that the Subversion directory is included in your PATH environment variable. Running the hook script in diagnostic mode checks if svnlook can be properly started. If it cannot, then add the Subversion directory to the PATH (or use an absolute path for this variable) and retry. |
windows_default_encoding (only for Subversion on Windows) | Character encoding used by Windows commandline programs for text data. (This variable affects how the hook scripts execute the commandline clients of your Version Control System.) |
None (forces the hook script to auto-detect the encoding from the Windows system settings). If you are loosing special characters in filenames or commit messages (ex: "ö" is transformed to "o", or Asian characters are transformed to garbage), then follow this guide to fix it. |
ssl_verify | Verify SSL certificate when accessing Jira. | True, so the SSL certificate will always be verified. Setting this to False imposes a security risk and is therefore not recommended in production. |
Authentication modes
Hook scripts need to authenticate to the Jira server before any commit verification can take place.
Until version 2.0.0 only password-based authentication was available, but that version introduced a better method called token-based authentication. Token-based authentication has significant advantages in most cases, thus this is the default. (Password-based authentication is still supported for backward compatibility reasons and for special use cases.)
Token-based authentication
(since 2.0.0)
This mode uses a generated secure token to log into Jira. Every time the user generates a hook script pack, a personal unique token is generated and inserted into the hook configuration file. A token looks like this:
477c9944c9b8eeb2acfed4012d8ebfa591c54a39dff9ac1daf9894fe9d65fa0f9a98999e989c9a9a999e99909a9191989fc9dac7c686cfc7c5cac9db
The token encodes the user's key who generated the token, the token submission date (to support token expiration), and a strong SHA-256 cryptographic hash to ensure integrity. In addition to these, each Jira instance has a so-called token secret that is used to generate and verify the token.
As this mode does not store Jira passwords in any way, this is very secure. The token also depends on your Better Commit Policy instance, so that cannot be used by a malicious user to access any other REST end-points of Jira.
When to use this? Always use this mode unless you have a really strong argument not to.
Password-based authentication
This mode uses the Jira username and password to log into Jira. As those are written as plain text to the hook configuration file, this method is less secure.
In order to increase its security, it is highly recommended to:
- Protect the hook configuration files in the file system. Ex: make them readable only for administrators.
- Use HTTPS connections from the hook script to Jira, so that the password is transmitted in an encrypted form over the network.
If you wanted to switch to this mode for some reason, follow these instructions:
- Generate and download your hook script pack.
- Unpack the ZIP file.
- Modify jcp_config.py: set the value of jcp_token to an empty string, and set the values of jira_login and jira_password.
- Run a diagnostic test to make sure that the login is accepted.
Diagnostic mode
Even if you followed the installation steps precisely, there may errors occur when executing the hook scripts. Errors may also occur later on the go, caused by an invalid Jira password or a temporary network problem, for example.
To detect these type of problems, hook scripts can be executed in the so-called diagnostic mode. Diagnostic mode will both validate the script's configuration, to make sure that everything is configured correctly, and send a test request to Jira, to test the network communication.
For diagnostic mode, just execute the hook script itself without any arguments like this (you will need execution permissions on Linux, OS X or other Unix-flavoured systems):
johndoe@acmeserver:/svn-repos/awesome-product/hooks$ ./pre-commit Hook script (version 1) running in DIAGNOSTIC mode Validating configuration parameters... OK Checking the svnlook application... OK Connecting to http://jira.acme.com/jira/rest/commit-policy/1.0/verify/1... OK Sending diagnostic commit data... OK ------------------------------------------------------------------------ TEST SUCCESS ------------------------------------------------------------------------ johndoe@acmeserver:/svn-repos/awesome-product/hooks$
Not only it will show you the cause of the problem, it will also give hints how to resolve those. For instance, if the Jira REST end-point is not accessible for the script, it will ask you to check the Jira URL in your web browser.
Overall, diagnostic mode is an easy-to-use and super-useful tool when resolving problems around hook scripts.
Hook script versions
Time to time, new Better Commit Policy versions introduce changes to the hook scripts in order to offer new functionality. It has two consequences.
First, all hook scripts generated with the new app version will be up-to-date and offer the most current function set. That's great.
Second, this also means that existing hook scripts (which are already installed to repositories) need to be upgraded to take advantage of the improvements.
Upgrading hook scripts can be tedious, therefore we try to minimize its frequency, but sometimes this cannot be avoided, otherwise the app wouldn't be able to introduce new functionality. For example, if the old version does not collect enough information for proper verification of newly introduced objects (branches, tags, etc.), then there is really no other option than upgrading.
Version checks
To help finding outdated hook scripts, there is a built-in version check mechanism in the app. It works like this:
- Hook scripts know their own version. At every verification, the hook script sends its version to the server.
- The app on the server checks if there is a newer version available for that particular VCS type.
- If there is, then:
-
A warning like this is written to the Jira log:
Outdated GIT_SERVER hook script version 1.2.3 found. Re-install the hook script in repository <my favourite repository>
This makes the problem visible for the Jira admins. -
A message like this is displayed to the committer:
Outdated GIT_SERVER hook script version 1.2.3 found. Re-install the hook script in repository <my favourite repository>
This makes the problem visible for the committer.
(Unfortunately, Subversion does not allow displaying messages for accepted commits. Due to this limitation, Subversion committers will see this warning in the Subversion client only if the commit was rejected for some other reason.)
-
A warning like this is written to the Jira log:
Overall, it is easy to see which repositories are still using outdated hook scripts.
Upgrading hook scripts (and other type of clients)
Upgrading hook scripts
To upgrade an outdated hook script, all you have to do is simply re-installing the hook script to the repository. The affected repository is identified in the message.
The re-installation will install the most current hook script version, which is compatible with your app version and your VCS, to the repository. It will simply overwrite the outdated version existing there.
Please note that there can be two possible "locations" for hook scripts. The upgrade process is slightly different for those:
- Server-side hook scripts (in the central repository): an admin with access to the server-side file system (which hosts the repository) should re-install the hook script.
- Client-side hook script (in the local clone): the developer should re-install the hook script to his local repository. There is really no need for the admin to be involved in this case.
In any case, after you successfully upgraded, the "outdated hook script version" message will not be shown any longer.
Upgrading Better Commit Policy for Bitbucket
If you are using Bitbucket to manage your Git repositories, then the Bitbucket app called Better Commit Policy for Bitbucket is used (instead of actual hook scripts) to interact with Better Commit Policy for Jira. In that case, the Bitbucket app itself is the "hook script".
Similarly to hook scripts, the Bitbucket app may also become outdated. To upgrade that, simply upgrade the version of ommit Policy Plugin for Bitbucket.
Hook script compatibility matrix
The hook script's version is the version of the app in which that script version was introduced. The following versions are available:
Git (server) | Git (local) | Subversion | Mercurial |
---|---|---|---|
|
|
|
|
The compatibility rule is simple: use the greatest hook script version that is less or equal than your app version.
For example, if you are using app version 3.0.0, then you should use Git server hook script 3.0.0 or Mercurial hook script 2.0.0.
Global settings
Certain configuration settings are global to the app, i.e. they universally apply to every policy.
To access the global configuration page, login to Jira as administrator, then go to the Commits drop-down in the top → Commit Policies → Global Configuration (in the top-right corner). Alternatively, login as Jira administrator, then go to Add-ons → Global Configuration in the Commit Policies section in the left-bar.
Parser patterns
Issue key pattern for commit messages
This regular expression (regex) is used to recognize Jira issue keys in commit messages. The default setting matches every text piece that is formatted like (and thus potentially could be) a Jira issue key, like FOOBAR-123.
Only change this pattern if in the commit messages you want to mention strings that are formally identical with Jira issue keys, but that are not actually Jira issue keys. For instance, you may want to mention a reference to an item in your Requirements Management or Test Management system, like REQ-123 or UITEST-456, that you don't want to be falsely recognized as issue keys.
You can ignore those unwanted strings with the following patterns:
(?<=\s|\W|^)(?!REQ)[A-Z]+\-\d+(?=\s|\W|$) ## ignores REQ-123 strings (?<=\s|\W|^)(?!REQ|UITEST)[A-Z]+\-\d+(?=\s|\W|$) ## ignores REQ-123 and UITEST-456 strings
The second pattern should give you an idea how to implement this for any number of prefixes.
Using lowercase or mixed case issue keys
(since 3.0.0)
Jira consistently uses all-uppercase project and issue keys everywhere (ex: FOO-123). That is the default for Better Commit Policy, too.
Some users may prefer lowercase (ex: foo-123) or mixed case issue keys (ex: Foo-123) though. For that, set your issue key pattern to:
(?<=\s|\W|^)[A-Za-z][A-Za-z\d]*\-\d+(?=\s|\W|$) ## matches FOO-123, Foo-123 and foo-123
Using numbers in project keys
Starting from Jira 7, project keys can contain numbers (ex: FOO16). If you have installed Better Commit Policy version 2.0.0+, then the default issue key pattern also supports this, recognizing issue keys like FOO16-123.
In contrary, if you upgraded to 2.0.0+ from a previous app version, then the default issue key pattern will not support numbers. To fix this, just go to Global Settings, clear the issue key pattern and save your settings. The issue key pattern will be reset to the new default.
Issue key pattern for branches, tags and paths
This regular expression (regex) is used to recognize Jira issue keys in branch names, tag names and the paths of modified files. You may want to use a pattern that is different from commit messages, because you may prefer different white space characters or uppercase/lowercase rules here.
See issue key pattern in commit messages section above for more details.
Subversion branch pattern
This regular expression (regex) is used to recognize branch names in Subversion paths.
See the Subversion-specific verifying branches and tags section for more details.
Subversion tag pattern
This regular expression (regex) is used to recognize tag names in Subversion paths.
See the Subversion-specific verifying branches and tags section for more details.
Bypass patterns
There are certain situations when commits should be accepted even if they don't satisfy the commit policy. Normally, you would fix those commits, but in reality this isn't always an option.
Typical examples:
- Merging a branch which was created before you started to use commit policies at all
- Merging a branch which was created using a different commit policy
- Receiving contribution to the source code from an external source
- Receiving an automatic commit from the build/CI/CD system
Bypass patterns allow handling these situations in an elegant way.
How bypass patterns work
Bypass patterns are regular expressions. They are matched against either the commit message or the committer name. When there is a match, the complete changeset or just the matching commit is accepted without verifying the commit policy. (Reminder: a changeset is a series of commits pushed to the VCS in one go.)
Therefore, 4 bypass patterns are supported:
- Changeset bypass message pattern: accepts the complete changeset without verification if the commit message of any commit in the changeset matches this.
- Changeset bypass user pattern: accepts the complete changeset without verification if the committer user of any commit in the changeset matches this. (since app version 4.3.0)
- Commit bypass message pattern: accepts a single commit without verification if its commit message matches this. (since app version 4.3.0)
- Commit bypass user pattern: accepts a single commit without verification if its committer user matches this. (since app version 4.3.0)
Usage examples:
- Pattern 1 and 3: simply include "#noverify_all" and "#noverify_one" anywhere in the commit message, like "Fix for the morning NullPointerException #noverify_one".
- Pattern 2 and 4: if your CI/CD system creates commits as user "cicd", then set the pattern "cicd" to the global setting, and the system's commits will not be verified.
Beware with the precedence: pattern 3 should not "extend" pattern 1! For example, if pattern 1 is "#foo" and pattern 3 is "#foobar", then the commit message "Bugfix #foobar" will match not only pattern 3, but also pattern 1. Then, the complete changeset will be accepted without verification, unexpectedly. (The same applies to pattern 4 and pattern 2, obviously.)
How to use bypass patterns?
Best practices:
- Bypass pattern should not be misused by turning to them every time when it feels "inconvenient" to correctly create the commit. They should only be used as a workaround in situations when the commit policy must be violated for a good reason.
- It is a good idea to keep them in "secret", so that only a restricted group of senior developers, team leaders or integration engineers know them.
- As bypass patterns are regular expressions, it is possible to use multiple "bypass sub-categories", like #noverify_all_external and #noverify_all_legacy.
Security
Token secret
This value, frequently called salt in cryptography, is used to generate and verify tokens. You must keep this setting secure, because this is what makes token-based authentication cryptographically secure and reliable.
When you first install Better Commit Policy (or you upgrade from a pre-2.0.0 version to a newer one), this secret is initialized to a random value. Please note that changing the token secret will invalidate all tokens that were generated using the old value of the secret! That means that you only need to alter it, if it became known for some external party due to a data breach, and if you are ready to re-generate and re-install all your existing hook scripts.
Token expiration period
A token is only considered valid if its age does not exceed the expiration period, expressed in days. Technically speaking, each token encodes its creation date and we check whether (creation_date + expiration_period) > now. The default value is 999999999, effectively disabling token expiration.
You may want to use this feature:
- to temporarily invalidate all existing tokens by setting the expiration period to a small value (1 day),
- or to force your developers to periodically re-generate their hook scripts for maintenance or security reasons. For instance, you can enforce them to upgrade their local hook scripts in every 365 days to get the latest version.
User interface
"Commits" menu visibility
You can restrict which Jira groups the main Commits menu in the top navigation bar should be displayed for. You can reduce the clutter by hiding it for anyone, but your administrators and developers, who will actually use it. Leave this setting empty to display it for every authenticated user (this is the default setting). Note: anonymous users will never see this menu.
Pattern syntax
Many of the app settings rely on using so-called patterns that are matched against pieces of text, like commit messages or branch names. There are two kinds of pattern syntaxes supported, each with its own merits.
Regular expressions
Regular expressions (regex) offer a super powerful concept for string searching. Although the Wikipedia article on regex is full of details, it may be overwhelming for the first read. You could instead start with:
- Regex tutorial - A quick cheatsheet by examples
- RegexOne (with interactive excercises)
Please note that regex has multiple, slightly different dialects. Better Commit Policy uses the Java-style regex dialect. To test if your regex works exactly as expected, RegexPlanet (for Java) is a safe choice.
Glob
Globs offer a simpler syntax relying on wildcards. Globs may be significantly easier to use than regex in many use cases, but the cost of simplicity is that their flexibility is limited.
See the Wikipedia article on glob for start.
Then read through one of the many glob tutorials on the web. Although many of those articles are talking about globs in the context of the Unix operating system, those are equally applicable to the Better Commit Policy glob expressions, too.
Differences between Centralized- and Distributed Version Control Systems
There are two main paradigms in Version Control at the moment: centralized (like Subversion) and distributed (like Git). Providing a full comparison between those rather different ideas is not in the scope of this manual. Please refer to the distributed revision control article in Wikipedia instead. This section focuses on their differences from the Better Commit Policy's point of view.
Verifying policies
Centralized
In case of a centralized version control system, users commit a single change at a time. That commit can be verified, accepted or rejected immediately. When rejected, the developer can fix the problem and commit again. There is no delay, everything happens "immediately".
Distributed
Distributed version control systems (DVCS), however, work in a different way. They allow working offline, making any number of commits in the developer's local repository ("local clone") without communicating with the central repository. Then, when the time comes, developers can "push" all those changes in one go to the central repository. A single push can include any number of commits, even on multiple branches, any number of tags, and so on.
The most frequently used distributed workflows use 3 different types of repositories. We suggest implementing commit verification for each type, because:
- Blessed repository: as it stores the "official version" of the code, verifying commit policies in blessed repositories is an absolute must. Learn more about installing server-side commit hooks.
- Server-side clone: it stores the "working copy" for the developer on the server (there is one of this per developer). As the blessed repository is involved relatively late in the process, verifying commit policies in server-side clones finds wrong commits earlier, making it easier to fix those. Learn more about installing server-side commit hooks.
- Local clone: it stores the code on the developer's own work station (locally). This is where the actual development work is getting done. Verying commit policies in local clones is the earliest point in the process to find wrong commits. It will simply super-charge your developers, save frustration and unnecessary efforts with fixing wrong commits. Learn more about installing local commit hooks.
Better Commit Policy is the only commit verifier app for Jira that supports all these 3 types. (Alternative apps support only the first and eventually the second one.)
Watch this quick video to understand the difference between server-side (remote) and local commit verification:
When verifying, either the whole set of changes is accepted or the whole is rejected. There are no "partially accepted" change sets. The conditions of the policy are evaluated on each commit separately, and then the changeset is accepted only if every commit in it satisfies those. In other words, the whole changeset is rejected and must be fixed if there is only one commit in it that violates the commit policy.
Fixing commits
Centralized
To fix a commit to a centralized version control system, just commit again after you made the fixes (like a rewritten commit message):
Distributed
To fix a commit to a centralized version control system, see these guides:
Next step
This page summarized the general, VCS-independent concepts that apply to any Version Control System supported by the app. Learn more about the VCS-dependent features in the Git, Subversion, Mercurial, Perforce, TFS (Team Foundation Server), and ClearCase pages.
Questions?
Ask us any time.