Custom Pre-Receive Hooks
Custom pre-receive hooks allow you to control what gets pushed to your Bitbucket repositories by running a script before changes are accepted.
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>
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>
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.
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)
}
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] default -> default (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)
}
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] default -> default (pre-receive hook declined)
error: failed to push some refs to 'http://acme.com/bitbucket/scm/test/test.git'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.