Growling bzr commits over the networkGrowling bzr commits over the network

All distributed version control systems have their strengths. One of Bazaar’s is that it has an installer for Windows including a Tortoise. It also has an Eclipse plug-in (but don’t forget this other thing that it requires). And it supports ‘checkouts’ that are automatically pushed when you commit. Which isn’t to say that no other DVCSes have those things, just that this one does. Neither the Explorer nor Eclipse front-ends support many of the features of the command line client, but they do enough for people that aren’t often branching and merging—which only defeats the purpose of DVCS for those particular users. And these non-branchers will still reap the indirect benefits of working on a gracefully branched codebase that is more like Crouching Tiger, Hidden Dragon than Drunken Master.

Check it out

There is a counter-intuitive advantage of checkouts in a distributed system: the ability to bind to shared repositories on internal networks, reducing the time penalty of pushing every commit (and other remote operations) to practically nothing. People not only get to keep the svn-style push-commits they generally want to keep, but they get to execute them faster than is possible with a monolithic svn server out on the internet. If an office’s local repository diverges from an externally hosted one (as is certain to occur if you have more than one office, or anyone works from home) any committer can resolve it with a simple merge. This is the sweet spot of version control for groups that work on code in the same place.

Another advantage of serving a repsitory internally is you may choose to do it with plain bzr serve --allow-writes sans ssh, so that everyone doesn’t have to have shell accounts with public key authentication on the server (the Eclipse plug-in can’t ask for a password). Obviously, in this scenario you’d be trusting your firewall not to expose your source code and allow the evil Carol to log commits as Alice from outside the network.

A local shared repository is already a pretty sweet set-up, but don’t you want to take your codegroup synergy to another level? Of course you do. The only thing better than having everyone’s commits instantly available to the group is having everyone instantly know they are available, in translucent notification panels. It will be just like Minority Report! Oh no wait that is the iPhone, which went back in time and invented multitouch. Anyway, broadcasting commits increases codebase awareness and decreases stepping on code toes.

Typical checkin

Snakes on a branch

Bazaar is in Python, which is nice when it’s time to hack a plug-in and you don’t want to know anything about version control programming (at least not today). Just create a new folder for your plug-in, like ~/.bazaar/plugins/growler .

This directory should be in source control, so run bzr init up in there, then add whatever Python scripts you want to use to the directory, like this one for sending growl messages. Your main source file will be called __init__.py (Python: not for aesthetes) and it goes something like this:

"""Bridge from bzr to Growl"""

import sys, os, time
from netgrowl import GrowlRegistrationPacket, GrowlNotificationPacket
from bzrlib import branch
from socket import socket
from ConfigParser import ConfigParser

def post_change_branch_tip(params):
  # revid is null when creating a branch, don't need to broadcast
  if 'null:' == params.new_revid: return

  # collect everything we need from bzrlib
  revision = params.branch.repository.get_revision(params.new_revid)
  nick = params.branch.get_config().get_nickname()
  summary = revision.get_summary()
  author = revision.get_apparent_author()
  
  cfg = ConfigParser()
  # need to load this each time in case it has changed
  cfg.read('/etc/growler.conf')
  
  for (name, ip) in cfg.items('listeners'):
    addr = (ip, netgrowl.GROWL_UDP_PORT)
    s = socket(netgrowl.AF_INET, netgrowl.SOCK_DGRAM)
    register = GrowlRegistrationPacket()
    register.addNotification()
    s.sendto(register.payload(), addr)
    
    notify = GrowlNotificationPacket(
      title="%s checkin" % nick,
      description="%s\n\n%s" % (author, summary)
    )
    s.sendto(notify.payload(), addr)
  

branch.Branch.hooks.install_named_hook('post_change_branch_tip', 
  post_change_branch_tip, 'growler')

Talking to your clients

A prior version of this script used Avahi’s Python bindings to find Growl clients using fancy Zeroconf. Thing is, those are meant to be used asynchronously, but Bazaar plug-ins are executed synchronous to the user operation. It gets weird if you fork() to handle the callbacks and exit twice. Also, it turns out that the Growl clones on other platforms don’t announce to Zeroconf anyway (they are working towards a common standard blah blah blah) so basically forget about doing it the hard but cool way for now.

Instead, this script uses ConfigParser to read a list of addresses from /etc/growler.conf, e.g.

[listeners]
alice: 192.168.1.3
bob:   192.168.1.4

The netgrowl notification is compatible Growl for Windows and Mumbles on Gnome, so yes we have our platforms covered. In whichever client, turn on remote notification and registration (with no password unless you add one to the script) and your commit messages should start popping right up. Once you have confirmed that everything is working with a local install of the plug-in, do a checkout of it in your office bzr server’s bzrlib/plugins, configure /etc/growler.conf, and you’re set.

Dang. The best name ever for this plug-in would have been Rowr, but now it would be a pain to change it. If anyone wants to make this script into a real project though, you have to call it Rowr; consider that to be the code license. Or maybe Grr.

No—definitely Rowr.

Add a comment