in bazaar you write hooks as plugins.
how it works
the “hook” is simple python function that will be executed before really committing when bzr commit is called . it is very simple, it just run an executable and check its result. if it is successful the commit operation is executed. if it fails the commit operation is cancelled.
plugins in bazaar are not project specific. so you cant control in which projects (branches) your plugin will be applied (it will be applied to all). so, first the plugin checks if the branch contains a “precommit” executable. it must be named “precommit” and must be in root path of your branch.
the plugin file should be placed in ~/.bazaar/plugins/. the name of the module doesn’t matter (i called it “check_pre_commit.py”). putting the boilerplate code together i got this:
"""this is a plugin/hook for bazaar. just add this file to ~/.bazaar/plugins/""" from bzrlib import branch def pre_commit_hook(local, master, old_revno, old_revid, future_revno, future_revid, tree_delta, future_tree): """This hook will execute precommit script from root path of the bazaar branch. Commit will be canceled if precommit fails.""" import os,subprocess from bzrlib import errors # this hook only makes sense if a precommit file exist. if not os.path.exists("precommit"): return try: subprocess.check_call(os.path.abspath("precommit")) # if precommit fails (process return not zero) cancel commit. except subprocess.CalledProcessError: raise errors.BzrError("pre commit check failed.") branch.Branch.hooks.install_hook('pre_commit', pre_commit_hook) branch.Branch.hooks.name_hook(pre_commit_hook, 'Check pre_commit hook')
thats all for the hook.
now we just need to create an executable that will run the tests and return something different from zero if it fails. and put it on the root path of the branch.
in this example i execute my tests using nose.
precommit
#!/bin/sh nosetests
ok. everything is working. but who remember to run the tests should be “punished” and will have to wait for the tests to run twice (once manually before committing and once automatically as a pre-commit hook)? no. if you remember to run the tests you can also remember to skip the plugin :)
bzr commit --no-plugins
Update: You can put your tests in a smart build-system (like doit) to keep track of the modifications in your source code and re-execute only the required tests (the ones that depends on the modified files).
caveats
-
the precommit executable is part of the branch but the hook is not. so just doing a branch on the project is not enough. every user has to install the plugin. but just once for all projects.
-
the hook is executed after you supply the commit message. it is annoying to write a message and than loose it because the commit failed. or maybe it is a good punishment for trying to commit an untested code :)
-
the output gets a bit messed up. but not a big deal…
eduardo@eduardo-laptop:~/work/doit$ bzr commit Committing to: /home/eduardo/work/doit/ modified precommit [========== ] Running pre_commit hooks [Check pre_commit hook] - Stage 3/5.... ---------------------------------------------------------------------- Ran 4 tests in 0.268s OK Committed revision 2.
thats it.
The code is out of date, here it is updates for Bazaar 2.2.1, getting the base path of the branch:
from bzrlib import branch, urlutils
def pre_commit_script(local, master, old_revno, old_revid, future_revno, future_revid, tree_delta, future_tree):
“””This hook will execute precommit script from root path of the bazaar
branch. Commit will be canceled if precommit fails.”””
import os, subprocess
from bzrlib import errors
base = urlutils.local_path_from_url((local if local else master).base)
script = os.path.join(base, “precommit”)
# this hook only makes sense if a precommit file exist.
if not os.path.exists(script):
return
try:
subprocess.check_call(script)
# if precommit fails (process return not zero) cancel commit.
except subprocess.CalledProcessError:
raise errors.BzrError(“pre commit check failed.”)
branch.Branch.hooks.install_named_hook(
‘pre_commit’, pre_commit_script,
‘pre_commit_script (runs “precommit” script in branch root )’)
Hello Eduardo,
I was looking for some example to write a post_push hook to automate documentation build in readthedocs.org, but I certainly ended up writing one for running unit test cases before every commit also. Thanks!
Regards,
Javier