Custom merge checks should return a RepositoryHookResult

To block the merge you should call the veto method on RepositoryHookResult and return the RepositoryHookResult returned by the method.

                import com.atlassian.bitbucket.hook.repository.RepositoryHookResult

                RepositoryHookResult.rejected("You cannot merge", "A more detailed explanation of why you can't merge")

The same functionality can now be achieved using a 'Conditional Merge Check' documented above.

Samples

Enforcing reviewers in several timezones

The intention of this hook is to ensure that the author and reviewers are in at least three time zones, as a mechanism of ensuring people in different offices are aware of code changes.

This approach is used by the Microsoft Kinect team apparently.

Currently we speak to Jira to get the timezone of the participants. When STASH-2817 is fixed this can be done without recourse to Jira. We use the application link to speak to Jira, and cache the results for future usage.

def final nTimeZonesRequired = 3

@BaseScript BitbucketBaseScript baseScript
MergeRequest mergeRequest = mergeRequest

Set participants = mergeRequest.pullRequest.reviewers*.user
participants << mergeRequest.pullRequest.author.user

/**
 * Get list of timezones for a bunch of user names, from JIRA. As this is run every time the PR page
 * is shown, we cache the result
 *
 * @param jiraLink
 * @param participants
 * @return list of timezones
 */
@Memoized(maxCacheSize = 100)
static List<String> getTimeZonesForUsers(ApplicationLink jiraLink, Set<String> participants) {

    def authenticatedRequestFactory = jiraLink.createImpersonatingAuthenticatedRequestFactory()
    def log = Logger.getLogger(this.class)
    def timeZones = participants.findResults { key ->
        authenticatedRequestFactory
            .createRequest(Request.MethodType.GET, "rest/api/2/user?username=$key")
            .addHeader("Content-Type", "application/json")
            .execute([
                handle: { Response response ->
                    if (response.successful) {
                        new JsonSlurper().parseText(response.responseBodyAsString).timeZone
                    } else {
                        log.warn "Failed to look up user $key in JIRA: " + response.responseBodyAsString
                    }
                }] as ApplicationLinkResponseHandler<Void>
            )
    }
    timeZones
}

def timeZones = getTimeZonesForUsers(getJiraAppLink(), participants*.name as Set)

def actualTimeZones = timeZones.unique().size()
if (actualTimeZones < nTimeZonesRequired) {
    RepositoryHookResult.rejected("Not enough timezones covered",
        "You need reviewers in ${nTimeZonesRequired - actualTimeZones} more timezone(s). " +
            "Currently you have: ${timeZones.join(", ")}.")
}