This is the git hooks implementation for Atlassian Bitbucket Server (former Stash) External Hooks plugin. This plugin provides an opportunity to add pre- or post-receive hooks without writting them in Java.
- External Hooks plugin wiki
- Githooks doc at git-scm.com
- Atlassian githooks tutorial
Pre- and post-receive hooks are invoked on the remote repository, when a git push is done on a local repository.
Pre-receive hook executes just before starting to update refs on the remote repository, so it’s a good place to enforce any kind of development policy. Its exit status determines the success or failure of the update. If you don’t like who is doing the pushing, how the commit message is formatted, or the changes contained in the commit, you can simply reject it. While you can’t stop developers from making malformed commits, you can prevent these commits from entering the official codebase by rejecting them with pre-receive.
Post-receive hook executes on the remote repository once after all the refs have been updated. Performing notifications and triggering a continuous integration system are common use cases for post-receive.
Both pre- and post-receive hooks are invoked once for the receive operation. Each of them takes no arguments, but for each ref being updated they receive a line of the format:
<old-obj-name> SP <new-obj-name> SP <updated-ref-name> LF
on standard input.
We have implemented pre- and post-receive hooks as a collection of
pluggable modules. githooks.py
serves as an entry point to them,
which loads and runs the modules specified in a configuration file
passed to githooks.py
via command line. githooks.py
reads the
standard input and passes , and
to each plugin being run.
A githooks.py
plugin implements any desired pre- or post-receive
action, such as indentation check or notification service. A zero
status returned from the plugin indicates success (e.g. the ref being
updated passed the check). A non-zero status from the plugin aborts
the pushing.
If multiple refs are pushed, returning a non-zero status from any of
the plugins for any of the refs aborts pushing all of them.
githooks.py
executes plugins regardless of their return status,
so all errors are reported at once.
githooks.py
gets to know which plugins to load and in what setting
to run them from a configuration file that must be passed to
githooks.py
as the first positional argument.
Configuration file format: dict of plugin basenames without extension as keys and plugin settings as values (see Implemented Githooks Plugins).
Note: githooks.py
configuration file must be a valid YAML/JSON.
Note: githooks now uses pyyaml to parse configuration files. Therefore, both YAML and JSON configuration files are accepted. See samples for YAML configuration examples (pre-receive.conf.sample and post-receive.conf-sample).
{
<plugin basename without extension>: <plugin settings>,
...
}
A configuration file for running pep8hook and line_endings plugins with default settings would be:
{
"pep8hook": [],
"line_endings": []
}
Githooks plugins reside in hooks.d.
Note: plugin settings must be a valid JSON.
- line_endings (deny committing files that contain both CRLF and LF line endings)
Implements checking if any of the modified files contains both CRLF and LF line endings.
Settings format: None, always runs with an empty list []
- pep8hook (code style check in python scripts)
Runs pycodestyle on changes in python scripts.
Settings format: None, always runs with an empty list []
- copyright (check copyright string)
Checks if file copyright matches at least one of the configured copyright strings.
Settings format: list of dicts. start
is a pythonic regexp to check
is the copyright is present in the file. full
is a full copyright
string to look for in case the copyright presence is detected by the
above check. It can have the currenct year modifier %Y.
[
{
"start": "Copyright ",
"full" : "Copyright %Y Roga I Kopyta International"
},
...
]
A string formatter for the current year (%Y) might be used.
- notify (subscribe to some paths via .gitattributes and notify of changes made to those paths on specific branches)
Reports changes made to paths with defined 'owners' attribute. 'owners' is a list of comma-separated emails. You can specify branches on which to report changes as a list of python regular expressions that match those branches. Note that this is a per-repository setting, i.e all subscribers will get reports from all those branches.
Settings format:
[
"refs/heads/master",
"refs/heads/release/.*",
...
]
Report format: Email reports are composed for each subscriber email. Essentially an email report is a list of new commits in remote repo where paths for which 'owners' attribute value contains that email are modified. List of commits comes in a similar to 'git log' porcelain output. Additionally, the list of modified paths along with their status is included for each commit.
Branch: <branch>
By user: <username>
Commit: <commit hash> (View in Stash, clickable)
Author: <author name> <author email>
Date: <commit date>
<commit message, trimmed to 100 symbols>
<status: M, A or D> <path to file>
...
Commit: ...
- email_mention (notify users mentioned in commit messages)
Reports commits to users mentioned in commit messages as @username.
git ci -m 'My cool new feature @someone'
to send this commit to
someone@domain. Domain is specified in githooks.ini.
Settings format: None, always runs with an empty list []
Report format: similar to notify's report, but commit messages left untrimmed and does not contain lists of modified files.
- Atlassian Stash/Bitbucket Server
- Stash/Bitbucket Server compatible External Hooks plugin
- Python 2.6 or higher
Note: Tested in the following stack:
- CentOS 6
- Atlassian Stash 3.11.1/Bitbucket Server 4.3.2
- External Hooks plugin 2.5-1/3.0-1
- Python 2.6
- Install and configure Atlassian Stash/Bitbucket Server
- Install External Hooks plugin
- To deploy githooks on a Stash server, clone this repo to External Hooks safe-dir ($STASH_HOME/external-hooks):
$ git clone git://github.com/Parallels/githooks.git $STASH_HOME/external-hooks
Then, edit $STASH_HOME/external-hooks/githooks.ini
and put your
githooks configuration files (.conf) in $STASH_HOME/external-hooks/conf
(pre-receive.conf.sample and post-receive.conf.sample demonstrate the
minimal setup). Change the files mode so that the user that runs Stash
(stash
/bitbucket
by default) owns the files. Python scripts must
be executable:
$ chown -R stash $STASH_HOME/external-hooks
$ chgrp -R stash $STASH_HOME/external-hooks
$ chmod -R 755 $STASH_HOME/external-hooks
Note: default githooks layout can be overriden in [DEFAULT] section of
githooks.ini
:
[DEFAULT]
; githooks logfile (log/atlassian-stash-githooks.log)
log_file = %(LOGFILE)s
; where to look for .conf files (external-hooks/conf)
conf_dir = %(CONFDIR)s
; where to look for hook scripts (external-hooks/hooks.d)
hooks_dir = %(HOOKSDIR)s
- Install dependencies:
$ pip install -r requirements.txt
- Go to repository Settings -> (Workflow) Hooks. Enable and configure
External Pre Receive and Post Receive Hooks. Set Executable to
githooks.py
and check 'Look for hooks only in safe dir'. Put the path to a configuration file in Positional parameters;githooks.py
expects a path that is relative to conf_dir (githooks.ini).
If you get an error while using Git Hooks or discover a bug, please report it on the issue tracker.
Run the unittests:
$ python -m unittest test
To deploy an empty repository with githooks installed (in $PWD/tmp):
$ source make_repo.sh
- Author: Anna Tikhonova anna.m.tikhonova@gmail.com
- Author: Konstantin Nazarov mail@racktear.com
- Copyright 2016, Parallels IP Holdings GmbH
Licensed under the MIT License.