Asynchronous Post-Receive with simple configuration

Here is an example of an asynchronous post-receive hook that notifies a specific url endpoint of changes. This might be useful for notifying your CI build server that Stash has been updated, rather than polling. The source is available for download from Bitbucket here.

src/main/resources/atlassian-plugin.xml

<atlassian-plugin name="My Repository Hook Example" key="example.plugin.myhook" plugins-version="2">
    <plugin-info>
        <description>A basic post-receive hook that notifies a remote server when changes are pushed to Stash</description>
        <vendor name="My Company" url="http://www.mycompany.com"/>
        <version>1.0</version>
    </plugin-info>

    <repository-hook key="examplehook" name="Webhook" class="com.mycompany.example.plugin.myhook.MyRepositoryHook">
        <description>Webhook for notifying a configured endpoint of changes to this repository.</description>
        <icon>icons/example.png</icon>
        <config-form name="Simple Hook Config" key="simpleHook-config">
            <view>stash.config.example.hook.simple.formContents</view>
            <directory location="/static/"/>
        </config-form>
    </repository-hook>

</atlassian-plugin>

src/main/java/com/mycompany/example/plugin/myhook/MyRepositoryHook.java

package com.mycompany.example.plugin.myhook;

import com.atlassian.stash.hook.repository.*;
import com.atlassian.stash.repository.*;
import com.atlassian.stash.setting.*;
import java.net.URL;
import java.util.Collection;

/**
 * Note that hooks can implement SettingsValidator directly.
 */
public class MyRepositoryHook implements AsyncPostReceiveRepositoryHook, RepositorySettingsValidator {

    /**
     * Connects to a configured URL to notify of all changes.
     */
    @Override
    public void postReceive(RepositoryHookContext context, Collection<RefChange> refChanges) {
        String url = context.getSettings().getString("url");
        if (url != null) {
            try {
                new URL(url).openConnection().getInputStream().close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void validate(Settings settings, SettingsValidationErrors errors, Repository repository) {
        if (settings.getString("url", "").isEmpty()) {
            errors.addFieldError("url", "Url field is blank, please supply one");
        }
    }
}

Note the absence of HookResponse in the method signature, which comes from being asynchronous. This is important to ensure that pushes are not affected by slow hooks.

src/main/resources/static/simple.soy

{namespace stash.config.example.hook.simple}

/**
 * @param config
 * @param? errors
 */
{template .formContents}
    {call aui.form.textField}
        {param id: 'url' /}
        {param value: $config['url'] /}
        {param labelContent}
            {stash_i18n('stash.web.test.hook.config.label', 'URL')}
        {/param}
        {param descriptionText: stash_i18n('stash.web.test.hook.config.description', 'A URL to be notified') /}
        {param extraClasses: 'long' /}
        {param errorTexts: $errors ? $errors['url'] : null /}
    {/call}
{/template}