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
<atlassian-plugin key="com.atlassian.bitbucket.server.bitbucket-docs" name="Bitbucket Server - Documentation" plugins-version="2">
<plugin-info>
<description>Provides a Hook for Bitbucket Server, which allows pushes to be denied</description>
<vendor name="Atlassian" url="http://www.atlassian.com"/>
<version>4.7.1</version>
</plugin-info>
<repository-hook key="disableRefChanges" name="Disable Ref Changes" class="com.atlassian.bitbucket.repository.hook.ref.ProtectRefHook">
<description>Disables changes to a specified reference (branch or tag) in this repository</description>
<icon>icons/example.png</icon>
<config-form name="Ref Hook Config" key="refHook-config">
<view>com.atlassian.bitbucket.repository.hook.ref.formContents</view>
<directory location="/static/"/>
</config-form>
<!-- Validators can be declared separately -->
<validator>com.atlassian.bitbucket.repository.hook.ref.RefValidator</validator>
</repository-hook>
</atlassian-plugin>
src/main/resources/META-INF/spring/atlassian-plugins-component-imports.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:osgi="http://www.springframework.org/schema/osgi"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/osgi
http://www.springframework.org/schema/osgi/spring-osgi.xsd">
<osgi:reference id="refService" interface="com.atlassian.bitbucket.repository.RefService" />
</beans>
src/main/java/com/atlassian/bitbucket/repository/hook/ref/ProtectRefHook.java
package com.atlassian.bitbucket.repository.hook.ref;
import com.atlassian.bitbucket.hook.HookResponse;
import com.atlassian.bitbucket.hook.repository.PreReceiveRepositoryHook;
import com.atlassian.bitbucket.hook.repository.RepositoryHookContext;
import com.atlassian.bitbucket.repository.Ref;
import com.atlassian.bitbucket.repository.RefChange;
import com.atlassian.bitbucket.repository.RefService;
import javax.annotation.Nonnull;
import java.util.Collection;
public class ProtectRefHook implements PreReceiveRepositoryHook {
private final RefService refService;
public ProtectRefHook(RefService refService) {
this.refService = refService;
}
/**
* Disables changes to a ref
*/
@Override
public boolean onReceive(@Nonnull RepositoryHookContext context,
@Nonnull Collection<RefChange> refChanges,
@Nonnull HookResponse hookResponse) {
String refId = context.getSettings().getString("ref-id");
Ref found = refService.resolveRef(context.getRepository(), refId);
for (RefChange refChange : refChanges) {
if (refChange.getRefId().equals(found.getId())) {
hookResponse.err().println("The ref '" + refChange.getRefId() + "' cannot be altered.");
return false;
}
}
return true;
}
}
src/main/java/com/atlassian/bitbucket/repository/hook/ref/RefValidator.java
package com.atlassian.bitbucket.repository.hook.ref;
import com.atlassian.bitbucket.repository.Ref;
import com.atlassian.bitbucket.repository.RefService;
import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.bitbucket.setting.RepositorySettingsValidator;
import com.atlassian.bitbucket.setting.Settings;
import com.atlassian.bitbucket.setting.SettingsValidationErrors;
import org.apache.commons.lang3.StringUtils;
import javax.annotation.Nonnull;
public class RefValidator implements RepositorySettingsValidator {
private final RefService refService;
public RefValidator(RefService refService) {
this.refService = refService;
}
@Override
public void validate(@Nonnull Settings settings, @Nonnull SettingsValidationErrors settingsValidationErrors, @Nonnull Repository repository) {
String refId = settings.getString("ref-id", null);
if (StringUtils.isEmpty(refId)) {
settingsValidationErrors.addFieldError("ref-id", "The ref id must be specified");
}else {
Ref ref = refService.resolveRef(repository, refId);
if (ref == null) {
settingsValidationErrors.addFieldError("ref-id", "Failed to find the ref");
}
}
}
}
src/main/resources/static/simple.soy
{namespace com.atlassian.bitbucket.repository.hook.ref}
/**
* @param config
* @param? errors
*/
{template .formContents}
{call bitbucket.component.branchSelector.field}
{param id: 'ref-id' /}
{param initialValue: $config['ref-id'] /}
{param labelText: 'Reference' /}
{param descriptionText: 'Any pushes with changes to this reference (branch or tag) will be rejected' /}
{param errorTexts: $errors ? $errors['ref-id'] : null/}
{param showTags: true/}
{/call}
{/template}
The HookResponse class has both error and output writers, both of which will be displayed to the client.