[Buildbot-devel] Different source types?

Brian Warner warner-buildbot at lothar.com
Fri Dec 12 16:42:42 UTC 2003

> Since we already have bonsai set up to get email notifications for CVS 
> changes, my admin asked if we could hook into that instead of running a 
> separate FreshCVS daemon.  This means I have to write my own kind of 
> mail-based "change source" that connects to the build master.

Correct. The fundamental building block is a ChangeSource object (i.e.
anything that implements the IChangeSource "Interface"/"protocol"). This is a
piece of code that lives inside the buildmaster and does anything it likes to
find out that something has changed in your source tree. When it detects a
change has occurred, it creates a Change object to describe the addition /
deletion / modification, and submits that Change object to the ChangeMaster.
The object is just a data-holder (like a struct) and has no particular
behavior attached to it. I use a separate object for Changes out of
convenience: they are passed around a lot after being given to the
ChangeMaster (for example the summaries retrieved by following the links in
the "Changes" column of the waterfall display are built directly from the
Change object).

There are three current built-in approaches to getting these Change objects
into the buildmaster.

 1: set up a FreshCVS listener (by creating a FreshCVSSource). This will
    connect to a FreshCVS daemon (and attempt to reconnect if the connection
    is ever lost), and subscribe to hear about all changes. This is the most
    direct approach, but requires that you have CVSToys installed on the
    machine that hosts your CVS repository (and is hampered at the moment by
    some Twisted version-skew issues between the latest releases of CVSToys,
    Buildbot, and Twisted itself).

 2: get changes by email. There are two orthogonal issues:
    2a: how do you receive mail?
    2b: how should we parse the each mail message?

    For 2a, there is built-in support for watching qmail-style Maildirs. This
    is a simple protocol that involves a trio of directories. New messages
    are created in a temporary directory (BASE/tmp/) and then, when complete,
    moved atomically into the incoming directory (BASE/new/). When the
    message has been processed (i.e. when the receiving application commits
    to accepting the message), it is moved to the old directory (BASE/cur/, I
    think, maybe BASE/old/).

    You can create a MaildirSource object (actually an FCMaildirSource or a
    SyncmailMaildirSource), give it the base directory as an argument, and
    the buildbot code will do the rest. It will poll (or use linux's DNotify
    scheme) the maildir every once in a while to look for new messages.

    Note that you do not have to run qmail to use maildirs. It is trivial to
    create them by hand (or use the 'maildirmake' utility distributed with
    qmail): 'mkdir BASE BASE/cur BASE/new BASE/tmp'. Len Budney has written a
    small utility called 'safecat' which safely takes a message on stdin and
    drops a file into the maildir (doing all the necessary error-checking
    that is the whole point of using maildirs in the first place). You can
    put a pipe-to-safecat command in your non-qmail .forward file and be done
    with it.

    Safecat is available from:

    For 2b, each mail source has a parsing function which is used to scan the
    message body and generate a set of Change objects. Buildbot is
    distributed with two such functions: parseFreshCVSMail() parses the
    messages created by the MailNotification target built-in to FreshCVS,
    while parseSyncmail() handles the mail format generated by syncmail (a
    commonly-used CVS change-message distribution tool).

    Take the dot-product of the possibilities for 2a and 2b, and you've got a
    set (well, a pair) of change source classes which can be used in your
    buildmaster configuration:

     FCMaildirSource(basedir): receive FreshCVS.MailNotification messages in
                               a maildir located at 'basedir'

     SyncmailMaildirSource(basedir): receive Syncmail messages in a maildir
                                     located at 'basedir'

    If you receive CVS mail messages (or Subversion messages or whatever) in
    a different format than MailNotification or Syncmail, you can write a
    parser function based upon the code in buildbot/changes/mail.py and then
    create a new MaildirSource subclass. If you receive mail messages through
    something other than a maildir, you can write a new class that behaves
    the same way as MaildirSource does (i.e., calls the .messageReceived
    method with a filename from which the message can be read).

 3: push changes into the buildmaster via RPC (using Twisted's native RPC
    system named Perspective Broker, abbreviated "PB")

    Inside the buildmaster, the various change sources all deliver the Change
    objects to a piece of code called the ChangeMaster. The ChangeMaster also
    listens on a TCP port (actually it shares the same TCP port with the
    BotMaster, the debug port, and the remote status client port), and
    external programs can inject Change objects through this port.

    This port is enabled by default. (in buildbot-0.3.5 you could disable it
    when you call makeApp, via the 'services' argument) (in the upcoming
    release, there is an unimplemented 'services' configuration variable that
    would, if it were implemented, let you control whether the port is
    enabled or not. This feature is under development, it will probably
    acquire some password-protection and source-IP-address restrictions
    within a release or two). It listens on the same TCP port as everything

    There is a sample client in contrib/fakechange.py, which I use to
    initiate new builds during local testing. You can adapt this program to,
    for example, collect text from stdin, parse the message to extract a set
    of (username, filenames, change comments), assemble them into a
    dictionary, and fire off the remote method call to inject the change.

    The contrib/svn_buildbot.py script also uses this approach. This script
    is meant to be run from the Subversion repository's post-commit hook, and
    connects to the buildmaster's change port to inject the Change object.

If none of these approaches suffice, you can always write a new kind of
ChangeSource. If you already have a cvs-commits mailing list, the easiest
approach is probably to watch a maildir with SyncmailMaildirSource and use
.forward or .procmail to 'safecat' the messages into the maildir. If you can
install CVSToys on your repository machine, then use FreshCVSSource (the
latency will be much lower.. watching for email involves both the email
propagation delay and the polling interval).

A hint for all those eager Buildbot developers out there :) : a nice addition
to contrib/ would be a short script that parses, say, Syncmail messages from
stdin and sends the resulting Change objects over PB to the change port. All
the code is there already (parseSyncmail and config/fakechange.py), it just
needs to be shuffled into a new tool. This new program could be called
directly by .forward/.procmail and let non-qmail non-CVSToys installations
bypass the maildir/safecat part.

good luck,

More information about the devel mailing list