Those of you who use the excellent Buildbot Continuous Integration system probably spend a fair amount of time looking at it’s Waterfall display. Which shows the status of your builders, and probably looks something like this.
One of the problems with an OOTB Buildbot install however, is that while the Waterfall is pretty awesome (when it’s green), the Home Page tends to be a little… let’s say dry. Which is a shame for what should be valuable URL real-estate.
All of the Web pages served by Buildbot are customizable however, although (at the time of writing) there seems to be little documentation on how to customize the home page in an interesting way – say by passing through extra context variables that can be used in the template.
Redressing that is the purpose of this post!
Basic Customisation
Buildbot will look for a directory called “templates” adjacent to your master.cfg for any custom templates you want to define. These are then rendered using the Jinja2 templating engine.
The template for the homepage is called root.html. You may like to use the current Buildbot default root.html template as a basis to work from – you can find the source here.
If you simply add
<div> Hello Beautiful World </div>
before the final closing endblock tag, well you can probably guess what shows up towards the bottom of your Buildbot’s home page…
Adding Variables
In order to pass extra variables through to the template, we’ll have to override a couple classes.
The frist thing we’ll want to do is provide a class that will render the homepage. This is normally buildbot.status.web.root.RootPage. We want an instance variable that will hold our extra variables, so we subclass and override __init__
from buildbot.status.web import root class ContextWebRoot(root.RootPage): def __init__(self, context, *args, **kwargs): self.context = context root.RootPage.__init__(self, *args, **kwargs)
The rendering actually gets done by the content() method of our page class, which has a signature that helpfully includes the request and the template context as a dictionary. This means that we can simply update the dictionary and continue as we otherwise would…
def content(self, request, cxt): """ Let's update the context with our extra vars """ cxt.update(self.context) return root.RootPage.content(self, request, cxt)
Which is all well and good, but now we need to have this class be the one that gets called when we visit the “/” url.
Now, in order to do this, we’ll want to subclass the buildbot.status.web.baseweb.WebStatus class that provides er… the Web status functionality. You can do that by adding the following to your master.cfg
from buildbot.status.web import baseweb class ContextWebStatus(baseweb.WebStatus): def __init__(self, context={}, *args, **kwargs): self.context = context baseweb.WebStatus.__init__(self, *args, **kwargs) def setupSite(self): """ Use the existing setup machinery, then replace the page that the "/" url points at. """ baseweb.WebStatus.setupSite(self) our_root = ContextWebRoot(projects=self.projects) self.site.resource.delEntity("") self.site.resource.putChild("", our_root)
Now all that remains is to set the custom WebStatus class in the master.cfg.
tpl_context= { 'foo': 'bar', 'goo': 'car', 'hoo': 'dar' } BuildmasterConfig['status'] = [ContextWebStatus( context=tpl_context, http_port=80, allowForce=True )]
You can now access the context variables from the template with e.g.
{{ foo }} <br/>
{{ goo }} <br/>
{{ hoo }} <br/>Putting it all together
So, the status section of your master.cfg should now look something like this:
from buildbot.status.web import baseweb, root class ContextWebRoot(root.RootPage): def __init__(self, context, *args, **kwargs): self.context = context root.RootPage.__init__(self, *args, **kwargs) def content(self, request, cxt): """ Let's update the context with our extra vars """ cxt.update(self.context) return root.RootPage.content(self, request, cxt) class ContextWebStatus(baseweb.WebStatus): def __init__(self, context={}, *args, **kwargs): self.context = context baseweb.WebStatus.__init__(self, *args, **kwargs) def setupSite(self): """ Use the existing setup machinery, then replace the page that the "/" url points at. """ baseweb.WebStatus.setupSite(self) our_root = ContextWebRoot(projects=self.projects) self.site.resource.delEntity("") self.site.resource.putChild("", our_root) tpl_context= { 'foo': 'bar', 'goo': 'car', 'hoo': 'dar' } BuildmasterConfig['status'] = [ContextWebStatus( context=tpl_context, http_port=80, allowForce=True )]