[Example] "pre-commit" server-side hook translated from SVN

Andrew Bennetts andrew at canonical.com
Wed Jul 16 02:33:34 BST 2008


GNOME's SVN repository has a pre-commit hook that is run for commits to most
projects.  I think many typical large projects want to do checks similar to

  * the commit message should not be empty
  * there should be a valid MAINTAINERS file
  * any *.po files that were changed should be syntactically valid
  * no *.po file can have the executable bit set

In bzr terms, the most direct translation of an SVN pre-commit hook (on a
centralised repository) is a pre_change_branch_tip hook on a branch (on a
shared, central server).  One small difference is that unlike SVN, we want to
check the differences between the old tip and the new tip, rather than just the
changes in the new tip vs. its parent revision.

I've attached a fairly direct translation of GNOME SVN's “common-pre-commit”
hook as an example of how the same hook would look in bzr.  It assumes my
TipChangeRejected patch has been merged.  It is a bzr plugin (of course), and is
enabled on a per-location basis by setting “gnome_hooks = on” in
~/.bazaar/locations.conf.  I hope this example may be of use to someone.

So, this shows it is possible to write a pre_change_branch_tip that does the
sorts of things people do in SVN pre-commit.  It's mostly straightforward, but
we could use some more convenience functions to make these sorts of tasks
easier.  There's also no documentation to help would-be hook writers know how to
go about this.  I'm working today on writing some docs based on what I've
learned by writing this example.

Aside from documetation, things that could be easier:

  * getting a list of all files that have been changed (i.e. modified, renamed,
    added, execute bit toggled, etc) vs. the old revision
  * how to get the contents of the version of a file in the new revision isn't
    particularly obvious, or easy.
  * getting at this sort of information from a shell script

To elaborate on that previous point, it's fairly easy to define a hook function

    def run_shell_script(params):
        # Run a shell script.
        config = LocationConfig(params.branch.base)
        shell_script = config.get_user_option('gnome_hook_script')
        if shell_script is None:
            # No gnome_hook_script defined for this location
        env = dict(os.environ)
        env['BRANCH'] = local_path_from_url(params.branch.base)
        env['OLD_REVID'] = params.old_revid
        env['REVID'] = params.new_revid
        proc = Popen([shell_script], stdout=PIPE, stderr=STDOUT, env=env)
        stdout, stderr = proc.communicate()
        retcode = proc.returncode
        if retcode != 0:
            raise errors.TipChangeRejected(stdout)

        'pre_change_branch_tip', run_shell_script, 'my test hook')

But there's very little that you can usefully do from such a script, at least
not out of the things the GNOME pre-commit hook wants to do.  You can't easily
get the list of changed files, or even something simple like the commit message,
with simple bzr invocations.  You could check the MAINTAINERS file with 
“bzr cat -rrevid:$REVID $BRANCH/MAINTAINERS” I guess, but that's about it.
Obviously Python and bzrlib is much more capable than shell and /usr/bin/bzr,
but it would be nice if we could make simple shell scripts possible.  This would
be especially helpful for people converting hook scripts for other VCS tools.


-------------- next part --------------
A non-text attachment was scrubbed...
Name: gnome_hooks.py
Type: text/x-python
Size: 4687 bytes
Desc: not available
Url : https://lists.ubuntu.com/archives/bazaar/attachments/20080716/8ed9b00c/attachment-0001.py 

More information about the bazaar mailing list