Samples

Writing a Custom Pre-Receive Hook

To write your own hook use Custom Pre-Hook. Enter the path to your groovy script file, or enter the script inline as usual.

The following examples will walk you through writing a custom pre-receive hook to block changes from being pushed.

Blocking Changes

A complete example of a hook that rejects everything is:

hookResponse.out().print("You cannot commit at the moment.") // <1>

return false // <2>

Line 1:  Provide an "informative" message that is displayed to the user.

Line 2: Return false to block the push, true to allow it.

Another example of the same hook but rewritten using the Hooks API provided by Atlassian in Bitbucket 5 is:

def message = "You cannot commit at the moment." // <1>

return resultBuilder
    .veto(message, message) // <2>
    .build() // <3>

Line 1:  Provide an "informative" message that is displayed to the user.

Line 4: Veto the result builder to block the push, omit this line to allow the push.

Line 5: Build and return the vetoed result builder to block the push.

We support both ways of writing your hook. The second one closely matches the Atlassian Hooks API introduced in Bitbucket 5.

If a single push fails for different reasons you may want to add multiple messages to inform the developer of the issues that need to be resolved.

Conditionally Blocking Changes

A very slightly more complex example, where we check the name of the repository. If the repository is test then the commit is blocked:

if (repository.name == "test") { // <1>
    def message = "You cannot commit at the moment to any repository named 'test'"
    resultBuilder.veto(message, message)
}

return resultBuilder.build()

Line 1: Define our condition to only veto the push is the repository is test.

It can be difficult for an end-user to pick out the reason for the failure amongst the response:

Counting objects: 5, done.
Writing objects: 100% (3/3), 256 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
remote: You cannot commit at the moment to any repository named 'test'
To http://acme.com/bitbucket/scm/test/test.git
 ! [remote rejected] master -> master (pre-receive hook declined)
error: failed to push some refs to 'http://acme.com/bitbucket/scm/test/test.git'


Therefore, you can use a utility method to call out the error in the response:

import com.onresolve.scriptrunner.canned.bitbucket.util.BitbucketCannedScriptUtils

if (repository.name == "test") {
    def message = BitbucketCannedScriptUtils.wrapHookResponse(new StringBuilder("You cannot commit at the moment to any repository named 'test'"))
    resultBuilder.veto(message, message)
}

return resultBuilder.build()

This produces a response that is easier to read:

Counting objects: 5, done.
Writing objects: 100% (3/3), 258 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
remote:
remote: =====================================================================
remote: You cannot commit at the moment to any repository named 'test'
remote: =====================================================================
remote:
To http://acme.com/bitbucket/scm/test/test.git
 ! [remote rejected] master -> master (pre-receive hook declined)
error: failed to push some refs to 'http://acme.com/bitbucket/scm/test/test.git'

It may be useful to include a link to an internal wiki article in the response, which can explain how to fix the problem.

As with all extension points, you can modify the file and repeat your push, without changing anything in the UI. The script will be automatically recompiled if it has changed, which makes the development process very fast.

When your script is successfully blocking pushes you don’t want, make sure it also allows the pushes that are OK.