Pre-Receive with configuration

Here is an example of a pre-receive hook that rejects changes to a specified branch or tag. The source is available for download from Bitbucket here.

src/main/resources/atlassian-plugin.xml

1
<atlassian-plugin key="com.atlassian.bitbucket.server.bitbucket-docs" name="Bitbucket Server - Documentation" plugins-version="2">
2
    <plugin-info>
3
        <description>Provides a Hook for Bitbucket Server, which allows pushes to be denied</description>
4
        <vendor name="Atlassian" url="http://www.atlassian.com"/>
5
        <version>4.7.1</version>
6
    </plugin-info>
7
 
8
    <repository-hook key="disableRefChanges" name="Disable Ref Changes" class="com.atlassian.bitbucket.repository.hook.ref.ProtectRefHook">
9
        <description>Disables changes to a specified reference (branch or tag) in this repository</description>
10
        <icon>icons/example.png</icon>
11
        <config-form name="Ref Hook Config" key="refHook-config">
12
            <view>com.atlassian.bitbucket.repository.hook.ref.formContents</view>
13
            <directory location="/static/"/>
14
        </config-form>
15
        <!-- Validators can be declared separately -->
16
        <validator>com.atlassian.bitbucket.repository.hook.ref.RefValidator</validator>
17
    </repository-hook>
18
 
19
</atlassian-plugin>
 

src/main/resources/META-INF/spring/atlassian-plugins-component-imports.xml

1
<?xml version="1.0" encoding="UTF-8"?>
2
<beans xmlns="http://www.springframework.org/schema/beans"
3
       xmlns:osgi="http://www.springframework.org/schema/osgi"
4
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5
       xsi:schemaLocation="http://www.springframework.org/schema/beans
6
                           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
7
                           http://www.springframework.org/schema/osgi
8
                           http://www.springframework.org/schema/osgi/spring-osgi.xsd">
9
 
10
    <osgi:reference id="refService" interface="com.atlassian.bitbucket.repository.RefService" />
11
 
12
</beans>
 

src/main/java/com/atlassian/bitbucket/repository/hook/ref/ProtectRefHook.java

1
package com.atlassian.bitbucket.repository.hook.ref;
2
 
3
import com.atlassian.bitbucket.hook.HookResponse;
4
import com.atlassian.bitbucket.hook.repository.PreReceiveRepositoryHook;
5
import com.atlassian.bitbucket.hook.repository.RepositoryHookContext;
6
import com.atlassian.bitbucket.repository.Ref;
7
import com.atlassian.bitbucket.repository.RefChange;
8
import com.atlassian.bitbucket.repository.RefService;
9
 
10
import javax.annotation.Nonnull;
11
import java.util.Collection;
12
 
13
public class ProtectRefHook implements PreReceiveRepositoryHook {
14
 
15
    private final RefService refService;
16
 
17
    public ProtectRefHook(RefService refService) {
18
        this.refService = refService;
19
    }
20
 
21
 
22
    /**
23
     * Disables changes to a ref
24
     */
25
    @Override
26
    public boolean onReceive(@Nonnull RepositoryHookContext context,
27
                             @Nonnull Collection<RefChange> refChanges,
28
                             @Nonnull HookResponse hookResponse) {
29
        String refId = context.getSettings().getString("ref-id");
30
        Ref found = refService.resolveRef(context.getRepository(), refId);
31
        for (RefChange refChange : refChanges) {
32
            if (refChange.getRefId().equals(found.getId())) {
33
                hookResponse.err().println("The ref '" + refChange.getRefId() + "' cannot be altered.");
34
                return false;
35
            }
36
        }
37
        return true;
38
    }
39
}
 

src/main/java/com/atlassian/bitbucket/repository/hook/ref/RefValidator.java

1
package com.atlassian.bitbucket.repository.hook.ref;
2
 
3
import com.atlassian.bitbucket.repository.Ref;
4
import com.atlassian.bitbucket.repository.RefService;
5
import com.atlassian.bitbucket.repository.Repository;
6
import com.atlassian.bitbucket.setting.RepositorySettingsValidator;
7
import com.atlassian.bitbucket.setting.Settings;
8
import com.atlassian.bitbucket.setting.SettingsValidationErrors;
9
import org.apache.commons.lang3.StringUtils;
10
 
11
import javax.annotation.Nonnull;
12
 
13
public class RefValidator implements RepositorySettingsValidator {
14
 
15
    private final RefService refService;
16
 
17
    public RefValidator(RefService refService) {
18
        this.refService = refService;
19
    }
20
 
21
    @Override
22
    public void validate(@Nonnull Settings settings, @Nonnull SettingsValidationErrors settingsValidationErrors, @Nonnull Repository repository) {
23
        String refId = settings.getString("ref-id", null);
24
        if (StringUtils.isEmpty(refId)) {
25
            settingsValidationErrors.addFieldError("ref-id", "The ref id must be specified");
26
        }else {
27
            Ref ref = refService.resolveRef(repository, refId);
28
            if (ref == null) {
29
                settingsValidationErrors.addFieldError("ref-id", "Failed to find the ref");
30
            }
31
        }
32
    }
33
}
 

src/main/resources/static/simple.soy

1
{namespace com.atlassian.bitbucket.repository.hook.ref}
2
 
3
/**
4
 * @param config
5
 * @param? errors
6
 */
7
{template .formContents}
8
    {call bitbucket.component.branchSelector.field}
9
             {param id: 'ref-id' /}
10
             {param initialValue: $config['ref-id'] /}
11
             {param labelText: 'Reference' /}
12
             {param descriptionText: 'Any pushes with changes to this reference (branch or tag) will be rejected' /}
13
             {param errorTexts: $errors ? $errors['ref-id'] : null/}
14
             {param showTags: true/}
15
    {/call}
16
{/template}
 

The HookResponse class has both error and output writers, both of which will be displayed to the client.