Repository Hooks and Merge Checks
Introduction
For users familiar with the Git hook system, Stash now provides a way for plugin developers to create their own hooks that can be installed and managed without having to touch the filesystem.
Hooks have been implemented using familiar modules within the Atlassian plugin system.
There are countless examples of hooks that could be deployed to a Stash instance:
- Restrict deletion of branches
- Restrict 'force-pushes' to select branches
- Integrate with a CI server (e.g. Bamboo) or other 3rd party application, to notify when code is pushed or branches are created, merged or deleted
- Require that pull requests have a minimum number of reviewers who have accepted
- Enforce a specific workflow (e.g. master cannot be merged into a release branch)
- Mirror a repository with another instance of Stash
- Ensure all commits contain a JIRA key
Stash supports this through Repository Hook Modules.
Categories
Currently there are three types of hooks that are available in Stash.
Pre-receive
Pre-receive hooks are used to potentially block pushes to Stash. An example might be to stop deletion of branches or limit force-pushes to personal branches. Pre-receive hooks do not fire when a pull-request is merged - for that, you may also want to implement a repository merge-check.
Asynchronous Post-receive
Post-receive hooks are invoked after a push or merge has taken place. An example might be to notify a build server, such as Bamboo, that there are new commits on a branch to be built.
Asynchronous post-receive hooks are preferred when there is no need to return any information to the client, as they are triggered after the push is complete. This is a better experience for the user as they will not have to wait for potentially slow operations.
Merge-checks
Merge checks are used to block pull requests from being merged until a required set of conditions have been met. An example might be that all reviewers must accept the request, or that any associated builds are green.
A RepositoryMergeRequestCheck
has similar functionality to the Merge Check Plugin Module, but additionally allows users to enable, disable and provide configuration on a per-repository basis via the Stash UI.
How-to
Generate a Stash plugin using the Atlassian SDK
Ensure you have a Stash plugin which will contain your hook, which you can create by following this guide. Don't forget to add bitbucket-spi
to your pom.xml
as this will be required to compile against the hook API.
Enabling your hook
Repository hooks need to be enabled and configured on a per repository basis, details are here.
Examples
- Pre-Receive with no configuration
- Pre-Receive with configuration
- Asynchronous Post-Receive with simple configuration and validation
- Merge check with simple configuration
Javadoc
For Java API specific documentation, please see the accompanying Javadoc for the revelant classes/packages.
Configuration
It is possible to add configuration to your hook by specifying a configuration screen. The values of input elements defined in the screen will be saved by the framework and passed to the hook each time it is called. Generally we use AUI templates to generate our controls to keep styling the same, however a simple element <input name="x" type="text"/> would work.
Hook configuration screens can be defined using Closure Templates. Atlassian is currently running an older version of Closure Templates, so you might find the best documentation in the Internet Archive.
Stash provides Stash.template.branchSelector field and input templates that you can include to let users select a branch or tag.
Atlassian User Interface (AUI)
A number of AUI Soy templates are also available to assist in creating a configuration form for your hook. For more information about AUI please read the AUI Docs or try out the live demos in the sandbox. For an a list of available AUI Soy templates that you can use in your own configuration form, consult the AUI Soy source.
Example
This template adds an AUI style text field with the id "url", user inputted text data will be saved with the key "url" and passed to the hook inside a Settings object.
/**
* @param config
* @param? errors
*/
{template .formContents}
{call aui.form.textField}
/** set the name of the input field -- this will be used as the setting key */
{param id: 'url' /}
/** supply the previous value to the text field so the user can see the saved data */
{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' /}
/** if validation returned errors then display them in the error text. */
{param errorTexts: $errors ? $errors['url'] : null /}
{/call}
{/template}
The matching hook accesses the "url" value and also validates it.
public class MyRepositoryHook implements AsyncPostReceiveRepositoryHook, RepositorySettingsValidator {
@Override
public void postReceive(RepositoryHookContext context, Collection<RefChange> refChanges) {
String url = context.getSettings().getString("url");
if (url != null) {
//do stuff
}
}
@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");
}
}
}
Adding Icons
Each hook can have an icon to display in the repository hook administration screen. See the repository hook module type for details.
Marketplace
If and when you decide to add your plugin to Atlassian's Marketplace, you can make your plugin more discoverable by selecting the "Repository Hooks" category when creating your plugin.