# A class to manage the remote connection to the buildbot server
-class BuildbotClient(pb.Referenceable):
+class BuildbotClient:
def __init__(self):
self.builders = {}
self.remote = None
- def callRemote(self, *args, **kwargs):
- return self.remote.callRemote(*args, **kwargs)
-
def connect(self, host, port, username, password):
cf = pb.PBClientFactory()
d = cf.login(creds)
reactor.connectTCP(host, port, cf)
- d.addCallback(self.connected)
+ d.addCallbacks(self.connected, lambda arg: log.err(arg))
log.msg('connecting to %s:%d as %s' % (host, port, username))
ref = None
+ def subscribe(self, mode, interval, target):
+ return self.remote.callRemote('subscribe', mode, interval, target)
+
# Application class
-class App:
+class App(pb.Referenceable):
+
+ # Model/View column identities
- COL_BUILDER = 0 # PYOBJECT: RemoteBuilder objecr
- COL_BUILDER_STATE = 1 # PYOBJECT: result of builder.getState()
- COL_BUILDER_NAME = 2 # str: Name of builder
- COL_BUILDER_LAST_STATUS = 3 # str: Status of last build
- COL_BUILDER_STATUS = 4 # str: Current status
+ COL_BUILDER = 0 # PYOBJECT: RemoteBuilder objecr
+ COL_BUILDER_NAME = 1 # str: Name of builder
+ COL_BUILDER_STATE = 2 # str: State of builder
+
+ # Subscription modes. Each mode includes events of previous mode.
+
+ MODE_BUILDERS = 'builders' # builderAdded, builderRemoved
+ MODE_BUILDS = 'builds' # builderChangedState, buildStarted, buildFinished
+ MODE_STEPS = 'steps' # buildETAUpdate, stepStarted, stepFinished
+ MODE_LOGS = 'logs' # stepETAUpdate, logStarted, logFinished
+ MODE_FULL = 'full' # logChunk
# Results constants
Results = ["success", "warnings", "failure", "skipped", "exception"]
- def __init__(self, host, port, username, password):
+ def __init__(self, host, port, username, password, updateInterval = 5):
# Initialise buildbot client
self.win = self.xml.get_widget('toplevel')
self.win.connect('destroy', gtk.main_quit)
- self.model = gtk.ListStore(
- gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT, str, str, str)
+ self.model = gtk.ListStore(gobject.TYPE_PYOBJECT, str, str)
self.model.set_sort_column_id(self.COL_BUILDER_NAME, gtk.SORT_ASCENDING)
SortableTreeViewColumn('Name', self.COL_BUILDER_NAME))
view.append_column(
- SortableTreeViewColumn('Last Build', self.COL_BUILDER_LAST_STATUS))
-
- view.append_column(
- SortableTreeViewColumn('Current Status', self.COL_BUILDER_STATUS))
+ SortableTreeViewColumn('State', self.COL_BUILDER_STATE))
view.show()
self.win.show()
- # Initialise model with list of builders
+ # Subscribe to build events to update main window
d = self.client.connect(host, port, username, password)
- d.addCallback(self._initModel)
- def _initModel(self, *args):
-
- # Call getBuilder() on each item returned by getBuilderNames()
- # and add (builder, name) tuple to the model.
+ def subscribe(arg):
+
+ d = self.client.subscribe(self.MODE_BUILDS, 5, self)
+
+ d.addCallbacks(lambda arg: sys.stdout.write("ok\n"),
+ lambda *args: sys.stdout.write("error: %s\n" % args))
+
+ d.addCallback(subscribe)
+
+ # Callbacks for subscription mode >= MODE_BUILDERS
+
+ def remote_builderAdded(self, buildername, builder):
+ """Called by the PB server when a builder has been added to the
+ buildbot. The buildername parameter is the name of the build
+ as a string, and builder is a RemoteBuilder object."""
+
+ log.msg('added builder "%s"' % buildername)
+
+ # Add builder to model
+
+ iter = self.model.append()
+
+ self.model.set_value(iter, self.COL_BUILDER, builder)
+ self.model.set_value(iter, self.COL_BUILDER_NAME, buildername)
+
+ def remote_builderRemoved(self, buildername):
+ """Called by the PB server when a builder has been removed from the
+ buildbot."""
+
+ log.msg('removed builder "%s"' % buildername)
+
+ # Remove builder from model
+
+ def delBuilder(model, path, iter, user_data):
+ if model.get_value(iter, self.COL_BUILDER_NAME) == buildername:
+ self.model.remove(iter)
- def addBuilderAndName(builder, name):
+ self.model.foreach(delBuilder, None)
- # Add builder to model
+ # Subscription mode >= builds
- iter = self.model.append()
+ def remote_buildStarted(self, buildername, build):
+ """Called by the PB server when a builder has started a build. The
+ buildername parameter is the name of the build as a string,
+ and build is a RemoteBuild object."""
- self.model.set_value(iter, self.COL_BUILDER, builder)
- self.model.set_value(iter, self.COL_BUILDER_NAME, name)
-
- # Fetch current state of builder
+ log.msg('remote_buildStarted')
- def setStateAndStatus(state):
- self.model.set_value(iter, self.COL_BUILDER_STATE, state)
- self.model.set_value(iter, self.COL_BUILDER_STATUS, state[0])
+ def remote_builderChangedState(self, buildername, statename, eta):
+ """Called by the PB server when a builder has changed state. The
+ buildername parameter is the name of the build, state is a
+ description of the state of the build, and eta is the
+ estimated time in seconds until the completion of the build."""
- builder.callRemote('getState').addCallback(setStateAndStatus)
+ log.msg('builder "%s" changed state to "%s"' % (buildername, statename))
- # Fetch last build status
+ # Reflect state change in model
- def setLastBuildStatus(result):
+ def updateState(model, path, iter, user_data):
+ if model.get_value(iter, self.COL_BUILDER_NAME) == buildername:
+ model.set_value(iter, self.COL_BUILDER_STATE, statename)
- self.model.set_value(iter, self.COL_BUILDER_LAST_STATUS,
- self.Results[result])
+ self.model.foreach(updateState, None)
- builder.callRemote('getLastFinishedBuild').addCallback(
- lambda build:
- build and build.callRemote('getResults')
- .addCallback(setLastBuildStatus))
+ def remote_buildFinished(self, buildername, build, result):
+ """Called by the PB server when a build has finished. Buildername is
+ the name of the build, build a RemoteBuild object, and result an
+ integer exit code being the result of the build."""
- self.client.callRemote('getBuilderNames').addCallback(
- lambda names:
- [self.client.callRemote('getBuilder', name).addCallback
- (addBuilderAndName, name) for name in names])
+ log.msg('remote_buildFinished')
# Main function